buildSDK/dependancy_bootstrap.txt
[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, function(s) {
10                     if ( s.href  && 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','d-none']);
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','d-none']);
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 fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         var sizes =   ['xs','sm','md','lg'];
1064         sizes.map(function(size ,ix){
1065             //Roo.log( size + ':' + settings[size]);
1066             
1067             if (settings[size+'off'] !== false) {
1068                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1069             }
1070             
1071             if (settings[size] === false) {
1072                 return;
1073             }
1074             
1075             if (!settings[size]) { // 0 = hidden
1076                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1077                 // bootsrap4
1078                 for (var i = ix; i > -1; i--) {
1079                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1080                 }
1081                 
1082                 
1083                 return;
1084             }
1085             cfg.cls += ' col-' + size + '-' + settings[size] + (
1086                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1087             );
1088             
1089         });
1090         
1091         if (this.hidden) {
1092             cfg.cls += ' hidden';
1093         }
1094         
1095         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1096             cfg.cls +=' alert alert-' + this.alert;
1097         }
1098         
1099         
1100         if (this.html.length) {
1101             cfg.html = this.html;
1102         }
1103         if (this.fa) {
1104             var fasize = '';
1105             if (this.fasize > 1) {
1106                 fasize = ' fa-' + this.fasize + 'x';
1107             }
1108             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1109             
1110             
1111         }
1112         if (this.icon) {
1113             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1114         }
1115         
1116         return cfg;
1117     }
1118    
1119 });
1120
1121  
1122
1123  /*
1124  * - LGPL
1125  *
1126  * page container.
1127  * 
1128  */
1129
1130
1131 /**
1132  * @class Roo.bootstrap.Container
1133  * @extends Roo.bootstrap.Component
1134  * Bootstrap Container class
1135  * @cfg {Boolean} jumbotron is it a jumbotron element
1136  * @cfg {String} html content of element
1137  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1138  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1139  * @cfg {String} header content of header (for panel)
1140  * @cfg {String} footer content of footer (for panel)
1141  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1142  * @cfg {String} tag (header|aside|section) type of HTML tag.
1143  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1144  * @cfg {String} fa font awesome icon
1145  * @cfg {String} icon (info-sign|check|...) glyphicon name
1146  * @cfg {Boolean} hidden (true|false) hide the element
1147  * @cfg {Boolean} expandable (true|false) default false
1148  * @cfg {Boolean} expanded (true|false) default true
1149  * @cfg {String} rheader contet on the right of header
1150  * @cfg {Boolean} clickable (true|false) default false
1151
1152  *     
1153  * @constructor
1154  * Create a new Container
1155  * @param {Object} config The config object
1156  */
1157
1158 Roo.bootstrap.Container = function(config){
1159     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1160     
1161     this.addEvents({
1162         // raw events
1163          /**
1164          * @event expand
1165          * After the panel has been expand
1166          * 
1167          * @param {Roo.bootstrap.Container} this
1168          */
1169         "expand" : true,
1170         /**
1171          * @event collapse
1172          * After the panel has been collapsed
1173          * 
1174          * @param {Roo.bootstrap.Container} this
1175          */
1176         "collapse" : true,
1177         /**
1178          * @event click
1179          * When a element is chick
1180          * @param {Roo.bootstrap.Container} this
1181          * @param {Roo.EventObject} e
1182          */
1183         "click" : true
1184     });
1185 };
1186
1187 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1188     
1189     jumbotron : false,
1190     well: '',
1191     panel : '',
1192     header: '',
1193     footer : '',
1194     sticky: '',
1195     tag : false,
1196     alert : false,
1197     fa: false,
1198     icon : false,
1199     expandable : false,
1200     rheader : '',
1201     expanded : true,
1202     clickable: false,
1203   
1204      
1205     getChildContainer : function() {
1206         
1207         if(!this.el){
1208             return false;
1209         }
1210         
1211         if (this.panel.length) {
1212             return this.el.select('.panel-body',true).first();
1213         }
1214         
1215         return this.el;
1216     },
1217     
1218     
1219     getAutoCreate : function(){
1220         
1221         var cfg = {
1222             tag : this.tag || 'div',
1223             html : '',
1224             cls : ''
1225         };
1226         if (this.jumbotron) {
1227             cfg.cls = 'jumbotron';
1228         }
1229         
1230         
1231         
1232         // - this is applied by the parent..
1233         //if (this.cls) {
1234         //    cfg.cls = this.cls + '';
1235         //}
1236         
1237         if (this.sticky.length) {
1238             
1239             var bd = Roo.get(document.body);
1240             if (!bd.hasClass('bootstrap-sticky')) {
1241                 bd.addClass('bootstrap-sticky');
1242                 Roo.select('html',true).setStyle('height', '100%');
1243             }
1244              
1245             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1246         }
1247         
1248         
1249         if (this.well.length) {
1250             switch (this.well) {
1251                 case 'lg':
1252                 case 'sm':
1253                     cfg.cls +=' well well-' +this.well;
1254                     break;
1255                 default:
1256                     cfg.cls +=' well';
1257                     break;
1258             }
1259         }
1260         
1261         if (this.hidden) {
1262             cfg.cls += ' hidden';
1263         }
1264         
1265         
1266         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1267             cfg.cls +=' alert alert-' + this.alert;
1268         }
1269         
1270         var body = cfg;
1271         
1272         if (this.panel.length) {
1273             cfg.cls += ' panel panel-' + this.panel;
1274             cfg.cn = [];
1275             if (this.header.length) {
1276                 
1277                 var h = [];
1278                 
1279                 if(this.expandable){
1280                     
1281                     cfg.cls = cfg.cls + ' expandable';
1282                     
1283                     h.push({
1284                         tag: 'i',
1285                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1286                     });
1287                     
1288                 }
1289                 
1290                 h.push(
1291                     {
1292                         tag: 'span',
1293                         cls : 'panel-title',
1294                         html : (this.expandable ? '&nbsp;' : '') + this.header
1295                     },
1296                     {
1297                         tag: 'span',
1298                         cls: 'panel-header-right',
1299                         html: this.rheader
1300                     }
1301                 );
1302                 
1303                 cfg.cn.push({
1304                     cls : 'panel-heading',
1305                     style : this.expandable ? 'cursor: pointer' : '',
1306                     cn : h
1307                 });
1308                 
1309             }
1310             
1311             body = false;
1312             cfg.cn.push({
1313                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1314                 html : this.html
1315             });
1316             
1317             
1318             if (this.footer.length) {
1319                 cfg.cn.push({
1320                     cls : 'panel-footer',
1321                     html : this.footer
1322                     
1323                 });
1324             }
1325             
1326         }
1327         
1328         if (body) {
1329             body.html = this.html || cfg.html;
1330             // prefix with the icons..
1331             if (this.fa) {
1332                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1333             }
1334             if (this.icon) {
1335                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1336             }
1337             
1338             
1339         }
1340         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1341             cfg.cls =  'container';
1342         }
1343         
1344         return cfg;
1345     },
1346     
1347     initEvents: function() 
1348     {
1349         if(this.expandable){
1350             var headerEl = this.headerEl();
1351         
1352             if(headerEl){
1353                 headerEl.on('click', this.onToggleClick, this);
1354             }
1355         }
1356         
1357         if(this.clickable){
1358             this.el.on('click', this.onClick, this);
1359         }
1360         
1361     },
1362     
1363     onToggleClick : function()
1364     {
1365         var headerEl = this.headerEl();
1366         
1367         if(!headerEl){
1368             return;
1369         }
1370         
1371         if(this.expanded){
1372             this.collapse();
1373             return;
1374         }
1375         
1376         this.expand();
1377     },
1378     
1379     expand : function()
1380     {
1381         if(this.fireEvent('expand', this)) {
1382             
1383             this.expanded = true;
1384             
1385             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1386             
1387             this.el.select('.panel-body',true).first().removeClass('hide');
1388             
1389             var toggleEl = this.toggleEl();
1390
1391             if(!toggleEl){
1392                 return;
1393             }
1394
1395             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1396         }
1397         
1398     },
1399     
1400     collapse : function()
1401     {
1402         if(this.fireEvent('collapse', this)) {
1403             
1404             this.expanded = false;
1405             
1406             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1407             this.el.select('.panel-body',true).first().addClass('hide');
1408         
1409             var toggleEl = this.toggleEl();
1410
1411             if(!toggleEl){
1412                 return;
1413             }
1414
1415             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1416         }
1417     },
1418     
1419     toggleEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading .fa',true).first();
1426     },
1427     
1428     headerEl : function()
1429     {
1430         if(!this.el || !this.panel.length || !this.header.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-heading',true).first()
1435     },
1436     
1437     bodyEl : function()
1438     {
1439         if(!this.el || !this.panel.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-body',true).first()
1444     },
1445     
1446     titleEl : function()
1447     {
1448         if(!this.el || !this.panel.length || !this.header.length){
1449             return;
1450         }
1451         
1452         return this.el.select('.panel-title',true).first();
1453     },
1454     
1455     setTitle : function(v)
1456     {
1457         var titleEl = this.titleEl();
1458         
1459         if(!titleEl){
1460             return;
1461         }
1462         
1463         titleEl.dom.innerHTML = v;
1464     },
1465     
1466     getTitle : function()
1467     {
1468         
1469         var titleEl = this.titleEl();
1470         
1471         if(!titleEl){
1472             return '';
1473         }
1474         
1475         return titleEl.dom.innerHTML;
1476     },
1477     
1478     setRightTitle : function(v)
1479     {
1480         var t = this.el.select('.panel-header-right',true).first();
1481         
1482         if(!t){
1483             return;
1484         }
1485         
1486         t.dom.innerHTML = v;
1487     },
1488     
1489     onClick : function(e)
1490     {
1491         e.preventDefault();
1492         
1493         this.fireEvent('click', this, e);
1494     }
1495 });
1496
1497  /*
1498  *  - LGPL
1499  *
1500  *  This is BS4's Card element.. - similar to our containers probably..
1501  * 
1502  */
1503 /**
1504  * @class Roo.bootstrap.Card
1505  * @extends Roo.bootstrap.Component
1506  * Bootstrap Card class
1507  *
1508  *
1509  * possible... may not be implemented..
1510  * @cfg {String} header_image  src url of image.
1511  * @cfg {String|Object} header
1512  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1513  * 
1514  * @cfg {String} title
1515  * @cfg {String} subtitle
1516  * @cfg {String} html -- html contents - or just use children..
1517  * @cfg {String} footer
1518  
1519  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1520  * 
1521  * @cfg {String} margin (0|1|2|3|4|5|auto)
1522  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1523  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1524  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1525  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1526  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1527  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1528  *
1529  * @cfg {String} padding (0|1|2|3|4|5)
1530  * @cfg {String} padding_top (0|1|2|3|4|5)
1531  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1532  * @cfg {String} padding_left (0|1|2|3|4|5)
1533  * @cfg {String} padding_right (0|1|2|3|4|5)
1534  * @cfg {String} padding_x (0|1|2|3|4|5)
1535  * @cfg {String} padding_y (0|1|2|3|4|5)
1536  *
1537  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1538  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1539  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1540  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1541  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1542  
1543  * @config {Boolean} dragable  if this card can be dragged.
1544  * @config {Boolean} drag_group  group for drag
1545  * 
1546  
1547  * @constructor
1548  * Create a new Container
1549  * @param {Object} config The config object
1550  */
1551
1552 Roo.bootstrap.Card = function(config){
1553     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1554     
1555     this.addEvents({
1556         
1557     });
1558 };
1559
1560
1561 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1562     
1563     
1564     weight : '',
1565     
1566     margin: '', /// may be better in component?
1567     margin_top: '', 
1568     margin_bottom: '', 
1569     margin_left: '',
1570     margin_right: '',
1571     margin_x: '',
1572     margin_y: '',
1573     
1574     padding : '',
1575     padding_top: '', 
1576     padding_bottom: '', 
1577     padding_left: '',
1578     padding_right: '',
1579     padding_x: '',
1580     padding_y: '',
1581     
1582     display: '', 
1583     display_xs: '', 
1584     display_sm: '', 
1585     display_lg: '',
1586     display_xl: '',
1587  
1588     header_image  : '',
1589     header : '',
1590     header_size : 0,
1591     title : '',
1592     subtitle : '',
1593     html : '',
1594     footer: '',
1595     
1596     dragable : false,
1597     drag_group : false,
1598     
1599     childContainer : false,
1600
1601     layoutCls : function()
1602     {
1603         var cls = '';
1604         var t = this;
1605         Roo.log(this.margin_bottom.length);
1606         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1607             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1608             
1609             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1610                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
1611             }
1612             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
1613                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
1614             }
1615         });
1616         
1617         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1618             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
1619                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
1620             }
1621         });
1622         
1623         // more generic support?
1624         if (this.hidden) {
1625             cls += ' d-none';
1626         }
1627         
1628         return cls;
1629     },
1630  
1631        // Roo.log("Call onRender: " + this.xtype);
1632         /*  We are looking at something like this.
1633 <div class="card">
1634     <img src="..." class="card-img-top" alt="...">
1635     <div class="card-body">
1636         <h5 class="card-title">Card title</h5>
1637          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1638
1639         >> this bit is really the body...
1640         <div> << we will ad dthis in hopefully it will not break shit.
1641         
1642         ** card text does not actually have any styling...
1643         
1644             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1645         
1646         </div> <<
1647           <a href="#" class="card-link">Card link</a>
1648           
1649     </div>
1650     <div class="card-footer">
1651         <small class="text-muted">Last updated 3 mins ago</small>
1652     </div>
1653 </div>
1654          */
1655     getAutoCreate : function(){
1656         
1657         var cfg = {
1658             tag : 'div',
1659             cls : 'card',
1660             cn : [ ]
1661         };
1662         
1663         if (this.weight.length && this.weight != 'light') {
1664             cfg.cls += ' text-white';
1665         } else {
1666             cfg.cls += ' text-dark'; // need as it's nested..
1667         }
1668         if (this.weight.length) {
1669             cfg.cls += ' bg-' + this.weight;
1670         }
1671         
1672         cfg.cls += this.layoutCls(); 
1673         
1674         if (this.header.length) {
1675             cfg.cn.push({
1676                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1677                 cls : 'card-header',
1678                 html : this.header // escape?
1679             });
1680         } else {
1681             cfg.cn.push({
1682                 tag : 'div',
1683                 cls : 'card-header d-none'
1684             });
1685         }
1686         if (this.header_image.length) {
1687             cfg.cn.push({
1688                 tag : 'img',
1689                 cls : 'card-img-top',
1690                 src: this.header_image // escape?
1691             });
1692         }
1693         
1694         var body = {
1695             tag : 'div',
1696             cls : 'card-body',
1697             cn : []
1698         };
1699         cfg.cn.push(body);
1700         
1701         if (this.title.length) {
1702             body.cn.push({
1703                 tag : 'div',
1704                 cls : 'card-title',
1705                 src: this.title // escape?
1706             });
1707         }
1708         
1709         if (this.subtitle.length) {
1710             body.cn.push({
1711                 tag : 'div',
1712                 cls : 'card-title',
1713                 src: this.subtitle // escape?
1714             });
1715         }
1716         
1717         body.cn.push({
1718             tag : 'div',
1719             cls : 'roo-card-body-ctr'
1720         });
1721         
1722         if (this.html.length) {
1723             body.cn.push({
1724                 tag: 'div',
1725                 html : this.html
1726             });
1727         }
1728         // fixme ? handle objects?
1729         if (this.footer.length) {
1730             cfg.cn.push({
1731                 tag : 'div',
1732                 cls : 'card-footer',
1733                 html: this.footer // escape?
1734             });
1735         }
1736         // footer...
1737         
1738         return cfg;
1739     },
1740     
1741     
1742     getCardHeader : function()
1743     {
1744         var  ret = this.el.select('.card-header',true).first();
1745         if (ret.hasClass('d-none')) {
1746             ret.removeClass('d-none');
1747         }
1748         
1749         return ret;
1750     },
1751     
1752     getChildContainer : function()
1753     {
1754         
1755         if(!this.el){
1756             return false;
1757         }
1758         return this.el.select('.roo-card-body-ctr',true).first();    
1759     },
1760     
1761     initEvents: function() 
1762     {
1763         if(this.dragable){
1764              this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1765                     containerScroll: true,
1766                     ddGroup: this.drag_group || 'default_card_drag_group'
1767             });
1768             this.dragZone.getDragData = this.getDragData.createDelegate(this);
1769         }
1770         
1771         
1772         
1773     },
1774     getDragData : function(e) {
1775         var target = this.getEl();
1776         if (target) {
1777             //this.handleSelection(e);
1778             
1779             var dragData = {
1780                 source: this,
1781                 copy: false,
1782                 nodes: this.getEl(),
1783                 records: []
1784             };
1785             
1786             
1787             dragData.ddel = target.dom ;        // the div element
1788             Roo.log(target.getWidth( ));
1789              dragData.ddel.style.width = target.getWidth() + 'px';
1790             
1791             return dragData;
1792         }
1793         return false;
1794     }
1795     
1796 });
1797
1798 /*
1799  * - LGPL
1800  *
1801  * image
1802  * 
1803  */
1804
1805
1806 /**
1807  * @class Roo.bootstrap.Img
1808  * @extends Roo.bootstrap.Component
1809  * Bootstrap Img class
1810  * @cfg {Boolean} imgResponsive false | true
1811  * @cfg {String} border rounded | circle | thumbnail
1812  * @cfg {String} src image source
1813  * @cfg {String} alt image alternative text
1814  * @cfg {String} href a tag href
1815  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1816  * @cfg {String} xsUrl xs image source
1817  * @cfg {String} smUrl sm image source
1818  * @cfg {String} mdUrl md image source
1819  * @cfg {String} lgUrl lg image source
1820  * 
1821  * @constructor
1822  * Create a new Input
1823  * @param {Object} config The config object
1824  */
1825
1826 Roo.bootstrap.Img = function(config){
1827     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1828     
1829     this.addEvents({
1830         // img events
1831         /**
1832          * @event click
1833          * The img click event for the img.
1834          * @param {Roo.EventObject} e
1835          */
1836         "click" : true
1837     });
1838 };
1839
1840 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1841     
1842     imgResponsive: true,
1843     border: '',
1844     src: 'about:blank',
1845     href: false,
1846     target: false,
1847     xsUrl: '',
1848     smUrl: '',
1849     mdUrl: '',
1850     lgUrl: '',
1851
1852     getAutoCreate : function()
1853     {   
1854         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1855             return this.createSingleImg();
1856         }
1857         
1858         var cfg = {
1859             tag: 'div',
1860             cls: 'roo-image-responsive-group',
1861             cn: []
1862         };
1863         var _this = this;
1864         
1865         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1866             
1867             if(!_this[size + 'Url']){
1868                 return;
1869             }
1870             
1871             var img = {
1872                 tag: 'img',
1873                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1874                 html: _this.html || cfg.html,
1875                 src: _this[size + 'Url']
1876             };
1877             
1878             img.cls += ' roo-image-responsive-' + size;
1879             
1880             var s = ['xs', 'sm', 'md', 'lg'];
1881             
1882             s.splice(s.indexOf(size), 1);
1883             
1884             Roo.each(s, function(ss){
1885                 img.cls += ' hidden-' + ss;
1886             });
1887             
1888             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1889                 cfg.cls += ' img-' + _this.border;
1890             }
1891             
1892             if(_this.alt){
1893                 cfg.alt = _this.alt;
1894             }
1895             
1896             if(_this.href){
1897                 var a = {
1898                     tag: 'a',
1899                     href: _this.href,
1900                     cn: [
1901                         img
1902                     ]
1903                 };
1904
1905                 if(this.target){
1906                     a.target = _this.target;
1907                 }
1908             }
1909             
1910             cfg.cn.push((_this.href) ? a : img);
1911             
1912         });
1913         
1914         return cfg;
1915     },
1916     
1917     createSingleImg : function()
1918     {
1919         var cfg = {
1920             tag: 'img',
1921             cls: (this.imgResponsive) ? 'img-responsive' : '',
1922             html : null,
1923             src : 'about:blank'  // just incase src get's set to undefined?!?
1924         };
1925         
1926         cfg.html = this.html || cfg.html;
1927         
1928         cfg.src = this.src || cfg.src;
1929         
1930         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1931             cfg.cls += ' img-' + this.border;
1932         }
1933         
1934         if(this.alt){
1935             cfg.alt = this.alt;
1936         }
1937         
1938         if(this.href){
1939             var a = {
1940                 tag: 'a',
1941                 href: this.href,
1942                 cn: [
1943                     cfg
1944                 ]
1945             };
1946             
1947             if(this.target){
1948                 a.target = this.target;
1949             }
1950             
1951         }
1952         
1953         return (this.href) ? a : cfg;
1954     },
1955     
1956     initEvents: function() 
1957     {
1958         if(!this.href){
1959             this.el.on('click', this.onClick, this);
1960         }
1961         
1962     },
1963     
1964     onClick : function(e)
1965     {
1966         Roo.log('img onclick');
1967         this.fireEvent('click', this, e);
1968     },
1969     /**
1970      * Sets the url of the image - used to update it
1971      * @param {String} url the url of the image
1972      */
1973     
1974     setSrc : function(url)
1975     {
1976         this.src =  url;
1977         
1978         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1979             this.el.dom.src =  url;
1980             return;
1981         }
1982         
1983         this.el.select('img', true).first().dom.src =  url;
1984     }
1985     
1986     
1987    
1988 });
1989
1990  /*
1991  * - LGPL
1992  *
1993  * image
1994  * 
1995  */
1996
1997
1998 /**
1999  * @class Roo.bootstrap.Link
2000  * @extends Roo.bootstrap.Component
2001  * Bootstrap Link Class
2002  * @cfg {String} alt image alternative text
2003  * @cfg {String} href a tag href
2004  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2005  * @cfg {String} html the content of the link.
2006  * @cfg {String} anchor name for the anchor link
2007  * @cfg {String} fa - favicon
2008
2009  * @cfg {Boolean} preventDefault (true | false) default false
2010
2011  * 
2012  * @constructor
2013  * Create a new Input
2014  * @param {Object} config The config object
2015  */
2016
2017 Roo.bootstrap.Link = function(config){
2018     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2019     
2020     this.addEvents({
2021         // img events
2022         /**
2023          * @event click
2024          * The img click event for the img.
2025          * @param {Roo.EventObject} e
2026          */
2027         "click" : true
2028     });
2029 };
2030
2031 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2032     
2033     href: false,
2034     target: false,
2035     preventDefault: false,
2036     anchor : false,
2037     alt : false,
2038     fa: false,
2039
2040
2041     getAutoCreate : function()
2042     {
2043         var html = this.html || '';
2044         
2045         if (this.fa !== false) {
2046             html = '<i class="fa fa-' + this.fa + '"></i>';
2047         }
2048         var cfg = {
2049             tag: 'a'
2050         };
2051         // anchor's do not require html/href...
2052         if (this.anchor === false) {
2053             cfg.html = html;
2054             cfg.href = this.href || '#';
2055         } else {
2056             cfg.name = this.anchor;
2057             if (this.html !== false || this.fa !== false) {
2058                 cfg.html = html;
2059             }
2060             if (this.href !== false) {
2061                 cfg.href = this.href;
2062             }
2063         }
2064         
2065         if(this.alt !== false){
2066             cfg.alt = this.alt;
2067         }
2068         
2069         
2070         if(this.target !== false) {
2071             cfg.target = this.target;
2072         }
2073         
2074         return cfg;
2075     },
2076     
2077     initEvents: function() {
2078         
2079         if(!this.href || this.preventDefault){
2080             this.el.on('click', this.onClick, this);
2081         }
2082     },
2083     
2084     onClick : function(e)
2085     {
2086         if(this.preventDefault){
2087             e.preventDefault();
2088         }
2089         //Roo.log('img onclick');
2090         this.fireEvent('click', this, e);
2091     }
2092    
2093 });
2094
2095  /*
2096  * - LGPL
2097  *
2098  * header
2099  * 
2100  */
2101
2102 /**
2103  * @class Roo.bootstrap.Header
2104  * @extends Roo.bootstrap.Component
2105  * Bootstrap Header class
2106  * @cfg {String} html content of header
2107  * @cfg {Number} level (1|2|3|4|5|6) default 1
2108  * 
2109  * @constructor
2110  * Create a new Header
2111  * @param {Object} config The config object
2112  */
2113
2114
2115 Roo.bootstrap.Header  = function(config){
2116     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2120     
2121     //href : false,
2122     html : false,
2123     level : 1,
2124     
2125     
2126     
2127     getAutoCreate : function(){
2128         
2129         
2130         
2131         var cfg = {
2132             tag: 'h' + (1 *this.level),
2133             html: this.html || ''
2134         } ;
2135         
2136         return cfg;
2137     }
2138    
2139 });
2140
2141  
2142
2143  /*
2144  * Based on:
2145  * Ext JS Library 1.1.1
2146  * Copyright(c) 2006-2007, Ext JS, LLC.
2147  *
2148  * Originally Released Under LGPL - original licence link has changed is not relivant.
2149  *
2150  * Fork - LGPL
2151  * <script type="text/javascript">
2152  */
2153  
2154 /**
2155  * @class Roo.bootstrap.MenuMgr
2156  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2157  * @singleton
2158  */
2159 Roo.bootstrap.MenuMgr = function(){
2160    var menus, active, groups = {}, attached = false, lastShow = new Date();
2161
2162    // private - called when first menu is created
2163    function init(){
2164        menus = {};
2165        active = new Roo.util.MixedCollection();
2166        Roo.get(document).addKeyListener(27, function(){
2167            if(active.length > 0){
2168                hideAll();
2169            }
2170        });
2171    }
2172
2173    // private
2174    function hideAll(){
2175        if(active && active.length > 0){
2176            var c = active.clone();
2177            c.each(function(m){
2178                m.hide();
2179            });
2180        }
2181    }
2182
2183    // private
2184    function onHide(m){
2185        active.remove(m);
2186        if(active.length < 1){
2187            Roo.get(document).un("mouseup", onMouseDown);
2188             
2189            attached = false;
2190        }
2191    }
2192
2193    // private
2194    function onShow(m){
2195        var last = active.last();
2196        lastShow = new Date();
2197        active.add(m);
2198        if(!attached){
2199           Roo.get(document).on("mouseup", onMouseDown);
2200            
2201            attached = true;
2202        }
2203        if(m.parentMenu){
2204           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2205           m.parentMenu.activeChild = m;
2206        }else if(last && last.isVisible()){
2207           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2208        }
2209    }
2210
2211    // private
2212    function onBeforeHide(m){
2213        if(m.activeChild){
2214            m.activeChild.hide();
2215        }
2216        if(m.autoHideTimer){
2217            clearTimeout(m.autoHideTimer);
2218            delete m.autoHideTimer;
2219        }
2220    }
2221
2222    // private
2223    function onBeforeShow(m){
2224        var pm = m.parentMenu;
2225        if(!pm && !m.allowOtherMenus){
2226            hideAll();
2227        }else if(pm && pm.activeChild && active != m){
2228            pm.activeChild.hide();
2229        }
2230    }
2231
2232    // private this should really trigger on mouseup..
2233    function onMouseDown(e){
2234         Roo.log("on Mouse Up");
2235         
2236         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2237             Roo.log("MenuManager hideAll");
2238             hideAll();
2239             e.stopEvent();
2240         }
2241         
2242         
2243    }
2244
2245    // private
2246    function onBeforeCheck(mi, state){
2247        if(state){
2248            var g = groups[mi.group];
2249            for(var i = 0, l = g.length; i < l; i++){
2250                if(g[i] != mi){
2251                    g[i].setChecked(false);
2252                }
2253            }
2254        }
2255    }
2256
2257    return {
2258
2259        /**
2260         * Hides all menus that are currently visible
2261         */
2262        hideAll : function(){
2263             hideAll();  
2264        },
2265
2266        // private
2267        register : function(menu){
2268            if(!menus){
2269                init();
2270            }
2271            menus[menu.id] = menu;
2272            menu.on("beforehide", onBeforeHide);
2273            menu.on("hide", onHide);
2274            menu.on("beforeshow", onBeforeShow);
2275            menu.on("show", onShow);
2276            var g = menu.group;
2277            if(g && menu.events["checkchange"]){
2278                if(!groups[g]){
2279                    groups[g] = [];
2280                }
2281                groups[g].push(menu);
2282                menu.on("checkchange", onCheck);
2283            }
2284        },
2285
2286         /**
2287          * Returns a {@link Roo.menu.Menu} object
2288          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2289          * be used to generate and return a new Menu instance.
2290          */
2291        get : function(menu){
2292            if(typeof menu == "string"){ // menu id
2293                return menus[menu];
2294            }else if(menu.events){  // menu instance
2295                return menu;
2296            }
2297            /*else if(typeof menu.length == 'number'){ // array of menu items?
2298                return new Roo.bootstrap.Menu({items:menu});
2299            }else{ // otherwise, must be a config
2300                return new Roo.bootstrap.Menu(menu);
2301            }
2302            */
2303            return false;
2304        },
2305
2306        // private
2307        unregister : function(menu){
2308            delete menus[menu.id];
2309            menu.un("beforehide", onBeforeHide);
2310            menu.un("hide", onHide);
2311            menu.un("beforeshow", onBeforeShow);
2312            menu.un("show", onShow);
2313            var g = menu.group;
2314            if(g && menu.events["checkchange"]){
2315                groups[g].remove(menu);
2316                menu.un("checkchange", onCheck);
2317            }
2318        },
2319
2320        // private
2321        registerCheckable : function(menuItem){
2322            var g = menuItem.group;
2323            if(g){
2324                if(!groups[g]){
2325                    groups[g] = [];
2326                }
2327                groups[g].push(menuItem);
2328                menuItem.on("beforecheckchange", onBeforeCheck);
2329            }
2330        },
2331
2332        // private
2333        unregisterCheckable : function(menuItem){
2334            var g = menuItem.group;
2335            if(g){
2336                groups[g].remove(menuItem);
2337                menuItem.un("beforecheckchange", onBeforeCheck);
2338            }
2339        }
2340    };
2341 }();/*
2342  * - LGPL
2343  *
2344  * menu
2345  * 
2346  */
2347
2348 /**
2349  * @class Roo.bootstrap.Menu
2350  * @extends Roo.bootstrap.Component
2351  * Bootstrap Menu class - container for MenuItems
2352  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2353  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2354  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2355  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2356  * 
2357  * @constructor
2358  * Create a new Menu
2359  * @param {Object} config The config object
2360  */
2361
2362
2363 Roo.bootstrap.Menu = function(config){
2364     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2365     if (this.registerMenu && this.type != 'treeview')  {
2366         Roo.bootstrap.MenuMgr.register(this);
2367     }
2368     
2369     
2370     this.addEvents({
2371         /**
2372          * @event beforeshow
2373          * Fires before this menu is displayed (return false to block)
2374          * @param {Roo.menu.Menu} this
2375          */
2376         beforeshow : true,
2377         /**
2378          * @event beforehide
2379          * Fires before this menu is hidden (return false to block)
2380          * @param {Roo.menu.Menu} this
2381          */
2382         beforehide : true,
2383         /**
2384          * @event show
2385          * Fires after this menu is displayed
2386          * @param {Roo.menu.Menu} this
2387          */
2388         show : true,
2389         /**
2390          * @event hide
2391          * Fires after this menu is hidden
2392          * @param {Roo.menu.Menu} this
2393          */
2394         hide : true,
2395         /**
2396          * @event click
2397          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2398          * @param {Roo.menu.Menu} this
2399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2400          * @param {Roo.EventObject} e
2401          */
2402         click : true,
2403         /**
2404          * @event mouseover
2405          * Fires when the mouse is hovering over this menu
2406          * @param {Roo.menu.Menu} this
2407          * @param {Roo.EventObject} e
2408          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2409          */
2410         mouseover : true,
2411         /**
2412          * @event mouseout
2413          * Fires when the mouse exits this menu
2414          * @param {Roo.menu.Menu} this
2415          * @param {Roo.EventObject} e
2416          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2417          */
2418         mouseout : true,
2419         /**
2420          * @event itemclick
2421          * Fires when a menu item contained in this menu is clicked
2422          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2423          * @param {Roo.EventObject} e
2424          */
2425         itemclick: true
2426     });
2427     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2428 };
2429
2430 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2431     
2432    /// html : false,
2433     //align : '',
2434     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2435     type: false,
2436     /**
2437      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2438      */
2439     registerMenu : true,
2440     
2441     menuItems :false, // stores the menu items..
2442     
2443     hidden:true,
2444         
2445     parentMenu : false,
2446     
2447     stopEvent : true,
2448     
2449     isLink : false,
2450     
2451     getChildContainer : function() {
2452         return this.el;  
2453     },
2454     
2455     getAutoCreate : function(){
2456          
2457         //if (['right'].indexOf(this.align)!==-1) {
2458         //    cfg.cn[1].cls += ' pull-right'
2459         //}
2460         
2461         
2462         var cfg = {
2463             tag : 'ul',
2464             cls : 'dropdown-menu' ,
2465             style : 'z-index:1000'
2466             
2467         };
2468         
2469         if (this.type === 'submenu') {
2470             cfg.cls = 'submenu active';
2471         }
2472         if (this.type === 'treeview') {
2473             cfg.cls = 'treeview-menu';
2474         }
2475         
2476         return cfg;
2477     },
2478     initEvents : function() {
2479         
2480        // Roo.log("ADD event");
2481        // Roo.log(this.triggerEl.dom);
2482         
2483         this.triggerEl.on('click', this.onTriggerClick, this);
2484         
2485         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2486         
2487         
2488         if (this.triggerEl.hasClass('nav-item')) {
2489             // dropdown toggle on the 'a' in BS4?
2490             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2491         } else {
2492             this.triggerEl.addClass('dropdown-toggle');
2493         }
2494         if (Roo.isTouch) {
2495             this.el.on('touchstart'  , this.onTouch, this);
2496         }
2497         this.el.on('click' , this.onClick, this);
2498
2499         this.el.on("mouseover", this.onMouseOver, this);
2500         this.el.on("mouseout", this.onMouseOut, this);
2501         
2502     },
2503     
2504     findTargetItem : function(e)
2505     {
2506         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2507         if(!t){
2508             return false;
2509         }
2510         //Roo.log(t);         Roo.log(t.id);
2511         if(t && t.id){
2512             //Roo.log(this.menuitems);
2513             return this.menuitems.get(t.id);
2514             
2515             //return this.items.get(t.menuItemId);
2516         }
2517         
2518         return false;
2519     },
2520     
2521     onTouch : function(e) 
2522     {
2523         Roo.log("menu.onTouch");
2524         //e.stopEvent(); this make the user popdown broken
2525         this.onClick(e);
2526     },
2527     
2528     onClick : function(e)
2529     {
2530         Roo.log("menu.onClick");
2531         
2532         var t = this.findTargetItem(e);
2533         if(!t || t.isContainer){
2534             return;
2535         }
2536         Roo.log(e);
2537         /*
2538         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2539             if(t == this.activeItem && t.shouldDeactivate(e)){
2540                 this.activeItem.deactivate();
2541                 delete this.activeItem;
2542                 return;
2543             }
2544             if(t.canActivate){
2545                 this.setActiveItem(t, true);
2546             }
2547             return;
2548             
2549             
2550         }
2551         */
2552        
2553         Roo.log('pass click event');
2554         
2555         t.onClick(e);
2556         
2557         this.fireEvent("click", this, t, e);
2558         
2559         var _this = this;
2560         
2561         if(!t.href.length || t.href == '#'){
2562             (function() { _this.hide(); }).defer(100);
2563         }
2564         
2565     },
2566     
2567     onMouseOver : function(e){
2568         var t  = this.findTargetItem(e);
2569         //Roo.log(t);
2570         //if(t){
2571         //    if(t.canActivate && !t.disabled){
2572         //        this.setActiveItem(t, true);
2573         //    }
2574         //}
2575         
2576         this.fireEvent("mouseover", this, e, t);
2577     },
2578     isVisible : function(){
2579         return !this.hidden;
2580     },
2581     onMouseOut : function(e){
2582         var t  = this.findTargetItem(e);
2583         
2584         //if(t ){
2585         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2586         //        this.activeItem.deactivate();
2587         //        delete this.activeItem;
2588         //    }
2589         //}
2590         this.fireEvent("mouseout", this, e, t);
2591     },
2592     
2593     
2594     /**
2595      * Displays this menu relative to another element
2596      * @param {String/HTMLElement/Roo.Element} element The element to align to
2597      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2598      * the element (defaults to this.defaultAlign)
2599      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2600      */
2601     show : function(el, pos, parentMenu)
2602     {
2603         if (false === this.fireEvent("beforeshow", this)) {
2604             Roo.log("show canceled");
2605             return;
2606         }
2607         this.parentMenu = parentMenu;
2608         if(!this.el){
2609             this.render();
2610         }
2611         
2612         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2613     },
2614      /**
2615      * Displays this menu at a specific xy position
2616      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2617      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2618      */
2619     showAt : function(xy, parentMenu, /* private: */_e){
2620         this.parentMenu = parentMenu;
2621         if(!this.el){
2622             this.render();
2623         }
2624         if(_e !== false){
2625             this.fireEvent("beforeshow", this);
2626             //xy = this.el.adjustForConstraints(xy);
2627         }
2628         
2629         //this.el.show();
2630         this.hideMenuItems();
2631         this.hidden = false;
2632         this.triggerEl.addClass('open');
2633         this.el.addClass('show');
2634         
2635         // reassign x when hitting right
2636         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2637             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2638         }
2639         
2640         // reassign y when hitting bottom
2641         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2642             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2643         }
2644         
2645         // but the list may align on trigger left or trigger top... should it be a properity?
2646         
2647         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2648             this.el.setXY(xy);
2649         }
2650         
2651         this.focus();
2652         this.fireEvent("show", this);
2653     },
2654     
2655     focus : function(){
2656         return;
2657         if(!this.hidden){
2658             this.doFocus.defer(50, this);
2659         }
2660     },
2661
2662     doFocus : function(){
2663         if(!this.hidden){
2664             this.focusEl.focus();
2665         }
2666     },
2667
2668     /**
2669      * Hides this menu and optionally all parent menus
2670      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2671      */
2672     hide : function(deep)
2673     {
2674         if (false === this.fireEvent("beforehide", this)) {
2675             Roo.log("hide canceled");
2676             return;
2677         }
2678         this.hideMenuItems();
2679         if(this.el && this.isVisible()){
2680            
2681             if(this.activeItem){
2682                 this.activeItem.deactivate();
2683                 this.activeItem = null;
2684             }
2685             this.triggerEl.removeClass('open');;
2686             this.el.removeClass('show');
2687             this.hidden = true;
2688             this.fireEvent("hide", this);
2689         }
2690         if(deep === true && this.parentMenu){
2691             this.parentMenu.hide(true);
2692         }
2693     },
2694     
2695     onTriggerClick : function(e)
2696     {
2697         Roo.log('trigger click');
2698         
2699         var target = e.getTarget();
2700         
2701         Roo.log(target.nodeName.toLowerCase());
2702         
2703         if(target.nodeName.toLowerCase() === 'i'){
2704             e.preventDefault();
2705         }
2706         
2707     },
2708     
2709     onTriggerPress  : function(e)
2710     {
2711         Roo.log('trigger press');
2712         //Roo.log(e.getTarget());
2713        // Roo.log(this.triggerEl.dom);
2714        
2715         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2716         var pel = Roo.get(e.getTarget());
2717         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2718             Roo.log('is treeview or dropdown?');
2719             return;
2720         }
2721         
2722         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2723             return;
2724         }
2725         
2726         if (this.isVisible()) {
2727             Roo.log('hide');
2728             this.hide();
2729         } else {
2730             Roo.log('show');
2731             this.show(this.triggerEl, '?', false);
2732         }
2733         
2734         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2735             e.stopEvent();
2736         }
2737         
2738     },
2739        
2740     
2741     hideMenuItems : function()
2742     {
2743         Roo.log("hide Menu Items");
2744         if (!this.el) { 
2745             return;
2746         }
2747         
2748         this.el.select('.open',true).each(function(aa) {
2749             
2750             aa.removeClass('open');
2751          
2752         });
2753     },
2754     addxtypeChild : function (tree, cntr) {
2755         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2756           
2757         this.menuitems.add(comp);
2758         return comp;
2759
2760     },
2761     getEl : function()
2762     {
2763         Roo.log(this.el);
2764         return this.el;
2765     },
2766     
2767     clear : function()
2768     {
2769         this.getEl().dom.innerHTML = '';
2770         this.menuitems.clear();
2771     }
2772 });
2773
2774  
2775  /*
2776  * - LGPL
2777  *
2778  * menu item
2779  * 
2780  */
2781
2782
2783 /**
2784  * @class Roo.bootstrap.MenuItem
2785  * @extends Roo.bootstrap.Component
2786  * Bootstrap MenuItem class
2787  * @cfg {String} html the menu label
2788  * @cfg {String} href the link
2789  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2790  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2791  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2792  * @cfg {String} fa favicon to show on left of menu item.
2793  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2794  * 
2795  * 
2796  * @constructor
2797  * Create a new MenuItem
2798  * @param {Object} config The config object
2799  */
2800
2801
2802 Roo.bootstrap.MenuItem = function(config){
2803     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2804     this.addEvents({
2805         // raw events
2806         /**
2807          * @event click
2808          * The raw click event for the entire grid.
2809          * @param {Roo.bootstrap.MenuItem} this
2810          * @param {Roo.EventObject} e
2811          */
2812         "click" : true
2813     });
2814 };
2815
2816 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2817     
2818     href : false,
2819     html : false,
2820     preventDefault: false,
2821     isContainer : false,
2822     active : false,
2823     fa: false,
2824     
2825     getAutoCreate : function(){
2826         
2827         if(this.isContainer){
2828             return {
2829                 tag: 'li',
2830                 cls: 'dropdown-menu-item '
2831             };
2832         }
2833         var ctag = {
2834             tag: 'span',
2835             html: 'Link'
2836         };
2837         
2838         var anc = {
2839             tag : 'a',
2840             cls : 'dropdown-item',
2841             href : '#',
2842             cn : [  ]
2843         };
2844         
2845         if (this.fa !== false) {
2846             anc.cn.push({
2847                 tag : 'i',
2848                 cls : 'fa fa-' + this.fa
2849             });
2850         }
2851         
2852         anc.cn.push(ctag);
2853         
2854         
2855         var cfg= {
2856             tag: 'li',
2857             cls: 'dropdown-menu-item',
2858             cn: [ anc ]
2859         };
2860         if (this.parent().type == 'treeview') {
2861             cfg.cls = 'treeview-menu';
2862         }
2863         if (this.active) {
2864             cfg.cls += ' active';
2865         }
2866         
2867         
2868         
2869         anc.href = this.href || cfg.cn[0].href ;
2870         ctag.html = this.html || cfg.cn[0].html ;
2871         return cfg;
2872     },
2873     
2874     initEvents: function()
2875     {
2876         if (this.parent().type == 'treeview') {
2877             this.el.select('a').on('click', this.onClick, this);
2878         }
2879         
2880         if (this.menu) {
2881             this.menu.parentType = this.xtype;
2882             this.menu.triggerEl = this.el;
2883             this.menu = this.addxtype(Roo.apply({}, this.menu));
2884         }
2885         
2886     },
2887     onClick : function(e)
2888     {
2889         Roo.log('item on click ');
2890         
2891         if(this.preventDefault){
2892             e.preventDefault();
2893         }
2894         //this.parent().hideMenuItems();
2895         
2896         this.fireEvent('click', this, e);
2897     },
2898     getEl : function()
2899     {
2900         return this.el;
2901     } 
2902 });
2903
2904  
2905
2906  /*
2907  * - LGPL
2908  *
2909  * menu separator
2910  * 
2911  */
2912
2913
2914 /**
2915  * @class Roo.bootstrap.MenuSeparator
2916  * @extends Roo.bootstrap.Component
2917  * Bootstrap MenuSeparator class
2918  * 
2919  * @constructor
2920  * Create a new MenuItem
2921  * @param {Object} config The config object
2922  */
2923
2924
2925 Roo.bootstrap.MenuSeparator = function(config){
2926     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2927 };
2928
2929 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2930     
2931     getAutoCreate : function(){
2932         var cfg = {
2933             cls: 'divider',
2934             tag : 'li'
2935         };
2936         
2937         return cfg;
2938     }
2939    
2940 });
2941
2942  
2943
2944  
2945 /*
2946 * Licence: LGPL
2947 */
2948
2949 /**
2950  * @class Roo.bootstrap.Modal
2951  * @extends Roo.bootstrap.Component
2952  * Bootstrap Modal class
2953  * @cfg {String} title Title of dialog
2954  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2955  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2956  * @cfg {Boolean} specificTitle default false
2957  * @cfg {Array} buttons Array of buttons or standard button set..
2958  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2959  * @cfg {Boolean} animate default true
2960  * @cfg {Boolean} allow_close default true
2961  * @cfg {Boolean} fitwindow default false
2962  * @cfg {Number} width fixed width - usefull for chrome extension only really.
2963  * @cfg {Number} height fixed height - usefull for chrome extension only really.
2964  * @cfg {String} size (sm|lg) default empty
2965  * @cfg {Number} max_width set the max width of modal
2966  *
2967  *
2968  * @constructor
2969  * Create a new Modal Dialog
2970  * @param {Object} config The config object
2971  */
2972
2973 Roo.bootstrap.Modal = function(config){
2974     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2975     this.addEvents({
2976         // raw events
2977         /**
2978          * @event btnclick
2979          * The raw btnclick event for the button
2980          * @param {Roo.EventObject} e
2981          */
2982         "btnclick" : true,
2983         /**
2984          * @event resize
2985          * Fire when dialog resize
2986          * @param {Roo.bootstrap.Modal} this
2987          * @param {Roo.EventObject} e
2988          */
2989         "resize" : true
2990     });
2991     this.buttons = this.buttons || [];
2992
2993     if (this.tmpl) {
2994         this.tmpl = Roo.factory(this.tmpl);
2995     }
2996
2997 };
2998
2999 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3000
3001     title : 'test dialog',
3002
3003     buttons : false,
3004
3005     // set on load...
3006
3007     html: false,
3008
3009     tmp: false,
3010
3011     specificTitle: false,
3012
3013     buttonPosition: 'right',
3014
3015     allow_close : true,
3016
3017     animate : true,
3018
3019     fitwindow: false,
3020     
3021      // private
3022     dialogEl: false,
3023     bodyEl:  false,
3024     footerEl:  false,
3025     titleEl:  false,
3026     closeEl:  false,
3027
3028     size: '',
3029     
3030     max_width: 0,
3031     
3032     max_height: 0,
3033     
3034     fit_content: false,
3035
3036     onRender : function(ct, position)
3037     {
3038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3039
3040         if(!this.el){
3041             var cfg = Roo.apply({},  this.getAutoCreate());
3042             cfg.id = Roo.id();
3043             //if(!cfg.name){
3044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3045             //}
3046             //if (!cfg.name.length) {
3047             //    delete cfg.name;
3048            // }
3049             if (this.cls) {
3050                 cfg.cls += ' ' + this.cls;
3051             }
3052             if (this.style) {
3053                 cfg.style = this.style;
3054             }
3055             this.el = Roo.get(document.body).createChild(cfg, position);
3056         }
3057         //var type = this.el.dom.type;
3058
3059
3060         if(this.tabIndex !== undefined){
3061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3062         }
3063
3064         this.dialogEl = this.el.select('.modal-dialog',true).first();
3065         this.bodyEl = this.el.select('.modal-body',true).first();
3066         this.closeEl = this.el.select('.modal-header .close', true).first();
3067         this.headerEl = this.el.select('.modal-header',true).first();
3068         this.titleEl = this.el.select('.modal-title',true).first();
3069         this.footerEl = this.el.select('.modal-footer',true).first();
3070
3071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3072         
3073         //this.el.addClass("x-dlg-modal");
3074
3075         if (this.buttons.length) {
3076             Roo.each(this.buttons, function(bb) {
3077                 var b = Roo.apply({}, bb);
3078                 b.xns = b.xns || Roo.bootstrap;
3079                 b.xtype = b.xtype || 'Button';
3080                 if (typeof(b.listeners) == 'undefined') {
3081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3082                 }
3083
3084                 var btn = Roo.factory(b);
3085
3086                 btn.render(this.getButtonContainer());
3087
3088             },this);
3089         }
3090         // render the children.
3091         var nitems = [];
3092
3093         if(typeof(this.items) != 'undefined'){
3094             var items = this.items;
3095             delete this.items;
3096
3097             for(var i =0;i < items.length;i++) {
3098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3099             }
3100         }
3101
3102         this.items = nitems;
3103
3104         // where are these used - they used to be body/close/footer
3105
3106
3107         this.initEvents();
3108         //this.el.addClass([this.fieldClass, this.cls]);
3109
3110     },
3111
3112     getAutoCreate : function()
3113     {
3114         // we will default to modal-body-overflow - might need to remove or make optional later.
3115         var bdy = {
3116                 cls : 'modal-body enable-modal-body-overflow ', 
3117                 html : this.html || ''
3118         };
3119
3120         var title = {
3121             tag: 'h4',
3122             cls : 'modal-title',
3123             html : this.title
3124         };
3125
3126         if(this.specificTitle){
3127             title = this.title;
3128
3129         }
3130
3131         var header = [];
3132         if (this.allow_close && Roo.bootstrap.version == 3) {
3133             header.push({
3134                 tag: 'button',
3135                 cls : 'close',
3136                 html : '&times'
3137             });
3138         }
3139
3140         header.push(title);
3141
3142         if (this.allow_close && Roo.bootstrap.version == 4) {
3143             header.push({
3144                 tag: 'button',
3145                 cls : 'close',
3146                 html : '&times'
3147             });
3148         }
3149         
3150         var size = '';
3151
3152         if(this.size.length){
3153             size = 'modal-' + this.size;
3154         }
3155         
3156         var footer = Roo.bootstrap.version == 3 ?
3157             {
3158                 cls : 'modal-footer',
3159                 cn : [
3160                     {
3161                         tag: 'div',
3162                         cls: 'btn-' + this.buttonPosition
3163                     }
3164                 ]
3165
3166             } :
3167             {  // BS4 uses mr-auto on left buttons....
3168                 cls : 'modal-footer'
3169             };
3170
3171             
3172
3173         
3174         
3175         var modal = {
3176             cls: "modal",
3177              cn : [
3178                 {
3179                     cls: "modal-dialog " + size,
3180                     cn : [
3181                         {
3182                             cls : "modal-content",
3183                             cn : [
3184                                 {
3185                                     cls : 'modal-header',
3186                                     cn : header
3187                                 },
3188                                 bdy,
3189                                 footer
3190                             ]
3191
3192                         }
3193                     ]
3194
3195                 }
3196             ]
3197         };
3198
3199         if(this.animate){
3200             modal.cls += ' fade';
3201         }
3202
3203         return modal;
3204
3205     },
3206     getChildContainer : function() {
3207
3208          return this.bodyEl;
3209
3210     },
3211     getButtonContainer : function() {
3212         
3213          return Roo.bootstrap.version == 4 ?
3214             this.el.select('.modal-footer',true).first()
3215             : this.el.select('.modal-footer div',true).first();
3216
3217     },
3218     initEvents : function()
3219     {
3220         if (this.allow_close) {
3221             this.closeEl.on('click', this.hide, this);
3222         }
3223         Roo.EventManager.onWindowResize(this.resize, this, true);
3224
3225
3226     },
3227   
3228
3229     resize : function()
3230     {
3231         this.maskEl.setSize(
3232             Roo.lib.Dom.getViewWidth(true),
3233             Roo.lib.Dom.getViewHeight(true)
3234         );
3235         
3236         if (this.fitwindow) {
3237             
3238            
3239             this.setSize(
3240                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3241                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3242             );
3243             return;
3244         }
3245         
3246         if(this.max_width !== 0) {
3247             
3248             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3249             
3250             if(this.height) {
3251                 this.setSize(w, this.height);
3252                 return;
3253             }
3254             
3255             if(this.max_height) {
3256                 this.setSize(w,Math.min(
3257                     this.max_height,
3258                     Roo.lib.Dom.getViewportHeight(true) - 60
3259                 ));
3260                 
3261                 return;
3262             }
3263             
3264             if(!this.fit_content) {
3265                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3266                 return;
3267             }
3268             
3269             this.setSize(w, Math.min(
3270                 60 +
3271                 this.headerEl.getHeight() + 
3272                 this.footerEl.getHeight() + 
3273                 this.getChildHeight(this.bodyEl.dom.childNodes),
3274                 Roo.lib.Dom.getViewportHeight(true) - 60)
3275             );
3276         }
3277         
3278     },
3279
3280     setSize : function(w,h)
3281     {
3282         if (!w && !h) {
3283             return;
3284         }
3285         
3286         this.resizeTo(w,h);
3287     },
3288
3289     show : function() {
3290
3291         if (!this.rendered) {
3292             this.render();
3293         }
3294
3295         //this.el.setStyle('display', 'block');
3296         this.el.removeClass('hideing');
3297         this.el.dom.style.display='block';
3298         
3299         Roo.get(document.body).addClass('modal-open');
3300  
3301         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3302             
3303             (function(){
3304                 this.el.addClass('show');
3305                 this.el.addClass('in');
3306             }).defer(50, this);
3307         }else{
3308             this.el.addClass('show');
3309             this.el.addClass('in');
3310         }
3311
3312         // not sure how we can show data in here..
3313         //if (this.tmpl) {
3314         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3315         //}
3316
3317         Roo.get(document.body).addClass("x-body-masked");
3318         
3319         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3320         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3321         this.maskEl.dom.style.display = 'block';
3322         this.maskEl.addClass('show');
3323         
3324         
3325         this.resize();
3326         
3327         this.fireEvent('show', this);
3328
3329         // set zindex here - otherwise it appears to be ignored...
3330         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3331
3332         (function () {
3333             this.items.forEach( function(e) {
3334                 e.layout ? e.layout() : false;
3335
3336             });
3337         }).defer(100,this);
3338
3339     },
3340     hide : function()
3341     {
3342         if(this.fireEvent("beforehide", this) !== false){
3343             
3344             this.maskEl.removeClass('show');
3345             
3346             this.maskEl.dom.style.display = '';
3347             Roo.get(document.body).removeClass("x-body-masked");
3348             this.el.removeClass('in');
3349             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3350
3351             if(this.animate){ // why
3352                 this.el.addClass('hideing');
3353                 this.el.removeClass('show');
3354                 (function(){
3355                     if (!this.el.hasClass('hideing')) {
3356                         return; // it's been shown again...
3357                     }
3358                     
3359                     this.el.dom.style.display='';
3360
3361                     Roo.get(document.body).removeClass('modal-open');
3362                     this.el.removeClass('hideing');
3363                 }).defer(150,this);
3364                 
3365             }else{
3366                 this.el.removeClass('show');
3367                 this.el.dom.style.display='';
3368                 Roo.get(document.body).removeClass('modal-open');
3369
3370             }
3371             this.fireEvent('hide', this);
3372         }
3373     },
3374     isVisible : function()
3375     {
3376         
3377         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3378         
3379     },
3380
3381     addButton : function(str, cb)
3382     {
3383
3384
3385         var b = Roo.apply({}, { html : str } );
3386         b.xns = b.xns || Roo.bootstrap;
3387         b.xtype = b.xtype || 'Button';
3388         if (typeof(b.listeners) == 'undefined') {
3389             b.listeners = { click : cb.createDelegate(this)  };
3390         }
3391
3392         var btn = Roo.factory(b);
3393
3394         btn.render(this.getButtonContainer());
3395
3396         return btn;
3397
3398     },
3399
3400     setDefaultButton : function(btn)
3401     {
3402         //this.el.select('.modal-footer').()
3403     },
3404
3405     resizeTo: function(w,h)
3406     {
3407         this.dialogEl.setWidth(w);
3408         
3409         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3410
3411         this.bodyEl.setHeight(h - diff);
3412         
3413         this.fireEvent('resize', this);
3414     },
3415     
3416     setContentSize  : function(w, h)
3417     {
3418
3419     },
3420     onButtonClick: function(btn,e)
3421     {
3422         //Roo.log([a,b,c]);
3423         this.fireEvent('btnclick', btn.name, e);
3424     },
3425      /**
3426      * Set the title of the Dialog
3427      * @param {String} str new Title
3428      */
3429     setTitle: function(str) {
3430         this.titleEl.dom.innerHTML = str;
3431     },
3432     /**
3433      * Set the body of the Dialog
3434      * @param {String} str new Title
3435      */
3436     setBody: function(str) {
3437         this.bodyEl.dom.innerHTML = str;
3438     },
3439     /**
3440      * Set the body of the Dialog using the template
3441      * @param {Obj} data - apply this data to the template and replace the body contents.
3442      */
3443     applyBody: function(obj)
3444     {
3445         if (!this.tmpl) {
3446             Roo.log("Error - using apply Body without a template");
3447             //code
3448         }
3449         this.tmpl.overwrite(this.bodyEl, obj);
3450     },
3451     
3452     getChildHeight : function(child_nodes)
3453     {
3454         if(
3455             !child_nodes ||
3456             child_nodes.length == 0
3457         ) {
3458             return;
3459         }
3460         
3461         var child_height = 0;
3462         
3463         for(var i = 0; i < child_nodes.length; i++) {
3464             
3465             /*
3466             * for modal with tabs...
3467             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3468                 
3469                 var layout_childs = child_nodes[i].childNodes;
3470                 
3471                 for(var j = 0; j < layout_childs.length; j++) {
3472                     
3473                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3474                         
3475                         var layout_body_childs = layout_childs[j].childNodes;
3476                         
3477                         for(var k = 0; k < layout_body_childs.length; k++) {
3478                             
3479                             if(layout_body_childs[k].classList.contains('navbar')) {
3480                                 child_height += layout_body_childs[k].offsetHeight;
3481                                 continue;
3482                             }
3483                             
3484                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3485                                 
3486                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3487                                 
3488                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3489                                     
3490                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3491                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3492                                         continue;
3493                                     }
3494                                     
3495                                 }
3496                                 
3497                             }
3498                             
3499                         }
3500                     }
3501                 }
3502                 continue;
3503             }
3504             */
3505             
3506             child_height += child_nodes[i].offsetHeight;
3507             // Roo.log(child_nodes[i].offsetHeight);
3508         }
3509         
3510         return child_height;
3511     }
3512
3513 });
3514
3515
3516 Roo.apply(Roo.bootstrap.Modal,  {
3517     /**
3518          * Button config that displays a single OK button
3519          * @type Object
3520          */
3521         OK :  [{
3522             name : 'ok',
3523             weight : 'primary',
3524             html : 'OK'
3525         }],
3526         /**
3527          * Button config that displays Yes and No buttons
3528          * @type Object
3529          */
3530         YESNO : [
3531             {
3532                 name  : 'no',
3533                 html : 'No'
3534             },
3535             {
3536                 name  :'yes',
3537                 weight : 'primary',
3538                 html : 'Yes'
3539             }
3540         ],
3541
3542         /**
3543          * Button config that displays OK and Cancel buttons
3544          * @type Object
3545          */
3546         OKCANCEL : [
3547             {
3548                name : 'cancel',
3549                 html : 'Cancel'
3550             },
3551             {
3552                 name : 'ok',
3553                 weight : 'primary',
3554                 html : 'OK'
3555             }
3556         ],
3557         /**
3558          * Button config that displays Yes, No and Cancel buttons
3559          * @type Object
3560          */
3561         YESNOCANCEL : [
3562             {
3563                 name : 'yes',
3564                 weight : 'primary',
3565                 html : 'Yes'
3566             },
3567             {
3568                 name : 'no',
3569                 html : 'No'
3570             },
3571             {
3572                 name : 'cancel',
3573                 html : 'Cancel'
3574             }
3575         ],
3576         
3577         zIndex : 10001
3578 });
3579 /*
3580  * - LGPL
3581  *
3582  * messagebox - can be used as a replace
3583  * 
3584  */
3585 /**
3586  * @class Roo.MessageBox
3587  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3588  * Example usage:
3589  *<pre><code>
3590 // Basic alert:
3591 Roo.Msg.alert('Status', 'Changes saved successfully.');
3592
3593 // Prompt for user data:
3594 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3595     if (btn == 'ok'){
3596         // process text value...
3597     }
3598 });
3599
3600 // Show a dialog using config options:
3601 Roo.Msg.show({
3602    title:'Save Changes?',
3603    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3604    buttons: Roo.Msg.YESNOCANCEL,
3605    fn: processResult,
3606    animEl: 'elId'
3607 });
3608 </code></pre>
3609  * @singleton
3610  */
3611 Roo.bootstrap.MessageBox = function(){
3612     var dlg, opt, mask, waitTimer;
3613     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3614     var buttons, activeTextEl, bwidth;
3615
3616     
3617     // private
3618     var handleButton = function(button){
3619         dlg.hide();
3620         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3621     };
3622
3623     // private
3624     var handleHide = function(){
3625         if(opt && opt.cls){
3626             dlg.el.removeClass(opt.cls);
3627         }
3628         //if(waitTimer){
3629         //    Roo.TaskMgr.stop(waitTimer);
3630         //    waitTimer = null;
3631         //}
3632     };
3633
3634     // private
3635     var updateButtons = function(b){
3636         var width = 0;
3637         if(!b){
3638             buttons["ok"].hide();
3639             buttons["cancel"].hide();
3640             buttons["yes"].hide();
3641             buttons["no"].hide();
3642             dlg.footerEl.hide();
3643             
3644             return width;
3645         }
3646         dlg.footerEl.show();
3647         for(var k in buttons){
3648             if(typeof buttons[k] != "function"){
3649                 if(b[k]){
3650                     buttons[k].show();
3651                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3652                     width += buttons[k].el.getWidth()+15;
3653                 }else{
3654                     buttons[k].hide();
3655                 }
3656             }
3657         }
3658         return width;
3659     };
3660
3661     // private
3662     var handleEsc = function(d, k, e){
3663         if(opt && opt.closable !== false){
3664             dlg.hide();
3665         }
3666         if(e){
3667             e.stopEvent();
3668         }
3669     };
3670
3671     return {
3672         /**
3673          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3674          * @return {Roo.BasicDialog} The BasicDialog element
3675          */
3676         getDialog : function(){
3677            if(!dlg){
3678                 dlg = new Roo.bootstrap.Modal( {
3679                     //draggable: true,
3680                     //resizable:false,
3681                     //constraintoviewport:false,
3682                     //fixedcenter:true,
3683                     //collapsible : false,
3684                     //shim:true,
3685                     //modal: true,
3686                 //    width: 'auto',
3687                   //  height:100,
3688                     //buttonAlign:"center",
3689                     closeClick : function(){
3690                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3691                             handleButton("no");
3692                         }else{
3693                             handleButton("cancel");
3694                         }
3695                     }
3696                 });
3697                 dlg.render();
3698                 dlg.on("hide", handleHide);
3699                 mask = dlg.mask;
3700                 //dlg.addKeyListener(27, handleEsc);
3701                 buttons = {};
3702                 this.buttons = buttons;
3703                 var bt = this.buttonText;
3704                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3705                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3706                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3707                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3708                 //Roo.log(buttons);
3709                 bodyEl = dlg.bodyEl.createChild({
3710
3711                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3712                         '<textarea class="roo-mb-textarea"></textarea>' +
3713                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3714                 });
3715                 msgEl = bodyEl.dom.firstChild;
3716                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3717                 textboxEl.enableDisplayMode();
3718                 textboxEl.addKeyListener([10,13], function(){
3719                     if(dlg.isVisible() && opt && opt.buttons){
3720                         if(opt.buttons.ok){
3721                             handleButton("ok");
3722                         }else if(opt.buttons.yes){
3723                             handleButton("yes");
3724                         }
3725                     }
3726                 });
3727                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3728                 textareaEl.enableDisplayMode();
3729                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3730                 progressEl.enableDisplayMode();
3731                 
3732                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3733                 var pf = progressEl.dom.firstChild;
3734                 if (pf) {
3735                     pp = Roo.get(pf.firstChild);
3736                     pp.setHeight(pf.offsetHeight);
3737                 }
3738                 
3739             }
3740             return dlg;
3741         },
3742
3743         /**
3744          * Updates the message box body text
3745          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3746          * the XHTML-compliant non-breaking space character '&amp;#160;')
3747          * @return {Roo.MessageBox} This message box
3748          */
3749         updateText : function(text)
3750         {
3751             if(!dlg.isVisible() && !opt.width){
3752                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3753                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3754             }
3755             msgEl.innerHTML = text || '&#160;';
3756       
3757             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3758             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3759             var w = Math.max(
3760                     Math.min(opt.width || cw , this.maxWidth), 
3761                     Math.max(opt.minWidth || this.minWidth, bwidth)
3762             );
3763             if(opt.prompt){
3764                 activeTextEl.setWidth(w);
3765             }
3766             if(dlg.isVisible()){
3767                 dlg.fixedcenter = false;
3768             }
3769             // to big, make it scroll. = But as usual stupid IE does not support
3770             // !important..
3771             
3772             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3773                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3774                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3775             } else {
3776                 bodyEl.dom.style.height = '';
3777                 bodyEl.dom.style.overflowY = '';
3778             }
3779             if (cw > w) {
3780                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3781             } else {
3782                 bodyEl.dom.style.overflowX = '';
3783             }
3784             
3785             dlg.setContentSize(w, bodyEl.getHeight());
3786             if(dlg.isVisible()){
3787                 dlg.fixedcenter = true;
3788             }
3789             return this;
3790         },
3791
3792         /**
3793          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3794          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3795          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3796          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3797          * @return {Roo.MessageBox} This message box
3798          */
3799         updateProgress : function(value, text){
3800             if(text){
3801                 this.updateText(text);
3802             }
3803             
3804             if (pp) { // weird bug on my firefox - for some reason this is not defined
3805                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3806                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3807             }
3808             return this;
3809         },        
3810
3811         /**
3812          * Returns true if the message box is currently displayed
3813          * @return {Boolean} True if the message box is visible, else false
3814          */
3815         isVisible : function(){
3816             return dlg && dlg.isVisible();  
3817         },
3818
3819         /**
3820          * Hides the message box if it is displayed
3821          */
3822         hide : function(){
3823             if(this.isVisible()){
3824                 dlg.hide();
3825             }  
3826         },
3827
3828         /**
3829          * Displays a new message box, or reinitializes an existing message box, based on the config options
3830          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3831          * The following config object properties are supported:
3832          * <pre>
3833 Property    Type             Description
3834 ----------  ---------------  ------------------------------------------------------------------------------------
3835 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3836                                    closes (defaults to undefined)
3837 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3838                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3839 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3840                                    progress and wait dialogs will ignore this property and always hide the
3841                                    close button as they can only be closed programmatically.
3842 cls               String           A custom CSS class to apply to the message box element
3843 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3844                                    displayed (defaults to 75)
3845 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3846                                    function will be btn (the name of the button that was clicked, if applicable,
3847                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3848                                    Progress and wait dialogs will ignore this option since they do not respond to
3849                                    user actions and can only be closed programmatically, so any required function
3850                                    should be called by the same code after it closes the dialog.
3851 icon              String           A CSS class that provides a background image to be used as an icon for
3852                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3853 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3854 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3855 modal             Boolean          False to allow user interaction with the page while the message box is
3856                                    displayed (defaults to true)
3857 msg               String           A string that will replace the existing message box body text (defaults
3858                                    to the XHTML-compliant non-breaking space character '&#160;')
3859 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3860 progress          Boolean          True to display a progress bar (defaults to false)
3861 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3862 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3863 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3864 title             String           The title text
3865 value             String           The string value to set into the active textbox element if displayed
3866 wait              Boolean          True to display a progress bar (defaults to false)
3867 width             Number           The width of the dialog in pixels
3868 </pre>
3869          *
3870          * Example usage:
3871          * <pre><code>
3872 Roo.Msg.show({
3873    title: 'Address',
3874    msg: 'Please enter your address:',
3875    width: 300,
3876    buttons: Roo.MessageBox.OKCANCEL,
3877    multiline: true,
3878    fn: saveAddress,
3879    animEl: 'addAddressBtn'
3880 });
3881 </code></pre>
3882          * @param {Object} config Configuration options
3883          * @return {Roo.MessageBox} This message box
3884          */
3885         show : function(options)
3886         {
3887             
3888             // this causes nightmares if you show one dialog after another
3889             // especially on callbacks..
3890              
3891             if(this.isVisible()){
3892                 
3893                 this.hide();
3894                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3895                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3896                 Roo.log("New Dialog Message:" +  options.msg )
3897                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3898                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3899                 
3900             }
3901             var d = this.getDialog();
3902             opt = options;
3903             d.setTitle(opt.title || "&#160;");
3904             d.closeEl.setDisplayed(opt.closable !== false);
3905             activeTextEl = textboxEl;
3906             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3907             if(opt.prompt){
3908                 if(opt.multiline){
3909                     textboxEl.hide();
3910                     textareaEl.show();
3911                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3912                         opt.multiline : this.defaultTextHeight);
3913                     activeTextEl = textareaEl;
3914                 }else{
3915                     textboxEl.show();
3916                     textareaEl.hide();
3917                 }
3918             }else{
3919                 textboxEl.hide();
3920                 textareaEl.hide();
3921             }
3922             progressEl.setDisplayed(opt.progress === true);
3923             if (opt.progress) {
3924                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3925             }
3926             this.updateProgress(0);
3927             activeTextEl.dom.value = opt.value || "";
3928             if(opt.prompt){
3929                 dlg.setDefaultButton(activeTextEl);
3930             }else{
3931                 var bs = opt.buttons;
3932                 var db = null;
3933                 if(bs && bs.ok){
3934                     db = buttons["ok"];
3935                 }else if(bs && bs.yes){
3936                     db = buttons["yes"];
3937                 }
3938                 dlg.setDefaultButton(db);
3939             }
3940             bwidth = updateButtons(opt.buttons);
3941             this.updateText(opt.msg);
3942             if(opt.cls){
3943                 d.el.addClass(opt.cls);
3944             }
3945             d.proxyDrag = opt.proxyDrag === true;
3946             d.modal = opt.modal !== false;
3947             d.mask = opt.modal !== false ? mask : false;
3948             if(!d.isVisible()){
3949                 // force it to the end of the z-index stack so it gets a cursor in FF
3950                 document.body.appendChild(dlg.el.dom);
3951                 d.animateTarget = null;
3952                 d.show(options.animEl);
3953             }
3954             return this;
3955         },
3956
3957         /**
3958          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3959          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3960          * and closing the message box when the process is complete.
3961          * @param {String} title The title bar text
3962          * @param {String} msg The message box body text
3963          * @return {Roo.MessageBox} This message box
3964          */
3965         progress : function(title, msg){
3966             this.show({
3967                 title : title,
3968                 msg : msg,
3969                 buttons: false,
3970                 progress:true,
3971                 closable:false,
3972                 minWidth: this.minProgressWidth,
3973                 modal : true
3974             });
3975             return this;
3976         },
3977
3978         /**
3979          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3980          * If a callback function is passed it will be called after the user clicks the button, and the
3981          * id of the button that was clicked will be passed as the only parameter to the callback
3982          * (could also be the top-right close button).
3983          * @param {String} title The title bar text
3984          * @param {String} msg The message box body text
3985          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3986          * @param {Object} scope (optional) The scope of the callback function
3987          * @return {Roo.MessageBox} This message box
3988          */
3989         alert : function(title, msg, fn, scope)
3990         {
3991             this.show({
3992                 title : title,
3993                 msg : msg,
3994                 buttons: this.OK,
3995                 fn: fn,
3996                 closable : false,
3997                 scope : scope,
3998                 modal : true
3999             });
4000             return this;
4001         },
4002
4003         /**
4004          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4005          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4006          * You are responsible for closing the message box when the process is complete.
4007          * @param {String} msg The message box body text
4008          * @param {String} title (optional) The title bar text
4009          * @return {Roo.MessageBox} This message box
4010          */
4011         wait : function(msg, title){
4012             this.show({
4013                 title : title,
4014                 msg : msg,
4015                 buttons: false,
4016                 closable:false,
4017                 progress:true,
4018                 modal:true,
4019                 width:300,
4020                 wait:true
4021             });
4022             waitTimer = Roo.TaskMgr.start({
4023                 run: function(i){
4024                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4025                 },
4026                 interval: 1000
4027             });
4028             return this;
4029         },
4030
4031         /**
4032          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4033          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4034          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4035          * @param {String} title The title bar text
4036          * @param {String} msg The message box body text
4037          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4038          * @param {Object} scope (optional) The scope of the callback function
4039          * @return {Roo.MessageBox} This message box
4040          */
4041         confirm : function(title, msg, fn, scope){
4042             this.show({
4043                 title : title,
4044                 msg : msg,
4045                 buttons: this.YESNO,
4046                 fn: fn,
4047                 scope : scope,
4048                 modal : true
4049             });
4050             return this;
4051         },
4052
4053         /**
4054          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4055          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4056          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4057          * (could also be the top-right close button) and the text that was entered will be passed as the two
4058          * parameters to the callback.
4059          * @param {String} title The title bar text
4060          * @param {String} msg The message box body text
4061          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4062          * @param {Object} scope (optional) The scope of the callback function
4063          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4064          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4065          * @return {Roo.MessageBox} This message box
4066          */
4067         prompt : function(title, msg, fn, scope, multiline){
4068             this.show({
4069                 title : title,
4070                 msg : msg,
4071                 buttons: this.OKCANCEL,
4072                 fn: fn,
4073                 minWidth:250,
4074                 scope : scope,
4075                 prompt:true,
4076                 multiline: multiline,
4077                 modal : true
4078             });
4079             return this;
4080         },
4081
4082         /**
4083          * Button config that displays a single OK button
4084          * @type Object
4085          */
4086         OK : {ok:true},
4087         /**
4088          * Button config that displays Yes and No buttons
4089          * @type Object
4090          */
4091         YESNO : {yes:true, no:true},
4092         /**
4093          * Button config that displays OK and Cancel buttons
4094          * @type Object
4095          */
4096         OKCANCEL : {ok:true, cancel:true},
4097         /**
4098          * Button config that displays Yes, No and Cancel buttons
4099          * @type Object
4100          */
4101         YESNOCANCEL : {yes:true, no:true, cancel:true},
4102
4103         /**
4104          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4105          * @type Number
4106          */
4107         defaultTextHeight : 75,
4108         /**
4109          * The maximum width in pixels of the message box (defaults to 600)
4110          * @type Number
4111          */
4112         maxWidth : 600,
4113         /**
4114          * The minimum width in pixels of the message box (defaults to 100)
4115          * @type Number
4116          */
4117         minWidth : 100,
4118         /**
4119          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4120          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4121          * @type Number
4122          */
4123         minProgressWidth : 250,
4124         /**
4125          * An object containing the default button text strings that can be overriden for localized language support.
4126          * Supported properties are: ok, cancel, yes and no.
4127          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4128          * @type Object
4129          */
4130         buttonText : {
4131             ok : "OK",
4132             cancel : "Cancel",
4133             yes : "Yes",
4134             no : "No"
4135         }
4136     };
4137 }();
4138
4139 /**
4140  * Shorthand for {@link Roo.MessageBox}
4141  */
4142 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4143 Roo.Msg = Roo.Msg || Roo.MessageBox;
4144 /*
4145  * - LGPL
4146  *
4147  * navbar
4148  * 
4149  */
4150
4151 /**
4152  * @class Roo.bootstrap.Navbar
4153  * @extends Roo.bootstrap.Component
4154  * Bootstrap Navbar class
4155
4156  * @constructor
4157  * Create a new Navbar
4158  * @param {Object} config The config object
4159  */
4160
4161
4162 Roo.bootstrap.Navbar = function(config){
4163     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4164     this.addEvents({
4165         // raw events
4166         /**
4167          * @event beforetoggle
4168          * Fire before toggle the menu
4169          * @param {Roo.EventObject} e
4170          */
4171         "beforetoggle" : true
4172     });
4173 };
4174
4175 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4176     
4177     
4178    
4179     // private
4180     navItems : false,
4181     loadMask : false,
4182     
4183     
4184     getAutoCreate : function(){
4185         
4186         
4187         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4188         
4189     },
4190     
4191     initEvents :function ()
4192     {
4193         //Roo.log(this.el.select('.navbar-toggle',true));
4194         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4195         
4196         var mark = {
4197             tag: "div",
4198             cls:"x-dlg-mask"
4199         };
4200         
4201         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4202         
4203         var size = this.el.getSize();
4204         this.maskEl.setSize(size.width, size.height);
4205         this.maskEl.enableDisplayMode("block");
4206         this.maskEl.hide();
4207         
4208         if(this.loadMask){
4209             this.maskEl.show();
4210         }
4211     },
4212     
4213     
4214     getChildContainer : function()
4215     {
4216         if (this.el && this.el.select('.collapse').getCount()) {
4217             return this.el.select('.collapse',true).first();
4218         }
4219         
4220         return this.el;
4221     },
4222     
4223     mask : function()
4224     {
4225         this.maskEl.show();
4226     },
4227     
4228     unmask : function()
4229     {
4230         this.maskEl.hide();
4231     },
4232     onToggle : function()
4233     {
4234         
4235         if(this.fireEvent('beforetoggle', this) === false){
4236             return;
4237         }
4238         var ce = this.el.select('.navbar-collapse',true).first();
4239       
4240         if (!ce.hasClass('show')) {
4241            this.expand();
4242         } else {
4243             this.collapse();
4244         }
4245         
4246         
4247     
4248     },
4249     /**
4250      * Expand the navbar pulldown 
4251      */
4252     expand : function ()
4253     {
4254        
4255         var ce = this.el.select('.navbar-collapse',true).first();
4256         if (ce.hasClass('collapsing')) {
4257             return;
4258         }
4259         ce.dom.style.height = '';
4260                // show it...
4261         ce.addClass('in'); // old...
4262         ce.removeClass('collapse');
4263         ce.addClass('show');
4264         var h = ce.getHeight();
4265         Roo.log(h);
4266         ce.removeClass('show');
4267         // at this point we should be able to see it..
4268         ce.addClass('collapsing');
4269         
4270         ce.setHeight(0); // resize it ...
4271         ce.on('transitionend', function() {
4272             //Roo.log('done transition');
4273             ce.removeClass('collapsing');
4274             ce.addClass('show');
4275             ce.removeClass('collapse');
4276
4277             ce.dom.style.height = '';
4278         }, this, { single: true} );
4279         ce.setHeight(h);
4280         ce.dom.scrollTop = 0;
4281     },
4282     /**
4283      * Collapse the navbar pulldown 
4284      */
4285     collapse : function()
4286     {
4287          var ce = this.el.select('.navbar-collapse',true).first();
4288        
4289         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4290             // it's collapsed or collapsing..
4291             return;
4292         }
4293         ce.removeClass('in'); // old...
4294         ce.setHeight(ce.getHeight());
4295         ce.removeClass('show');
4296         ce.addClass('collapsing');
4297         
4298         ce.on('transitionend', function() {
4299             ce.dom.style.height = '';
4300             ce.removeClass('collapsing');
4301             ce.addClass('collapse');
4302         }, this, { single: true} );
4303         ce.setHeight(0);
4304     }
4305     
4306     
4307     
4308 });
4309
4310
4311
4312  
4313
4314  /*
4315  * - LGPL
4316  *
4317  * navbar
4318  * 
4319  */
4320
4321 /**
4322  * @class Roo.bootstrap.NavSimplebar
4323  * @extends Roo.bootstrap.Navbar
4324  * Bootstrap Sidebar class
4325  *
4326  * @cfg {Boolean} inverse is inverted color
4327  * 
4328  * @cfg {String} type (nav | pills | tabs)
4329  * @cfg {Boolean} arrangement stacked | justified
4330  * @cfg {String} align (left | right) alignment
4331  * 
4332  * @cfg {Boolean} main (true|false) main nav bar? default false
4333  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4334  * 
4335  * @cfg {String} tag (header|footer|nav|div) default is nav 
4336
4337  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4338  * 
4339  * 
4340  * @constructor
4341  * Create a new Sidebar
4342  * @param {Object} config The config object
4343  */
4344
4345
4346 Roo.bootstrap.NavSimplebar = function(config){
4347     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4348 };
4349
4350 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4351     
4352     inverse: false,
4353     
4354     type: false,
4355     arrangement: '',
4356     align : false,
4357     
4358     weight : 'light',
4359     
4360     main : false,
4361     
4362     
4363     tag : false,
4364     
4365     
4366     getAutoCreate : function(){
4367         
4368         
4369         var cfg = {
4370             tag : this.tag || 'div',
4371             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4372         };
4373         if (['light','white'].indexOf(this.weight) > -1) {
4374             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4375         }
4376         cfg.cls += ' bg-' + this.weight;
4377         
4378         if (this.inverse) {
4379             cfg.cls += ' navbar-inverse';
4380             
4381         }
4382         
4383         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4384         
4385         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4386             return cfg;
4387         }
4388         
4389         
4390     
4391         
4392         cfg.cn = [
4393             {
4394                 cls: 'nav nav-' + this.xtype,
4395                 tag : 'ul'
4396             }
4397         ];
4398         
4399          
4400         this.type = this.type || 'nav';
4401         if (['tabs','pills'].indexOf(this.type) != -1) {
4402             cfg.cn[0].cls += ' nav-' + this.type
4403         
4404         
4405         } else {
4406             if (this.type!=='nav') {
4407                 Roo.log('nav type must be nav/tabs/pills')
4408             }
4409             cfg.cn[0].cls += ' navbar-nav'
4410         }
4411         
4412         
4413         
4414         
4415         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4416             cfg.cn[0].cls += ' nav-' + this.arrangement;
4417         }
4418         
4419         
4420         if (this.align === 'right') {
4421             cfg.cn[0].cls += ' navbar-right';
4422         }
4423         
4424         
4425         
4426         
4427         return cfg;
4428     
4429         
4430     }
4431     
4432     
4433     
4434 });
4435
4436
4437
4438  
4439
4440  
4441        /*
4442  * - LGPL
4443  *
4444  * navbar
4445  * navbar-fixed-top
4446  * navbar-expand-md  fixed-top 
4447  */
4448
4449 /**
4450  * @class Roo.bootstrap.NavHeaderbar
4451  * @extends Roo.bootstrap.NavSimplebar
4452  * Bootstrap Sidebar class
4453  *
4454  * @cfg {String} brand what is brand
4455  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4456  * @cfg {String} brand_href href of the brand
4457  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4458  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4459  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4460  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4461  * 
4462  * @constructor
4463  * Create a new Sidebar
4464  * @param {Object} config The config object
4465  */
4466
4467
4468 Roo.bootstrap.NavHeaderbar = function(config){
4469     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4470       
4471 };
4472
4473 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4474     
4475     position: '',
4476     brand: '',
4477     brand_href: false,
4478     srButton : true,
4479     autohide : false,
4480     desktopCenter : false,
4481    
4482     
4483     getAutoCreate : function(){
4484         
4485         var   cfg = {
4486             tag: this.nav || 'nav',
4487             cls: 'navbar navbar-expand-md',
4488             role: 'navigation',
4489             cn: []
4490         };
4491         
4492         var cn = cfg.cn;
4493         if (this.desktopCenter) {
4494             cn.push({cls : 'container', cn : []});
4495             cn = cn[0].cn;
4496         }
4497         
4498         if(this.srButton){
4499             var btn = {
4500                 tag: 'button',
4501                 type: 'button',
4502                 cls: 'navbar-toggle navbar-toggler',
4503                 'data-toggle': 'collapse',
4504                 cn: [
4505                     {
4506                         tag: 'span',
4507                         cls: 'sr-only',
4508                         html: 'Toggle navigation'
4509                     },
4510                     {
4511                         tag: 'span',
4512                         cls: 'icon-bar navbar-toggler-icon'
4513                     },
4514                     {
4515                         tag: 'span',
4516                         cls: 'icon-bar'
4517                     },
4518                     {
4519                         tag: 'span',
4520                         cls: 'icon-bar'
4521                     }
4522                 ]
4523             };
4524             
4525             cn.push( Roo.bootstrap.version == 4 ? btn : {
4526                 tag: 'div',
4527                 cls: 'navbar-header',
4528                 cn: [
4529                     btn
4530                 ]
4531             });
4532         }
4533         
4534         cn.push({
4535             tag: 'div',
4536             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4537             cn : []
4538         });
4539         
4540         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4541         
4542         if (['light','white'].indexOf(this.weight) > -1) {
4543             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4544         }
4545         cfg.cls += ' bg-' + this.weight;
4546         
4547         
4548         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4549             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4550             
4551             // tag can override this..
4552             
4553             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4554         }
4555         
4556         if (this.brand !== '') {
4557             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4558             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4559                 tag: 'a',
4560                 href: this.brand_href ? this.brand_href : '#',
4561                 cls: 'navbar-brand',
4562                 cn: [
4563                 this.brand
4564                 ]
4565             });
4566         }
4567         
4568         if(this.main){
4569             cfg.cls += ' main-nav';
4570         }
4571         
4572         
4573         return cfg;
4574
4575         
4576     },
4577     getHeaderChildContainer : function()
4578     {
4579         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4580             return this.el.select('.navbar-header',true).first();
4581         }
4582         
4583         return this.getChildContainer();
4584     },
4585     
4586     getChildContainer : function()
4587     {
4588          
4589         return this.el.select('.roo-navbar-collapse',true).first();
4590          
4591         
4592     },
4593     
4594     initEvents : function()
4595     {
4596         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4597         
4598         if (this.autohide) {
4599             
4600             var prevScroll = 0;
4601             var ft = this.el;
4602             
4603             Roo.get(document).on('scroll',function(e) {
4604                 var ns = Roo.get(document).getScroll().top;
4605                 var os = prevScroll;
4606                 prevScroll = ns;
4607                 
4608                 if(ns > os){
4609                     ft.removeClass('slideDown');
4610                     ft.addClass('slideUp');
4611                     return;
4612                 }
4613                 ft.removeClass('slideUp');
4614                 ft.addClass('slideDown');
4615                  
4616               
4617           },this);
4618         }
4619     }    
4620     
4621 });
4622
4623
4624
4625  
4626
4627  /*
4628  * - LGPL
4629  *
4630  * navbar
4631  * 
4632  */
4633
4634 /**
4635  * @class Roo.bootstrap.NavSidebar
4636  * @extends Roo.bootstrap.Navbar
4637  * Bootstrap Sidebar class
4638  * 
4639  * @constructor
4640  * Create a new Sidebar
4641  * @param {Object} config The config object
4642  */
4643
4644
4645 Roo.bootstrap.NavSidebar = function(config){
4646     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4647 };
4648
4649 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4650     
4651     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4652     
4653     getAutoCreate : function(){
4654         
4655         
4656         return  {
4657             tag: 'div',
4658             cls: 'sidebar sidebar-nav'
4659         };
4660     
4661         
4662     }
4663     
4664     
4665     
4666 });
4667
4668
4669
4670  
4671
4672  /*
4673  * - LGPL
4674  *
4675  * nav group
4676  * 
4677  */
4678
4679 /**
4680  * @class Roo.bootstrap.NavGroup
4681  * @extends Roo.bootstrap.Component
4682  * Bootstrap NavGroup class
4683  * @cfg {String} align (left|right)
4684  * @cfg {Boolean} inverse
4685  * @cfg {String} type (nav|pills|tab) default nav
4686  * @cfg {String} navId - reference Id for navbar.
4687
4688  * 
4689  * @constructor
4690  * Create a new nav group
4691  * @param {Object} config The config object
4692  */
4693
4694 Roo.bootstrap.NavGroup = function(config){
4695     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4696     this.navItems = [];
4697    
4698     Roo.bootstrap.NavGroup.register(this);
4699      this.addEvents({
4700         /**
4701              * @event changed
4702              * Fires when the active item changes
4703              * @param {Roo.bootstrap.NavGroup} this
4704              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4705              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4706          */
4707         'changed': true
4708      });
4709     
4710 };
4711
4712 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4713     
4714     align: '',
4715     inverse: false,
4716     form: false,
4717     type: 'nav',
4718     navId : '',
4719     // private
4720     
4721     navItems : false, 
4722     
4723     getAutoCreate : function()
4724     {
4725         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4726         
4727         cfg = {
4728             tag : 'ul',
4729             cls: 'nav' 
4730         };
4731         if (Roo.bootstrap.version == 4) {
4732             if (['tabs','pills'].indexOf(this.type) != -1) {
4733                 cfg.cls += ' nav-' + this.type; 
4734             } else {
4735                 // trying to remove so header bar can right align top?
4736                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4737                     // do not use on header bar... 
4738                     cfg.cls += ' navbar-nav';
4739                 }
4740             }
4741             
4742         } else {
4743             if (['tabs','pills'].indexOf(this.type) != -1) {
4744                 cfg.cls += ' nav-' + this.type
4745             } else {
4746                 if (this.type !== 'nav') {
4747                     Roo.log('nav type must be nav/tabs/pills')
4748                 }
4749                 cfg.cls += ' navbar-nav'
4750             }
4751         }
4752         
4753         if (this.parent() && this.parent().sidebar) {
4754             cfg = {
4755                 tag: 'ul',
4756                 cls: 'dashboard-menu sidebar-menu'
4757             };
4758             
4759             return cfg;
4760         }
4761         
4762         if (this.form === true) {
4763             cfg = {
4764                 tag: 'form',
4765                 cls: 'navbar-form form-inline'
4766             };
4767             //nav navbar-right ml-md-auto
4768             if (this.align === 'right') {
4769                 cfg.cls += ' navbar-right ml-md-auto';
4770             } else {
4771                 cfg.cls += ' navbar-left';
4772             }
4773         }
4774         
4775         if (this.align === 'right') {
4776             cfg.cls += ' navbar-right ml-md-auto';
4777         } else {
4778             cfg.cls += ' mr-auto';
4779         }
4780         
4781         if (this.inverse) {
4782             cfg.cls += ' navbar-inverse';
4783             
4784         }
4785         
4786         
4787         return cfg;
4788     },
4789     /**
4790     * sets the active Navigation item
4791     * @param {Roo.bootstrap.NavItem} the new current navitem
4792     */
4793     setActiveItem : function(item)
4794     {
4795         var prev = false;
4796         Roo.each(this.navItems, function(v){
4797             if (v == item) {
4798                 return ;
4799             }
4800             if (v.isActive()) {
4801                 v.setActive(false, true);
4802                 prev = v;
4803                 
4804             }
4805             
4806         });
4807
4808         item.setActive(true, true);
4809         this.fireEvent('changed', this, item, prev);
4810         
4811         
4812     },
4813     /**
4814     * gets the active Navigation item
4815     * @return {Roo.bootstrap.NavItem} the current navitem
4816     */
4817     getActive : function()
4818     {
4819         
4820         var prev = false;
4821         Roo.each(this.navItems, function(v){
4822             
4823             if (v.isActive()) {
4824                 prev = v;
4825                 
4826             }
4827             
4828         });
4829         return prev;
4830     },
4831     
4832     indexOfNav : function()
4833     {
4834         
4835         var prev = false;
4836         Roo.each(this.navItems, function(v,i){
4837             
4838             if (v.isActive()) {
4839                 prev = i;
4840                 
4841             }
4842             
4843         });
4844         return prev;
4845     },
4846     /**
4847     * adds a Navigation item
4848     * @param {Roo.bootstrap.NavItem} the navitem to add
4849     */
4850     addItem : function(cfg)
4851     {
4852         if (this.form && Roo.bootstrap.version == 4) {
4853             cfg.tag = 'div';
4854         }
4855         var cn = new Roo.bootstrap.NavItem(cfg);
4856         this.register(cn);
4857         cn.parentId = this.id;
4858         cn.onRender(this.el, null);
4859         return cn;
4860     },
4861     /**
4862     * register a Navigation item
4863     * @param {Roo.bootstrap.NavItem} the navitem to add
4864     */
4865     register : function(item)
4866     {
4867         this.navItems.push( item);
4868         item.navId = this.navId;
4869     
4870     },
4871     
4872     /**
4873     * clear all the Navigation item
4874     */
4875    
4876     clearAll : function()
4877     {
4878         this.navItems = [];
4879         this.el.dom.innerHTML = '';
4880     },
4881     
4882     getNavItem: function(tabId)
4883     {
4884         var ret = false;
4885         Roo.each(this.navItems, function(e) {
4886             if (e.tabId == tabId) {
4887                ret =  e;
4888                return false;
4889             }
4890             return true;
4891             
4892         });
4893         return ret;
4894     },
4895     
4896     setActiveNext : function()
4897     {
4898         var i = this.indexOfNav(this.getActive());
4899         if (i > this.navItems.length) {
4900             return;
4901         }
4902         this.setActiveItem(this.navItems[i+1]);
4903     },
4904     setActivePrev : function()
4905     {
4906         var i = this.indexOfNav(this.getActive());
4907         if (i  < 1) {
4908             return;
4909         }
4910         this.setActiveItem(this.navItems[i-1]);
4911     },
4912     clearWasActive : function(except) {
4913         Roo.each(this.navItems, function(e) {
4914             if (e.tabId != except.tabId && e.was_active) {
4915                e.was_active = false;
4916                return false;
4917             }
4918             return true;
4919             
4920         });
4921     },
4922     getWasActive : function ()
4923     {
4924         var r = false;
4925         Roo.each(this.navItems, function(e) {
4926             if (e.was_active) {
4927                r = e;
4928                return false;
4929             }
4930             return true;
4931             
4932         });
4933         return r;
4934     }
4935     
4936     
4937 });
4938
4939  
4940 Roo.apply(Roo.bootstrap.NavGroup, {
4941     
4942     groups: {},
4943      /**
4944     * register a Navigation Group
4945     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4946     */
4947     register : function(navgrp)
4948     {
4949         this.groups[navgrp.navId] = navgrp;
4950         
4951     },
4952     /**
4953     * fetch a Navigation Group based on the navigation ID
4954     * @param {string} the navgroup to add
4955     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4956     */
4957     get: function(navId) {
4958         if (typeof(this.groups[navId]) == 'undefined') {
4959             return false;
4960             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4961         }
4962         return this.groups[navId] ;
4963     }
4964     
4965     
4966     
4967 });
4968
4969  /*
4970  * - LGPL
4971  *
4972  * row
4973  * 
4974  */
4975
4976 /**
4977  * @class Roo.bootstrap.NavItem
4978  * @extends Roo.bootstrap.Component
4979  * Bootstrap Navbar.NavItem class
4980  * @cfg {String} href  link to
4981  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4982
4983  * @cfg {String} html content of button
4984  * @cfg {String} badge text inside badge
4985  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4986  * @cfg {String} glyphicon DEPRICATED - use fa
4987  * @cfg {String} icon DEPRICATED - use fa
4988  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4989  * @cfg {Boolean} active Is item active
4990  * @cfg {Boolean} disabled Is item disabled
4991  
4992  * @cfg {Boolean} preventDefault (true | false) default false
4993  * @cfg {String} tabId the tab that this item activates.
4994  * @cfg {String} tagtype (a|span) render as a href or span?
4995  * @cfg {Boolean} animateRef (true|false) link to element default false  
4996   
4997  * @constructor
4998  * Create a new Navbar Item
4999  * @param {Object} config The config object
5000  */
5001 Roo.bootstrap.NavItem = function(config){
5002     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5003     this.addEvents({
5004         // raw events
5005         /**
5006          * @event click
5007          * The raw click event for the entire grid.
5008          * @param {Roo.EventObject} e
5009          */
5010         "click" : true,
5011          /**
5012             * @event changed
5013             * Fires when the active item active state changes
5014             * @param {Roo.bootstrap.NavItem} this
5015             * @param {boolean} state the new state
5016              
5017          */
5018         'changed': true,
5019         /**
5020             * @event scrollto
5021             * Fires when scroll to element
5022             * @param {Roo.bootstrap.NavItem} this
5023             * @param {Object} options
5024             * @param {Roo.EventObject} e
5025              
5026          */
5027         'scrollto': true
5028     });
5029    
5030 };
5031
5032 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5033     
5034     href: false,
5035     html: '',
5036     badge: '',
5037     icon: false,
5038     fa : false,
5039     glyphicon: false,
5040     active: false,
5041     preventDefault : false,
5042     tabId : false,
5043     tagtype : 'a',
5044     tag: 'li',
5045     disabled : false,
5046     animateRef : false,
5047     was_active : false,
5048     button_weight : '',
5049     button_outline : false,
5050     
5051     navLink: false,
5052     
5053     getAutoCreate : function(){
5054          
5055         var cfg = {
5056             tag: this.tag,
5057             cls: 'nav-item'
5058         };
5059         
5060         if (this.active) {
5061             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5062         }
5063         if (this.disabled) {
5064             cfg.cls += ' disabled';
5065         }
5066         
5067         // BS4 only?
5068         if (this.button_weight.length) {
5069             cfg.tag = this.href ? 'a' : 'button';
5070             cfg.html = this.html || '';
5071             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5072             if (this.href) {
5073                 cfg.href = this.href;
5074             }
5075             if (this.fa) {
5076                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5077             }
5078             
5079             // menu .. should add dropdown-menu class - so no need for carat..
5080             
5081             if (this.badge !== '') {
5082                  
5083                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5084             }
5085             return cfg;
5086         }
5087         
5088         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5089             cfg.cn = [
5090                 {
5091                     tag: this.tagtype,
5092                     href : this.href || "#",
5093                     html: this.html || ''
5094                 }
5095             ];
5096             if (this.tagtype == 'a') {
5097                 cfg.cn[0].cls = 'nav-link';
5098             }
5099             if (this.icon) {
5100                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5101             }
5102             if (this.fa) {
5103                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5104             }
5105             if(this.glyphicon) {
5106                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5107             }
5108             
5109             if (this.menu) {
5110                 
5111                 cfg.cn[0].html += " <span class='caret'></span>";
5112              
5113             }
5114             
5115             if (this.badge !== '') {
5116                  
5117                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5118             }
5119         }
5120         
5121         
5122         
5123         return cfg;
5124     },
5125     onRender : function(ct, position)
5126     {
5127        // Roo.log("Call onRender: " + this.xtype);
5128         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5129             this.tag = 'div';
5130         }
5131         
5132         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5133         this.navLink = this.el.select('.nav-link',true).first();
5134         return ret;
5135     },
5136       
5137     
5138     initEvents: function() 
5139     {
5140         if (typeof (this.menu) != 'undefined') {
5141             this.menu.parentType = this.xtype;
5142             this.menu.triggerEl = this.el;
5143             this.menu = this.addxtype(Roo.apply({}, this.menu));
5144         }
5145         
5146         this.el.select('a',true).on('click', this.onClick, this);
5147         
5148         if(this.tagtype == 'span'){
5149             this.el.select('span',true).on('click', this.onClick, this);
5150         }
5151        
5152         // at this point parent should be available..
5153         this.parent().register(this);
5154     },
5155     
5156     onClick : function(e)
5157     {
5158         if (e.getTarget('.dropdown-menu-item')) {
5159             // did you click on a menu itemm.... - then don't trigger onclick..
5160             return;
5161         }
5162         
5163         if(
5164                 this.preventDefault || 
5165                 this.href == '#' 
5166         ){
5167             Roo.log("NavItem - prevent Default?");
5168             e.preventDefault();
5169         }
5170         
5171         if (this.disabled) {
5172             return;
5173         }
5174         
5175         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5176         if (tg && tg.transition) {
5177             Roo.log("waiting for the transitionend");
5178             return;
5179         }
5180         
5181         
5182         
5183         //Roo.log("fire event clicked");
5184         if(this.fireEvent('click', this, e) === false){
5185             return;
5186         };
5187         
5188         if(this.tagtype == 'span'){
5189             return;
5190         }
5191         
5192         //Roo.log(this.href);
5193         var ael = this.el.select('a',true).first();
5194         //Roo.log(ael);
5195         
5196         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5197             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5198             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5199                 return; // ignore... - it's a 'hash' to another page.
5200             }
5201             Roo.log("NavItem - prevent Default?");
5202             e.preventDefault();
5203             this.scrollToElement(e);
5204         }
5205         
5206         
5207         var p =  this.parent();
5208    
5209         if (['tabs','pills'].indexOf(p.type)!==-1) {
5210             if (typeof(p.setActiveItem) !== 'undefined') {
5211                 p.setActiveItem(this);
5212             }
5213         }
5214         
5215         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5216         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5217             // remove the collapsed menu expand...
5218             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5219         }
5220     },
5221     
5222     isActive: function () {
5223         return this.active
5224     },
5225     setActive : function(state, fire, is_was_active)
5226     {
5227         if (this.active && !state && this.navId) {
5228             this.was_active = true;
5229             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5230             if (nv) {
5231                 nv.clearWasActive(this);
5232             }
5233             
5234         }
5235         this.active = state;
5236         
5237         if (!state ) {
5238             this.el.removeClass('active');
5239             this.navLink ? this.navLink.removeClass('active') : false;
5240         } else if (!this.el.hasClass('active')) {
5241             
5242             this.el.addClass('active');
5243             if (Roo.bootstrap.version == 4 && this.navLink ) {
5244                 this.navLink.addClass('active');
5245             }
5246             
5247         }
5248         if (fire) {
5249             this.fireEvent('changed', this, state);
5250         }
5251         
5252         // show a panel if it's registered and related..
5253         
5254         if (!this.navId || !this.tabId || !state || is_was_active) {
5255             return;
5256         }
5257         
5258         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5259         if (!tg) {
5260             return;
5261         }
5262         var pan = tg.getPanelByName(this.tabId);
5263         if (!pan) {
5264             return;
5265         }
5266         // if we can not flip to new panel - go back to old nav highlight..
5267         if (false == tg.showPanel(pan)) {
5268             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5269             if (nv) {
5270                 var onav = nv.getWasActive();
5271                 if (onav) {
5272                     onav.setActive(true, false, true);
5273                 }
5274             }
5275             
5276         }
5277         
5278         
5279         
5280     },
5281      // this should not be here...
5282     setDisabled : function(state)
5283     {
5284         this.disabled = state;
5285         if (!state ) {
5286             this.el.removeClass('disabled');
5287         } else if (!this.el.hasClass('disabled')) {
5288             this.el.addClass('disabled');
5289         }
5290         
5291     },
5292     
5293     /**
5294      * Fetch the element to display the tooltip on.
5295      * @return {Roo.Element} defaults to this.el
5296      */
5297     tooltipEl : function()
5298     {
5299         return this.el.select('' + this.tagtype + '', true).first();
5300     },
5301     
5302     scrollToElement : function(e)
5303     {
5304         var c = document.body;
5305         
5306         /*
5307          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5308          */
5309         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5310             c = document.documentElement;
5311         }
5312         
5313         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5314         
5315         if(!target){
5316             return;
5317         }
5318
5319         var o = target.calcOffsetsTo(c);
5320         
5321         var options = {
5322             target : target,
5323             value : o[1]
5324         };
5325         
5326         this.fireEvent('scrollto', this, options, e);
5327         
5328         Roo.get(c).scrollTo('top', options.value, true);
5329         
5330         return;
5331     }
5332 });
5333  
5334
5335  /*
5336  * - LGPL
5337  *
5338  * sidebar item
5339  *
5340  *  li
5341  *    <span> icon </span>
5342  *    <span> text </span>
5343  *    <span>badge </span>
5344  */
5345
5346 /**
5347  * @class Roo.bootstrap.NavSidebarItem
5348  * @extends Roo.bootstrap.NavItem
5349  * Bootstrap Navbar.NavSidebarItem class
5350  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5351  * {Boolean} open is the menu open
5352  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5353  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5354  * {String} buttonSize (sm|md|lg)the extra classes for the button
5355  * {Boolean} showArrow show arrow next to the text (default true)
5356  * @constructor
5357  * Create a new Navbar Button
5358  * @param {Object} config The config object
5359  */
5360 Roo.bootstrap.NavSidebarItem = function(config){
5361     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5362     this.addEvents({
5363         // raw events
5364         /**
5365          * @event click
5366          * The raw click event for the entire grid.
5367          * @param {Roo.EventObject} e
5368          */
5369         "click" : true,
5370          /**
5371             * @event changed
5372             * Fires when the active item active state changes
5373             * @param {Roo.bootstrap.NavSidebarItem} this
5374             * @param {boolean} state the new state
5375              
5376          */
5377         'changed': true
5378     });
5379    
5380 };
5381
5382 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5383     
5384     badgeWeight : 'default',
5385     
5386     open: false,
5387     
5388     buttonView : false,
5389     
5390     buttonWeight : 'default',
5391     
5392     buttonSize : 'md',
5393     
5394     showArrow : true,
5395     
5396     getAutoCreate : function(){
5397         
5398         
5399         var a = {
5400                 tag: 'a',
5401                 href : this.href || '#',
5402                 cls: '',
5403                 html : '',
5404                 cn : []
5405         };
5406         
5407         if(this.buttonView){
5408             a = {
5409                 tag: 'button',
5410                 href : this.href || '#',
5411                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5412                 html : this.html,
5413                 cn : []
5414             };
5415         }
5416         
5417         var cfg = {
5418             tag: 'li',
5419             cls: '',
5420             cn: [ a ]
5421         };
5422         
5423         if (this.active) {
5424             cfg.cls += ' active';
5425         }
5426         
5427         if (this.disabled) {
5428             cfg.cls += ' disabled';
5429         }
5430         if (this.open) {
5431             cfg.cls += ' open x-open';
5432         }
5433         // left icon..
5434         if (this.glyphicon || this.icon) {
5435             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5436             a.cn.push({ tag : 'i', cls : c }) ;
5437         }
5438         
5439         if(!this.buttonView){
5440             var span = {
5441                 tag: 'span',
5442                 html : this.html || ''
5443             };
5444
5445             a.cn.push(span);
5446             
5447         }
5448         
5449         if (this.badge !== '') {
5450             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5451         }
5452         
5453         if (this.menu) {
5454             
5455             if(this.showArrow){
5456                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5457             }
5458             
5459             a.cls += ' dropdown-toggle treeview' ;
5460         }
5461         
5462         return cfg;
5463     },
5464     
5465     initEvents : function()
5466     { 
5467         if (typeof (this.menu) != 'undefined') {
5468             this.menu.parentType = this.xtype;
5469             this.menu.triggerEl = this.el;
5470             this.menu = this.addxtype(Roo.apply({}, this.menu));
5471         }
5472         
5473         this.el.on('click', this.onClick, this);
5474         
5475         if(this.badge !== ''){
5476             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5477         }
5478         
5479     },
5480     
5481     onClick : function(e)
5482     {
5483         if(this.disabled){
5484             e.preventDefault();
5485             return;
5486         }
5487         
5488         if(this.preventDefault){
5489             e.preventDefault();
5490         }
5491         
5492         this.fireEvent('click', this, e);
5493     },
5494     
5495     disable : function()
5496     {
5497         this.setDisabled(true);
5498     },
5499     
5500     enable : function()
5501     {
5502         this.setDisabled(false);
5503     },
5504     
5505     setDisabled : function(state)
5506     {
5507         if(this.disabled == state){
5508             return;
5509         }
5510         
5511         this.disabled = state;
5512         
5513         if (state) {
5514             this.el.addClass('disabled');
5515             return;
5516         }
5517         
5518         this.el.removeClass('disabled');
5519         
5520         return;
5521     },
5522     
5523     setActive : function(state)
5524     {
5525         if(this.active == state){
5526             return;
5527         }
5528         
5529         this.active = state;
5530         
5531         if (state) {
5532             this.el.addClass('active');
5533             return;
5534         }
5535         
5536         this.el.removeClass('active');
5537         
5538         return;
5539     },
5540     
5541     isActive: function () 
5542     {
5543         return this.active;
5544     },
5545     
5546     setBadge : function(str)
5547     {
5548         if(!this.badgeEl){
5549             return;
5550         }
5551         
5552         this.badgeEl.dom.innerHTML = str;
5553     }
5554     
5555    
5556      
5557  
5558 });
5559  
5560
5561  /*
5562  * - LGPL
5563  *
5564  * row
5565  * 
5566  */
5567
5568 /**
5569  * @class Roo.bootstrap.Row
5570  * @extends Roo.bootstrap.Component
5571  * Bootstrap Row class (contains columns...)
5572  * 
5573  * @constructor
5574  * Create a new Row
5575  * @param {Object} config The config object
5576  */
5577
5578 Roo.bootstrap.Row = function(config){
5579     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5580 };
5581
5582 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5583     
5584     getAutoCreate : function(){
5585        return {
5586             cls: 'row clearfix'
5587        };
5588     }
5589     
5590     
5591 });
5592
5593  
5594
5595  /*
5596  * - LGPL
5597  *
5598  * element
5599  * 
5600  */
5601
5602 /**
5603  * @class Roo.bootstrap.Element
5604  * @extends Roo.bootstrap.Component
5605  * Bootstrap Element class
5606  * @cfg {String} html contents of the element
5607  * @cfg {String} tag tag of the element
5608  * @cfg {String} cls class of the element
5609  * @cfg {Boolean} preventDefault (true|false) default false
5610  * @cfg {Boolean} clickable (true|false) default false
5611  * 
5612  * @constructor
5613  * Create a new Element
5614  * @param {Object} config The config object
5615  */
5616
5617 Roo.bootstrap.Element = function(config){
5618     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5619     
5620     this.addEvents({
5621         // raw events
5622         /**
5623          * @event click
5624          * When a element is chick
5625          * @param {Roo.bootstrap.Element} this
5626          * @param {Roo.EventObject} e
5627          */
5628         "click" : true
5629     });
5630 };
5631
5632 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5633     
5634     tag: 'div',
5635     cls: '',
5636     html: '',
5637     preventDefault: false, 
5638     clickable: false,
5639     
5640     getAutoCreate : function(){
5641         
5642         var cfg = {
5643             tag: this.tag,
5644             // cls: this.cls, double assign in parent class Component.js :: onRender
5645             html: this.html
5646         };
5647         
5648         return cfg;
5649     },
5650     
5651     initEvents: function() 
5652     {
5653         Roo.bootstrap.Element.superclass.initEvents.call(this);
5654         
5655         if(this.clickable){
5656             this.el.on('click', this.onClick, this);
5657         }
5658         
5659     },
5660     
5661     onClick : function(e)
5662     {
5663         if(this.preventDefault){
5664             e.preventDefault();
5665         }
5666         
5667         this.fireEvent('click', this, e);
5668     },
5669     
5670     getValue : function()
5671     {
5672         return this.el.dom.innerHTML;
5673     },
5674     
5675     setValue : function(value)
5676     {
5677         this.el.dom.innerHTML = value;
5678     }
5679    
5680 });
5681
5682  
5683
5684  /*
5685  * - LGPL
5686  *
5687  * pagination
5688  * 
5689  */
5690
5691 /**
5692  * @class Roo.bootstrap.Pagination
5693  * @extends Roo.bootstrap.Component
5694  * Bootstrap Pagination class
5695  * @cfg {String} size xs | sm | md | lg
5696  * @cfg {Boolean} inverse false | true
5697  * 
5698  * @constructor
5699  * Create a new Pagination
5700  * @param {Object} config The config object
5701  */
5702
5703 Roo.bootstrap.Pagination = function(config){
5704     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5705 };
5706
5707 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5708     
5709     cls: false,
5710     size: false,
5711     inverse: false,
5712     
5713     getAutoCreate : function(){
5714         var cfg = {
5715             tag: 'ul',
5716                 cls: 'pagination'
5717         };
5718         if (this.inverse) {
5719             cfg.cls += ' inverse';
5720         }
5721         if (this.html) {
5722             cfg.html=this.html;
5723         }
5724         if (this.cls) {
5725             cfg.cls += " " + this.cls;
5726         }
5727         return cfg;
5728     }
5729    
5730 });
5731
5732  
5733
5734  /*
5735  * - LGPL
5736  *
5737  * Pagination item
5738  * 
5739  */
5740
5741
5742 /**
5743  * @class Roo.bootstrap.PaginationItem
5744  * @extends Roo.bootstrap.Component
5745  * Bootstrap PaginationItem class
5746  * @cfg {String} html text
5747  * @cfg {String} href the link
5748  * @cfg {Boolean} preventDefault (true | false) default true
5749  * @cfg {Boolean} active (true | false) default false
5750  * @cfg {Boolean} disabled default false
5751  * 
5752  * 
5753  * @constructor
5754  * Create a new PaginationItem
5755  * @param {Object} config The config object
5756  */
5757
5758
5759 Roo.bootstrap.PaginationItem = function(config){
5760     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5761     this.addEvents({
5762         // raw events
5763         /**
5764          * @event click
5765          * The raw click event for the entire grid.
5766          * @param {Roo.EventObject} e
5767          */
5768         "click" : true
5769     });
5770 };
5771
5772 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5773     
5774     href : false,
5775     html : false,
5776     preventDefault: true,
5777     active : false,
5778     cls : false,
5779     disabled: false,
5780     
5781     getAutoCreate : function(){
5782         var cfg= {
5783             tag: 'li',
5784             cn: [
5785                 {
5786                     tag : 'a',
5787                     href : this.href ? this.href : '#',
5788                     html : this.html ? this.html : ''
5789                 }
5790             ]
5791         };
5792         
5793         if(this.cls){
5794             cfg.cls = this.cls;
5795         }
5796         
5797         if(this.disabled){
5798             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5799         }
5800         
5801         if(this.active){
5802             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5803         }
5804         
5805         return cfg;
5806     },
5807     
5808     initEvents: function() {
5809         
5810         this.el.on('click', this.onClick, this);
5811         
5812     },
5813     onClick : function(e)
5814     {
5815         Roo.log('PaginationItem on click ');
5816         if(this.preventDefault){
5817             e.preventDefault();
5818         }
5819         
5820         if(this.disabled){
5821             return;
5822         }
5823         
5824         this.fireEvent('click', this, e);
5825     }
5826    
5827 });
5828
5829  
5830
5831  /*
5832  * - LGPL
5833  *
5834  * slider
5835  * 
5836  */
5837
5838
5839 /**
5840  * @class Roo.bootstrap.Slider
5841  * @extends Roo.bootstrap.Component
5842  * Bootstrap Slider class
5843  *    
5844  * @constructor
5845  * Create a new Slider
5846  * @param {Object} config The config object
5847  */
5848
5849 Roo.bootstrap.Slider = function(config){
5850     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5851 };
5852
5853 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5854     
5855     getAutoCreate : function(){
5856         
5857         var cfg = {
5858             tag: 'div',
5859             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5860             cn: [
5861                 {
5862                     tag: 'a',
5863                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5864                 }
5865             ]
5866         };
5867         
5868         return cfg;
5869     }
5870    
5871 });
5872
5873  /*
5874  * Based on:
5875  * Ext JS Library 1.1.1
5876  * Copyright(c) 2006-2007, Ext JS, LLC.
5877  *
5878  * Originally Released Under LGPL - original licence link has changed is not relivant.
5879  *
5880  * Fork - LGPL
5881  * <script type="text/javascript">
5882  */
5883  
5884
5885 /**
5886  * @class Roo.grid.ColumnModel
5887  * @extends Roo.util.Observable
5888  * This is the default implementation of a ColumnModel used by the Grid. It defines
5889  * the columns in the grid.
5890  * <br>Usage:<br>
5891  <pre><code>
5892  var colModel = new Roo.grid.ColumnModel([
5893         {header: "Ticker", width: 60, sortable: true, locked: true},
5894         {header: "Company Name", width: 150, sortable: true},
5895         {header: "Market Cap.", width: 100, sortable: true},
5896         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5897         {header: "Employees", width: 100, sortable: true, resizable: false}
5898  ]);
5899  </code></pre>
5900  * <p>
5901  
5902  * The config options listed for this class are options which may appear in each
5903  * individual column definition.
5904  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5905  * @constructor
5906  * @param {Object} config An Array of column config objects. See this class's
5907  * config objects for details.
5908 */
5909 Roo.grid.ColumnModel = function(config){
5910         /**
5911      * The config passed into the constructor
5912      */
5913     this.config = config;
5914     this.lookup = {};
5915
5916     // if no id, create one
5917     // if the column does not have a dataIndex mapping,
5918     // map it to the order it is in the config
5919     for(var i = 0, len = config.length; i < len; i++){
5920         var c = config[i];
5921         if(typeof c.dataIndex == "undefined"){
5922             c.dataIndex = i;
5923         }
5924         if(typeof c.renderer == "string"){
5925             c.renderer = Roo.util.Format[c.renderer];
5926         }
5927         if(typeof c.id == "undefined"){
5928             c.id = Roo.id();
5929         }
5930         if(c.editor && c.editor.xtype){
5931             c.editor  = Roo.factory(c.editor, Roo.grid);
5932         }
5933         if(c.editor && c.editor.isFormField){
5934             c.editor = new Roo.grid.GridEditor(c.editor);
5935         }
5936         this.lookup[c.id] = c;
5937     }
5938
5939     /**
5940      * The width of columns which have no width specified (defaults to 100)
5941      * @type Number
5942      */
5943     this.defaultWidth = 100;
5944
5945     /**
5946      * Default sortable of columns which have no sortable specified (defaults to false)
5947      * @type Boolean
5948      */
5949     this.defaultSortable = false;
5950
5951     this.addEvents({
5952         /**
5953              * @event widthchange
5954              * Fires when the width of a column changes.
5955              * @param {ColumnModel} this
5956              * @param {Number} columnIndex The column index
5957              * @param {Number} newWidth The new width
5958              */
5959             "widthchange": true,
5960         /**
5961              * @event headerchange
5962              * Fires when the text of a header changes.
5963              * @param {ColumnModel} this
5964              * @param {Number} columnIndex The column index
5965              * @param {Number} newText The new header text
5966              */
5967             "headerchange": true,
5968         /**
5969              * @event hiddenchange
5970              * Fires when a column is hidden or "unhidden".
5971              * @param {ColumnModel} this
5972              * @param {Number} columnIndex The column index
5973              * @param {Boolean} hidden true if hidden, false otherwise
5974              */
5975             "hiddenchange": true,
5976             /**
5977          * @event columnmoved
5978          * Fires when a column is moved.
5979          * @param {ColumnModel} this
5980          * @param {Number} oldIndex
5981          * @param {Number} newIndex
5982          */
5983         "columnmoved" : true,
5984         /**
5985          * @event columlockchange
5986          * Fires when a column's locked state is changed
5987          * @param {ColumnModel} this
5988          * @param {Number} colIndex
5989          * @param {Boolean} locked true if locked
5990          */
5991         "columnlockchange" : true
5992     });
5993     Roo.grid.ColumnModel.superclass.constructor.call(this);
5994 };
5995 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5996     /**
5997      * @cfg {String} header The header text to display in the Grid view.
5998      */
5999     /**
6000      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6001      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6002      * specified, the column's index is used as an index into the Record's data Array.
6003      */
6004     /**
6005      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6006      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6007      */
6008     /**
6009      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6010      * Defaults to the value of the {@link #defaultSortable} property.
6011      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6012      */
6013     /**
6014      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6015      */
6016     /**
6017      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6018      */
6019     /**
6020      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6021      */
6022     /**
6023      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6024      */
6025     /**
6026      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6027      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6028      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6029      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6030      */
6031        /**
6032      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6033      */
6034     /**
6035      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6036      */
6037     /**
6038      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6039      */
6040     /**
6041      * @cfg {String} cursor (Optional)
6042      */
6043     /**
6044      * @cfg {String} tooltip (Optional)
6045      */
6046     /**
6047      * @cfg {Number} xs (Optional)
6048      */
6049     /**
6050      * @cfg {Number} sm (Optional)
6051      */
6052     /**
6053      * @cfg {Number} md (Optional)
6054      */
6055     /**
6056      * @cfg {Number} lg (Optional)
6057      */
6058     /**
6059      * Returns the id of the column at the specified index.
6060      * @param {Number} index The column index
6061      * @return {String} the id
6062      */
6063     getColumnId : function(index){
6064         return this.config[index].id;
6065     },
6066
6067     /**
6068      * Returns the column for a specified id.
6069      * @param {String} id The column id
6070      * @return {Object} the column
6071      */
6072     getColumnById : function(id){
6073         return this.lookup[id];
6074     },
6075
6076     
6077     /**
6078      * Returns the column for a specified dataIndex.
6079      * @param {String} dataIndex The column dataIndex
6080      * @return {Object|Boolean} the column or false if not found
6081      */
6082     getColumnByDataIndex: function(dataIndex){
6083         var index = this.findColumnIndex(dataIndex);
6084         return index > -1 ? this.config[index] : false;
6085     },
6086     
6087     /**
6088      * Returns the index for a specified column id.
6089      * @param {String} id The column id
6090      * @return {Number} the index, or -1 if not found
6091      */
6092     getIndexById : function(id){
6093         for(var i = 0, len = this.config.length; i < len; i++){
6094             if(this.config[i].id == id){
6095                 return i;
6096             }
6097         }
6098         return -1;
6099     },
6100     
6101     /**
6102      * Returns the index for a specified column dataIndex.
6103      * @param {String} dataIndex The column dataIndex
6104      * @return {Number} the index, or -1 if not found
6105      */
6106     
6107     findColumnIndex : function(dataIndex){
6108         for(var i = 0, len = this.config.length; i < len; i++){
6109             if(this.config[i].dataIndex == dataIndex){
6110                 return i;
6111             }
6112         }
6113         return -1;
6114     },
6115     
6116     
6117     moveColumn : function(oldIndex, newIndex){
6118         var c = this.config[oldIndex];
6119         this.config.splice(oldIndex, 1);
6120         this.config.splice(newIndex, 0, c);
6121         this.dataMap = null;
6122         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6123     },
6124
6125     isLocked : function(colIndex){
6126         return this.config[colIndex].locked === true;
6127     },
6128
6129     setLocked : function(colIndex, value, suppressEvent){
6130         if(this.isLocked(colIndex) == value){
6131             return;
6132         }
6133         this.config[colIndex].locked = value;
6134         if(!suppressEvent){
6135             this.fireEvent("columnlockchange", this, colIndex, value);
6136         }
6137     },
6138
6139     getTotalLockedWidth : function(){
6140         var totalWidth = 0;
6141         for(var i = 0; i < this.config.length; i++){
6142             if(this.isLocked(i) && !this.isHidden(i)){
6143                 this.totalWidth += this.getColumnWidth(i);
6144             }
6145         }
6146         return totalWidth;
6147     },
6148
6149     getLockedCount : function(){
6150         for(var i = 0, len = this.config.length; i < len; i++){
6151             if(!this.isLocked(i)){
6152                 return i;
6153             }
6154         }
6155         
6156         return this.config.length;
6157     },
6158
6159     /**
6160      * Returns the number of columns.
6161      * @return {Number}
6162      */
6163     getColumnCount : function(visibleOnly){
6164         if(visibleOnly === true){
6165             var c = 0;
6166             for(var i = 0, len = this.config.length; i < len; i++){
6167                 if(!this.isHidden(i)){
6168                     c++;
6169                 }
6170             }
6171             return c;
6172         }
6173         return this.config.length;
6174     },
6175
6176     /**
6177      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6178      * @param {Function} fn
6179      * @param {Object} scope (optional)
6180      * @return {Array} result
6181      */
6182     getColumnsBy : function(fn, scope){
6183         var r = [];
6184         for(var i = 0, len = this.config.length; i < len; i++){
6185             var c = this.config[i];
6186             if(fn.call(scope||this, c, i) === true){
6187                 r[r.length] = c;
6188             }
6189         }
6190         return r;
6191     },
6192
6193     /**
6194      * Returns true if the specified column is sortable.
6195      * @param {Number} col The column index
6196      * @return {Boolean}
6197      */
6198     isSortable : function(col){
6199         if(typeof this.config[col].sortable == "undefined"){
6200             return this.defaultSortable;
6201         }
6202         return this.config[col].sortable;
6203     },
6204
6205     /**
6206      * Returns the rendering (formatting) function defined for the column.
6207      * @param {Number} col The column index.
6208      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6209      */
6210     getRenderer : function(col){
6211         if(!this.config[col].renderer){
6212             return Roo.grid.ColumnModel.defaultRenderer;
6213         }
6214         return this.config[col].renderer;
6215     },
6216
6217     /**
6218      * Sets the rendering (formatting) function for a column.
6219      * @param {Number} col The column index
6220      * @param {Function} fn The function to use to process the cell's raw data
6221      * to return HTML markup for the grid view. The render function is called with
6222      * the following parameters:<ul>
6223      * <li>Data value.</li>
6224      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6225      * <li>css A CSS style string to apply to the table cell.</li>
6226      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6227      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6228      * <li>Row index</li>
6229      * <li>Column index</li>
6230      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6231      */
6232     setRenderer : function(col, fn){
6233         this.config[col].renderer = fn;
6234     },
6235
6236     /**
6237      * Returns the width for the specified column.
6238      * @param {Number} col The column index
6239      * @return {Number}
6240      */
6241     getColumnWidth : function(col){
6242         return this.config[col].width * 1 || this.defaultWidth;
6243     },
6244
6245     /**
6246      * Sets the width for a column.
6247      * @param {Number} col The column index
6248      * @param {Number} width The new width
6249      */
6250     setColumnWidth : function(col, width, suppressEvent){
6251         this.config[col].width = width;
6252         this.totalWidth = null;
6253         if(!suppressEvent){
6254              this.fireEvent("widthchange", this, col, width);
6255         }
6256     },
6257
6258     /**
6259      * Returns the total width of all columns.
6260      * @param {Boolean} includeHidden True to include hidden column widths
6261      * @return {Number}
6262      */
6263     getTotalWidth : function(includeHidden){
6264         if(!this.totalWidth){
6265             this.totalWidth = 0;
6266             for(var i = 0, len = this.config.length; i < len; i++){
6267                 if(includeHidden || !this.isHidden(i)){
6268                     this.totalWidth += this.getColumnWidth(i);
6269                 }
6270             }
6271         }
6272         return this.totalWidth;
6273     },
6274
6275     /**
6276      * Returns the header for the specified column.
6277      * @param {Number} col The column index
6278      * @return {String}
6279      */
6280     getColumnHeader : function(col){
6281         return this.config[col].header;
6282     },
6283
6284     /**
6285      * Sets the header for a column.
6286      * @param {Number} col The column index
6287      * @param {String} header The new header
6288      */
6289     setColumnHeader : function(col, header){
6290         this.config[col].header = header;
6291         this.fireEvent("headerchange", this, col, header);
6292     },
6293
6294     /**
6295      * Returns the tooltip for the specified column.
6296      * @param {Number} col The column index
6297      * @return {String}
6298      */
6299     getColumnTooltip : function(col){
6300             return this.config[col].tooltip;
6301     },
6302     /**
6303      * Sets the tooltip for a column.
6304      * @param {Number} col The column index
6305      * @param {String} tooltip The new tooltip
6306      */
6307     setColumnTooltip : function(col, tooltip){
6308             this.config[col].tooltip = tooltip;
6309     },
6310
6311     /**
6312      * Returns the dataIndex for the specified column.
6313      * @param {Number} col The column index
6314      * @return {Number}
6315      */
6316     getDataIndex : function(col){
6317         return this.config[col].dataIndex;
6318     },
6319
6320     /**
6321      * Sets the dataIndex for a column.
6322      * @param {Number} col The column index
6323      * @param {Number} dataIndex The new dataIndex
6324      */
6325     setDataIndex : function(col, dataIndex){
6326         this.config[col].dataIndex = dataIndex;
6327     },
6328
6329     
6330     
6331     /**
6332      * Returns true if the cell is editable.
6333      * @param {Number} colIndex The column index
6334      * @param {Number} rowIndex The row index - this is nto actually used..?
6335      * @return {Boolean}
6336      */
6337     isCellEditable : function(colIndex, rowIndex){
6338         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6339     },
6340
6341     /**
6342      * Returns the editor defined for the cell/column.
6343      * return false or null to disable editing.
6344      * @param {Number} colIndex The column index
6345      * @param {Number} rowIndex The row index
6346      * @return {Object}
6347      */
6348     getCellEditor : function(colIndex, rowIndex){
6349         return this.config[colIndex].editor;
6350     },
6351
6352     /**
6353      * Sets if a column is editable.
6354      * @param {Number} col The column index
6355      * @param {Boolean} editable True if the column is editable
6356      */
6357     setEditable : function(col, editable){
6358         this.config[col].editable = editable;
6359     },
6360
6361
6362     /**
6363      * Returns true if the column is hidden.
6364      * @param {Number} colIndex The column index
6365      * @return {Boolean}
6366      */
6367     isHidden : function(colIndex){
6368         return this.config[colIndex].hidden;
6369     },
6370
6371
6372     /**
6373      * Returns true if the column width cannot be changed
6374      */
6375     isFixed : function(colIndex){
6376         return this.config[colIndex].fixed;
6377     },
6378
6379     /**
6380      * Returns true if the column can be resized
6381      * @return {Boolean}
6382      */
6383     isResizable : function(colIndex){
6384         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6385     },
6386     /**
6387      * Sets if a column is hidden.
6388      * @param {Number} colIndex The column index
6389      * @param {Boolean} hidden True if the column is hidden
6390      */
6391     setHidden : function(colIndex, hidden){
6392         this.config[colIndex].hidden = hidden;
6393         this.totalWidth = null;
6394         this.fireEvent("hiddenchange", this, colIndex, hidden);
6395     },
6396
6397     /**
6398      * Sets the editor for a column.
6399      * @param {Number} col The column index
6400      * @param {Object} editor The editor object
6401      */
6402     setEditor : function(col, editor){
6403         this.config[col].editor = editor;
6404     }
6405 });
6406
6407 Roo.grid.ColumnModel.defaultRenderer = function(value)
6408 {
6409     if(typeof value == "object") {
6410         return value;
6411     }
6412         if(typeof value == "string" && value.length < 1){
6413             return "&#160;";
6414         }
6415     
6416         return String.format("{0}", value);
6417 };
6418
6419 // Alias for backwards compatibility
6420 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6421 /*
6422  * Based on:
6423  * Ext JS Library 1.1.1
6424  * Copyright(c) 2006-2007, Ext JS, LLC.
6425  *
6426  * Originally Released Under LGPL - original licence link has changed is not relivant.
6427  *
6428  * Fork - LGPL
6429  * <script type="text/javascript">
6430  */
6431  
6432 /**
6433  * @class Roo.LoadMask
6434  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6435  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6436  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6437  * element's UpdateManager load indicator and will be destroyed after the initial load.
6438  * @constructor
6439  * Create a new LoadMask
6440  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6441  * @param {Object} config The config object
6442  */
6443 Roo.LoadMask = function(el, config){
6444     this.el = Roo.get(el);
6445     Roo.apply(this, config);
6446     if(this.store){
6447         this.store.on('beforeload', this.onBeforeLoad, this);
6448         this.store.on('load', this.onLoad, this);
6449         this.store.on('loadexception', this.onLoadException, this);
6450         this.removeMask = false;
6451     }else{
6452         var um = this.el.getUpdateManager();
6453         um.showLoadIndicator = false; // disable the default indicator
6454         um.on('beforeupdate', this.onBeforeLoad, this);
6455         um.on('update', this.onLoad, this);
6456         um.on('failure', this.onLoad, this);
6457         this.removeMask = true;
6458     }
6459 };
6460
6461 Roo.LoadMask.prototype = {
6462     /**
6463      * @cfg {Boolean} removeMask
6464      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6465      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6466      */
6467     /**
6468      * @cfg {String} msg
6469      * The text to display in a centered loading message box (defaults to 'Loading...')
6470      */
6471     msg : 'Loading...',
6472     /**
6473      * @cfg {String} msgCls
6474      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6475      */
6476     msgCls : 'x-mask-loading',
6477
6478     /**
6479      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6480      * @type Boolean
6481      */
6482     disabled: false,
6483
6484     /**
6485      * Disables the mask to prevent it from being displayed
6486      */
6487     disable : function(){
6488        this.disabled = true;
6489     },
6490
6491     /**
6492      * Enables the mask so that it can be displayed
6493      */
6494     enable : function(){
6495         this.disabled = false;
6496     },
6497     
6498     onLoadException : function()
6499     {
6500         Roo.log(arguments);
6501         
6502         if (typeof(arguments[3]) != 'undefined') {
6503             Roo.MessageBox.alert("Error loading",arguments[3]);
6504         } 
6505         /*
6506         try {
6507             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6508                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6509             }   
6510         } catch(e) {
6511             
6512         }
6513         */
6514     
6515         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6516     },
6517     // private
6518     onLoad : function()
6519     {
6520         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6521     },
6522
6523     // private
6524     onBeforeLoad : function(){
6525         if(!this.disabled){
6526             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6527         }
6528     },
6529
6530     // private
6531     destroy : function(){
6532         if(this.store){
6533             this.store.un('beforeload', this.onBeforeLoad, this);
6534             this.store.un('load', this.onLoad, this);
6535             this.store.un('loadexception', this.onLoadException, this);
6536         }else{
6537             var um = this.el.getUpdateManager();
6538             um.un('beforeupdate', this.onBeforeLoad, this);
6539             um.un('update', this.onLoad, this);
6540             um.un('failure', this.onLoad, this);
6541         }
6542     }
6543 };/*
6544  * - LGPL
6545  *
6546  * table
6547  * 
6548  */
6549
6550 /**
6551  * @class Roo.bootstrap.Table
6552  * @extends Roo.bootstrap.Component
6553  * Bootstrap Table class
6554  * @cfg {String} cls table class
6555  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6556  * @cfg {String} bgcolor Specifies the background color for a table
6557  * @cfg {Number} border Specifies whether the table cells should have borders or not
6558  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6559  * @cfg {Number} cellspacing Specifies the space between cells
6560  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6561  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6562  * @cfg {String} sortable Specifies that the table should be sortable
6563  * @cfg {String} summary Specifies a summary of the content of a table
6564  * @cfg {Number} width Specifies the width of a table
6565  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6566  * 
6567  * @cfg {boolean} striped Should the rows be alternative striped
6568  * @cfg {boolean} bordered Add borders to the table
6569  * @cfg {boolean} hover Add hover highlighting
6570  * @cfg {boolean} condensed Format condensed
6571  * @cfg {boolean} responsive Format condensed
6572  * @cfg {Boolean} loadMask (true|false) default false
6573  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6574  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6575  * @cfg {Boolean} rowSelection (true|false) default false
6576  * @cfg {Boolean} cellSelection (true|false) default false
6577  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6578  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6579  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6580  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6581  
6582  * 
6583  * @constructor
6584  * Create a new Table
6585  * @param {Object} config The config object
6586  */
6587
6588 Roo.bootstrap.Table = function(config){
6589     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6590     
6591   
6592     
6593     // BC...
6594     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6595     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6596     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6597     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6598     
6599     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6600     if (this.sm) {
6601         this.sm.grid = this;
6602         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6603         this.sm = this.selModel;
6604         this.sm.xmodule = this.xmodule || false;
6605     }
6606     
6607     if (this.cm && typeof(this.cm.config) == 'undefined') {
6608         this.colModel = new Roo.grid.ColumnModel(this.cm);
6609         this.cm = this.colModel;
6610         this.cm.xmodule = this.xmodule || false;
6611     }
6612     if (this.store) {
6613         this.store= Roo.factory(this.store, Roo.data);
6614         this.ds = this.store;
6615         this.ds.xmodule = this.xmodule || false;
6616          
6617     }
6618     if (this.footer && this.store) {
6619         this.footer.dataSource = this.ds;
6620         this.footer = Roo.factory(this.footer);
6621     }
6622     
6623     /** @private */
6624     this.addEvents({
6625         /**
6626          * @event cellclick
6627          * Fires when a cell is clicked
6628          * @param {Roo.bootstrap.Table} this
6629          * @param {Roo.Element} el
6630          * @param {Number} rowIndex
6631          * @param {Number} columnIndex
6632          * @param {Roo.EventObject} e
6633          */
6634         "cellclick" : true,
6635         /**
6636          * @event celldblclick
6637          * Fires when a cell is double clicked
6638          * @param {Roo.bootstrap.Table} this
6639          * @param {Roo.Element} el
6640          * @param {Number} rowIndex
6641          * @param {Number} columnIndex
6642          * @param {Roo.EventObject} e
6643          */
6644         "celldblclick" : true,
6645         /**
6646          * @event rowclick
6647          * Fires when a row is clicked
6648          * @param {Roo.bootstrap.Table} this
6649          * @param {Roo.Element} el
6650          * @param {Number} rowIndex
6651          * @param {Roo.EventObject} e
6652          */
6653         "rowclick" : true,
6654         /**
6655          * @event rowdblclick
6656          * Fires when a row is double clicked
6657          * @param {Roo.bootstrap.Table} this
6658          * @param {Roo.Element} el
6659          * @param {Number} rowIndex
6660          * @param {Roo.EventObject} e
6661          */
6662         "rowdblclick" : true,
6663         /**
6664          * @event mouseover
6665          * Fires when a mouseover occur
6666          * @param {Roo.bootstrap.Table} this
6667          * @param {Roo.Element} el
6668          * @param {Number} rowIndex
6669          * @param {Number} columnIndex
6670          * @param {Roo.EventObject} e
6671          */
6672         "mouseover" : true,
6673         /**
6674          * @event mouseout
6675          * Fires when a mouseout occur
6676          * @param {Roo.bootstrap.Table} this
6677          * @param {Roo.Element} el
6678          * @param {Number} rowIndex
6679          * @param {Number} columnIndex
6680          * @param {Roo.EventObject} e
6681          */
6682         "mouseout" : true,
6683         /**
6684          * @event rowclass
6685          * Fires when a row is rendered, so you can change add a style to it.
6686          * @param {Roo.bootstrap.Table} this
6687          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6688          */
6689         'rowclass' : true,
6690           /**
6691          * @event rowsrendered
6692          * Fires when all the  rows have been rendered
6693          * @param {Roo.bootstrap.Table} this
6694          */
6695         'rowsrendered' : true,
6696         /**
6697          * @event contextmenu
6698          * The raw contextmenu event for the entire grid.
6699          * @param {Roo.EventObject} e
6700          */
6701         "contextmenu" : true,
6702         /**
6703          * @event rowcontextmenu
6704          * Fires when a row is right clicked
6705          * @param {Roo.bootstrap.Table} this
6706          * @param {Number} rowIndex
6707          * @param {Roo.EventObject} e
6708          */
6709         "rowcontextmenu" : true,
6710         /**
6711          * @event cellcontextmenu
6712          * Fires when a cell is right clicked
6713          * @param {Roo.bootstrap.Table} this
6714          * @param {Number} rowIndex
6715          * @param {Number} cellIndex
6716          * @param {Roo.EventObject} e
6717          */
6718          "cellcontextmenu" : true,
6719          /**
6720          * @event headercontextmenu
6721          * Fires when a header is right clicked
6722          * @param {Roo.bootstrap.Table} this
6723          * @param {Number} columnIndex
6724          * @param {Roo.EventObject} e
6725          */
6726         "headercontextmenu" : true
6727     });
6728 };
6729
6730 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6731     
6732     cls: false,
6733     align: false,
6734     bgcolor: false,
6735     border: false,
6736     cellpadding: false,
6737     cellspacing: false,
6738     frame: false,
6739     rules: false,
6740     sortable: false,
6741     summary: false,
6742     width: false,
6743     striped : false,
6744     scrollBody : false,
6745     bordered: false,
6746     hover:  false,
6747     condensed : false,
6748     responsive : false,
6749     sm : false,
6750     cm : false,
6751     store : false,
6752     loadMask : false,
6753     footerShow : true,
6754     headerShow : true,
6755   
6756     rowSelection : false,
6757     cellSelection : false,
6758     layout : false,
6759     
6760     // Roo.Element - the tbody
6761     mainBody: false,
6762     // Roo.Element - thead element
6763     mainHead: false,
6764     
6765     container: false, // used by gridpanel...
6766     
6767     lazyLoad : false,
6768     
6769     CSS : Roo.util.CSS,
6770     
6771     auto_hide_footer : false,
6772     
6773     getAutoCreate : function()
6774     {
6775         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6776         
6777         cfg = {
6778             tag: 'table',
6779             cls : 'table',
6780             cn : []
6781         };
6782         if (this.scrollBody) {
6783             cfg.cls += ' table-body-fixed';
6784         }    
6785         if (this.striped) {
6786             cfg.cls += ' table-striped';
6787         }
6788         
6789         if (this.hover) {
6790             cfg.cls += ' table-hover';
6791         }
6792         if (this.bordered) {
6793             cfg.cls += ' table-bordered';
6794         }
6795         if (this.condensed) {
6796             cfg.cls += ' table-condensed';
6797         }
6798         if (this.responsive) {
6799             cfg.cls += ' table-responsive';
6800         }
6801         
6802         if (this.cls) {
6803             cfg.cls+=  ' ' +this.cls;
6804         }
6805         
6806         // this lot should be simplifed...
6807         var _t = this;
6808         var cp = [
6809             'align',
6810             'bgcolor',
6811             'border',
6812             'cellpadding',
6813             'cellspacing',
6814             'frame',
6815             'rules',
6816             'sortable',
6817             'summary',
6818             'width'
6819         ].forEach(function(k) {
6820             if (_t[k]) {
6821                 cfg[k] = _t[k];
6822             }
6823         });
6824         
6825         
6826         if (this.layout) {
6827             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6828         }
6829         
6830         if(this.store || this.cm){
6831             if(this.headerShow){
6832                 cfg.cn.push(this.renderHeader());
6833             }
6834             
6835             cfg.cn.push(this.renderBody());
6836             
6837             if(this.footerShow){
6838                 cfg.cn.push(this.renderFooter());
6839             }
6840             // where does this come from?
6841             //cfg.cls+=  ' TableGrid';
6842         }
6843         
6844         return { cn : [ cfg ] };
6845     },
6846     
6847     initEvents : function()
6848     {   
6849         if(!this.store || !this.cm){
6850             return;
6851         }
6852         if (this.selModel) {
6853             this.selModel.initEvents();
6854         }
6855         
6856         
6857         //Roo.log('initEvents with ds!!!!');
6858         
6859         this.mainBody = this.el.select('tbody', true).first();
6860         this.mainHead = this.el.select('thead', true).first();
6861         this.mainFoot = this.el.select('tfoot', true).first();
6862         
6863         
6864         
6865         var _this = this;
6866         
6867         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6868             e.on('click', _this.sort, _this);
6869         });
6870         
6871         this.mainBody.on("click", this.onClick, this);
6872         this.mainBody.on("dblclick", this.onDblClick, this);
6873         
6874         // why is this done????? = it breaks dialogs??
6875         //this.parent().el.setStyle('position', 'relative');
6876         
6877         
6878         if (this.footer) {
6879             this.footer.parentId = this.id;
6880             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6881             
6882             if(this.lazyLoad){
6883                 this.el.select('tfoot tr td').first().addClass('hide');
6884             }
6885         } 
6886         
6887         if(this.loadMask) {
6888             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6889         }
6890         
6891         this.store.on('load', this.onLoad, this);
6892         this.store.on('beforeload', this.onBeforeLoad, this);
6893         this.store.on('update', this.onUpdate, this);
6894         this.store.on('add', this.onAdd, this);
6895         this.store.on("clear", this.clear, this);
6896         
6897         this.el.on("contextmenu", this.onContextMenu, this);
6898         
6899         this.mainBody.on('scroll', this.onBodyScroll, this);
6900         
6901         this.cm.on("headerchange", this.onHeaderChange, this);
6902         
6903         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6904         
6905     },
6906     
6907     onContextMenu : function(e, t)
6908     {
6909         this.processEvent("contextmenu", e);
6910     },
6911     
6912     processEvent : function(name, e)
6913     {
6914         if (name != 'touchstart' ) {
6915             this.fireEvent(name, e);    
6916         }
6917         
6918         var t = e.getTarget();
6919         
6920         var cell = Roo.get(t);
6921         
6922         if(!cell){
6923             return;
6924         }
6925         
6926         if(cell.findParent('tfoot', false, true)){
6927             return;
6928         }
6929         
6930         if(cell.findParent('thead', false, true)){
6931             
6932             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6933                 cell = Roo.get(t).findParent('th', false, true);
6934                 if (!cell) {
6935                     Roo.log("failed to find th in thead?");
6936                     Roo.log(e.getTarget());
6937                     return;
6938                 }
6939             }
6940             
6941             var cellIndex = cell.dom.cellIndex;
6942             
6943             var ename = name == 'touchstart' ? 'click' : name;
6944             this.fireEvent("header" + ename, this, cellIndex, e);
6945             
6946             return;
6947         }
6948         
6949         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6950             cell = Roo.get(t).findParent('td', false, true);
6951             if (!cell) {
6952                 Roo.log("failed to find th in tbody?");
6953                 Roo.log(e.getTarget());
6954                 return;
6955             }
6956         }
6957         
6958         var row = cell.findParent('tr', false, true);
6959         var cellIndex = cell.dom.cellIndex;
6960         var rowIndex = row.dom.rowIndex - 1;
6961         
6962         if(row !== false){
6963             
6964             this.fireEvent("row" + name, this, rowIndex, e);
6965             
6966             if(cell !== false){
6967             
6968                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6969             }
6970         }
6971         
6972     },
6973     
6974     onMouseover : function(e, el)
6975     {
6976         var cell = Roo.get(el);
6977         
6978         if(!cell){
6979             return;
6980         }
6981         
6982         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6983             cell = cell.findParent('td', false, true);
6984         }
6985         
6986         var row = cell.findParent('tr', false, true);
6987         var cellIndex = cell.dom.cellIndex;
6988         var rowIndex = row.dom.rowIndex - 1; // start from 0
6989         
6990         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6991         
6992     },
6993     
6994     onMouseout : function(e, el)
6995     {
6996         var cell = Roo.get(el);
6997         
6998         if(!cell){
6999             return;
7000         }
7001         
7002         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7003             cell = cell.findParent('td', false, true);
7004         }
7005         
7006         var row = cell.findParent('tr', false, true);
7007         var cellIndex = cell.dom.cellIndex;
7008         var rowIndex = row.dom.rowIndex - 1; // start from 0
7009         
7010         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7011         
7012     },
7013     
7014     onClick : function(e, el)
7015     {
7016         var cell = Roo.get(el);
7017         
7018         if(!cell || (!this.cellSelection && !this.rowSelection)){
7019             return;
7020         }
7021         
7022         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7023             cell = cell.findParent('td', false, true);
7024         }
7025         
7026         if(!cell || typeof(cell) == 'undefined'){
7027             return;
7028         }
7029         
7030         var row = cell.findParent('tr', false, true);
7031         
7032         if(!row || typeof(row) == 'undefined'){
7033             return;
7034         }
7035         
7036         var cellIndex = cell.dom.cellIndex;
7037         var rowIndex = this.getRowIndex(row);
7038         
7039         // why??? - should these not be based on SelectionModel?
7040         if(this.cellSelection){
7041             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7042         }
7043         
7044         if(this.rowSelection){
7045             this.fireEvent('rowclick', this, row, rowIndex, e);
7046         }
7047         
7048         
7049     },
7050         
7051     onDblClick : function(e,el)
7052     {
7053         var cell = Roo.get(el);
7054         
7055         if(!cell || (!this.cellSelection && !this.rowSelection)){
7056             return;
7057         }
7058         
7059         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7060             cell = cell.findParent('td', false, true);
7061         }
7062         
7063         if(!cell || typeof(cell) == 'undefined'){
7064             return;
7065         }
7066         
7067         var row = cell.findParent('tr', false, true);
7068         
7069         if(!row || typeof(row) == 'undefined'){
7070             return;
7071         }
7072         
7073         var cellIndex = cell.dom.cellIndex;
7074         var rowIndex = this.getRowIndex(row);
7075         
7076         if(this.cellSelection){
7077             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7078         }
7079         
7080         if(this.rowSelection){
7081             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7082         }
7083     },
7084     
7085     sort : function(e,el)
7086     {
7087         var col = Roo.get(el);
7088         
7089         if(!col.hasClass('sortable')){
7090             return;
7091         }
7092         
7093         var sort = col.attr('sort');
7094         var dir = 'ASC';
7095         
7096         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7097             dir = 'DESC';
7098         }
7099         
7100         this.store.sortInfo = {field : sort, direction : dir};
7101         
7102         if (this.footer) {
7103             Roo.log("calling footer first");
7104             this.footer.onClick('first');
7105         } else {
7106         
7107             this.store.load({ params : { start : 0 } });
7108         }
7109     },
7110     
7111     renderHeader : function()
7112     {
7113         var header = {
7114             tag: 'thead',
7115             cn : []
7116         };
7117         
7118         var cm = this.cm;
7119         this.totalWidth = 0;
7120         
7121         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7122             
7123             var config = cm.config[i];
7124             
7125             var c = {
7126                 tag: 'th',
7127                 cls : 'x-hcol-' + i,
7128                 style : '',
7129                 html: cm.getColumnHeader(i)
7130             };
7131             
7132             var hh = '';
7133             
7134             if(typeof(config.sortable) != 'undefined' && config.sortable){
7135                 c.cls = 'sortable';
7136                 c.html = '<i class="glyphicon"></i>' + c.html;
7137             }
7138             
7139             // could use BS4 hidden-..-down 
7140             
7141             if(typeof(config.lgHeader) != 'undefined'){
7142                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7143             }
7144             
7145             if(typeof(config.mdHeader) != 'undefined'){
7146                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7147             }
7148             
7149             if(typeof(config.smHeader) != 'undefined'){
7150                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7151             }
7152             
7153             if(typeof(config.xsHeader) != 'undefined'){
7154                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7155             }
7156             
7157             if(hh.length){
7158                 c.html = hh;
7159             }
7160             
7161             if(typeof(config.tooltip) != 'undefined'){
7162                 c.tooltip = config.tooltip;
7163             }
7164             
7165             if(typeof(config.colspan) != 'undefined'){
7166                 c.colspan = config.colspan;
7167             }
7168             
7169             if(typeof(config.hidden) != 'undefined' && config.hidden){
7170                 c.style += ' display:none;';
7171             }
7172             
7173             if(typeof(config.dataIndex) != 'undefined'){
7174                 c.sort = config.dataIndex;
7175             }
7176             
7177            
7178             
7179             if(typeof(config.align) != 'undefined' && config.align.length){
7180                 c.style += ' text-align:' + config.align + ';';
7181             }
7182             
7183             if(typeof(config.width) != 'undefined'){
7184                 c.style += ' width:' + config.width + 'px;';
7185                 this.totalWidth += config.width;
7186             } else {
7187                 this.totalWidth += 100; // assume minimum of 100 per column?
7188             }
7189             
7190             if(typeof(config.cls) != 'undefined'){
7191                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7192             }
7193             
7194             ['xs','sm','md','lg'].map(function(size){
7195                 
7196                 if(typeof(config[size]) == 'undefined'){
7197                     return;
7198                 }
7199                  
7200                 if (!config[size]) { // 0 = hidden
7201                     // BS 4 '0' is treated as hide that column and below.
7202                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7203                     return;
7204                 }
7205                 
7206                 c.cls += ' col-' + size + '-' + config[size] + (
7207                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7208                 );
7209                 
7210                 
7211             });
7212             
7213             header.cn.push(c)
7214         }
7215         
7216         return header;
7217     },
7218     
7219     renderBody : function()
7220     {
7221         var body = {
7222             tag: 'tbody',
7223             cn : [
7224                 {
7225                     tag: 'tr',
7226                     cn : [
7227                         {
7228                             tag : 'td',
7229                             colspan :  this.cm.getColumnCount()
7230                         }
7231                     ]
7232                 }
7233             ]
7234         };
7235         
7236         return body;
7237     },
7238     
7239     renderFooter : function()
7240     {
7241         var footer = {
7242             tag: 'tfoot',
7243             cn : [
7244                 {
7245                     tag: 'tr',
7246                     cn : [
7247                         {
7248                             tag : 'td',
7249                             colspan :  this.cm.getColumnCount()
7250                         }
7251                     ]
7252                 }
7253             ]
7254         };
7255         
7256         return footer;
7257     },
7258     
7259     
7260     
7261     onLoad : function()
7262     {
7263 //        Roo.log('ds onload');
7264         this.clear();
7265         
7266         var _this = this;
7267         var cm = this.cm;
7268         var ds = this.store;
7269         
7270         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7271             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7272             if (_this.store.sortInfo) {
7273                     
7274                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7275                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7276                 }
7277                 
7278                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7279                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7280                 }
7281             }
7282         });
7283         
7284         var tbody =  this.mainBody;
7285               
7286         if(ds.getCount() > 0){
7287             ds.data.each(function(d,rowIndex){
7288                 var row =  this.renderRow(cm, ds, rowIndex);
7289                 
7290                 tbody.createChild(row);
7291                 
7292                 var _this = this;
7293                 
7294                 if(row.cellObjects.length){
7295                     Roo.each(row.cellObjects, function(r){
7296                         _this.renderCellObject(r);
7297                     })
7298                 }
7299                 
7300             }, this);
7301         }
7302         
7303         var tfoot = this.el.select('tfoot', true).first();
7304         
7305         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7306             
7307             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7308             
7309             var total = this.ds.getTotalCount();
7310             
7311             if(this.footer.pageSize < total){
7312                 this.mainFoot.show();
7313             }
7314         }
7315         
7316         Roo.each(this.el.select('tbody td', true).elements, function(e){
7317             e.on('mouseover', _this.onMouseover, _this);
7318         });
7319         
7320         Roo.each(this.el.select('tbody td', true).elements, function(e){
7321             e.on('mouseout', _this.onMouseout, _this);
7322         });
7323         this.fireEvent('rowsrendered', this);
7324         
7325         this.autoSize();
7326     },
7327     
7328     
7329     onUpdate : function(ds,record)
7330     {
7331         this.refreshRow(record);
7332         this.autoSize();
7333     },
7334     
7335     onRemove : function(ds, record, index, isUpdate){
7336         if(isUpdate !== true){
7337             this.fireEvent("beforerowremoved", this, index, record);
7338         }
7339         var bt = this.mainBody.dom;
7340         
7341         var rows = this.el.select('tbody > tr', true).elements;
7342         
7343         if(typeof(rows[index]) != 'undefined'){
7344             bt.removeChild(rows[index].dom);
7345         }
7346         
7347 //        if(bt.rows[index]){
7348 //            bt.removeChild(bt.rows[index]);
7349 //        }
7350         
7351         if(isUpdate !== true){
7352             //this.stripeRows(index);
7353             //this.syncRowHeights(index, index);
7354             //this.layout();
7355             this.fireEvent("rowremoved", this, index, record);
7356         }
7357     },
7358     
7359     onAdd : function(ds, records, rowIndex)
7360     {
7361         //Roo.log('on Add called');
7362         // - note this does not handle multiple adding very well..
7363         var bt = this.mainBody.dom;
7364         for (var i =0 ; i < records.length;i++) {
7365             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7366             //Roo.log(records[i]);
7367             //Roo.log(this.store.getAt(rowIndex+i));
7368             this.insertRow(this.store, rowIndex + i, false);
7369             return;
7370         }
7371         
7372     },
7373     
7374     
7375     refreshRow : function(record){
7376         var ds = this.store, index;
7377         if(typeof record == 'number'){
7378             index = record;
7379             record = ds.getAt(index);
7380         }else{
7381             index = ds.indexOf(record);
7382         }
7383         this.insertRow(ds, index, true);
7384         this.autoSize();
7385         this.onRemove(ds, record, index+1, true);
7386         this.autoSize();
7387         //this.syncRowHeights(index, index);
7388         //this.layout();
7389         this.fireEvent("rowupdated", this, index, record);
7390     },
7391     
7392     insertRow : function(dm, rowIndex, isUpdate){
7393         
7394         if(!isUpdate){
7395             this.fireEvent("beforerowsinserted", this, rowIndex);
7396         }
7397             //var s = this.getScrollState();
7398         var row = this.renderRow(this.cm, this.store, rowIndex);
7399         // insert before rowIndex..
7400         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7401         
7402         var _this = this;
7403                 
7404         if(row.cellObjects.length){
7405             Roo.each(row.cellObjects, function(r){
7406                 _this.renderCellObject(r);
7407             })
7408         }
7409             
7410         if(!isUpdate){
7411             this.fireEvent("rowsinserted", this, rowIndex);
7412             //this.syncRowHeights(firstRow, lastRow);
7413             //this.stripeRows(firstRow);
7414             //this.layout();
7415         }
7416         
7417     },
7418     
7419     
7420     getRowDom : function(rowIndex)
7421     {
7422         var rows = this.el.select('tbody > tr', true).elements;
7423         
7424         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7425         
7426     },
7427     // returns the object tree for a tr..
7428   
7429     
7430     renderRow : function(cm, ds, rowIndex) 
7431     {
7432         var d = ds.getAt(rowIndex);
7433         
7434         var row = {
7435             tag : 'tr',
7436             cls : 'x-row-' + rowIndex,
7437             cn : []
7438         };
7439             
7440         var cellObjects = [];
7441         
7442         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7443             var config = cm.config[i];
7444             
7445             var renderer = cm.getRenderer(i);
7446             var value = '';
7447             var id = false;
7448             
7449             if(typeof(renderer) !== 'undefined'){
7450                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7451             }
7452             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7453             // and are rendered into the cells after the row is rendered - using the id for the element.
7454             
7455             if(typeof(value) === 'object'){
7456                 id = Roo.id();
7457                 cellObjects.push({
7458                     container : id,
7459                     cfg : value 
7460                 })
7461             }
7462             
7463             var rowcfg = {
7464                 record: d,
7465                 rowIndex : rowIndex,
7466                 colIndex : i,
7467                 rowClass : ''
7468             };
7469
7470             this.fireEvent('rowclass', this, rowcfg);
7471             
7472             var td = {
7473                 tag: 'td',
7474                 cls : rowcfg.rowClass + ' x-col-' + i,
7475                 style: '',
7476                 html: (typeof(value) === 'object') ? '' : value
7477             };
7478             
7479             if (id) {
7480                 td.id = id;
7481             }
7482             
7483             if(typeof(config.colspan) != 'undefined'){
7484                 td.colspan = config.colspan;
7485             }
7486             
7487             if(typeof(config.hidden) != 'undefined' && config.hidden){
7488                 td.style += ' display:none;';
7489             }
7490             
7491             if(typeof(config.align) != 'undefined' && config.align.length){
7492                 td.style += ' text-align:' + config.align + ';';
7493             }
7494             if(typeof(config.valign) != 'undefined' && config.valign.length){
7495                 td.style += ' vertical-align:' + config.valign + ';';
7496             }
7497             
7498             if(typeof(config.width) != 'undefined'){
7499                 td.style += ' width:' +  config.width + 'px;';
7500             }
7501             
7502             if(typeof(config.cursor) != 'undefined'){
7503                 td.style += ' cursor:' +  config.cursor + ';';
7504             }
7505             
7506             if(typeof(config.cls) != 'undefined'){
7507                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7508             }
7509             
7510             ['xs','sm','md','lg'].map(function(size){
7511                 
7512                 if(typeof(config[size]) == 'undefined'){
7513                     return;
7514                 }
7515                 
7516                 
7517                   
7518                 if (!config[size]) { // 0 = hidden
7519                     // BS 4 '0' is treated as hide that column and below.
7520                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7521                     return;
7522                 }
7523                 
7524                 td.cls += ' col-' + size + '-' + config[size] + (
7525                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7526                 );
7527                  
7528
7529             });
7530             
7531             row.cn.push(td);
7532            
7533         }
7534         
7535         row.cellObjects = cellObjects;
7536         
7537         return row;
7538           
7539     },
7540     
7541     
7542     
7543     onBeforeLoad : function()
7544     {
7545         
7546     },
7547      /**
7548      * Remove all rows
7549      */
7550     clear : function()
7551     {
7552         this.el.select('tbody', true).first().dom.innerHTML = '';
7553     },
7554     /**
7555      * Show or hide a row.
7556      * @param {Number} rowIndex to show or hide
7557      * @param {Boolean} state hide
7558      */
7559     setRowVisibility : function(rowIndex, state)
7560     {
7561         var bt = this.mainBody.dom;
7562         
7563         var rows = this.el.select('tbody > tr', true).elements;
7564         
7565         if(typeof(rows[rowIndex]) == 'undefined'){
7566             return;
7567         }
7568         rows[rowIndex].dom.style.display = state ? '' : 'none';
7569     },
7570     
7571     
7572     getSelectionModel : function(){
7573         if(!this.selModel){
7574             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7575         }
7576         return this.selModel;
7577     },
7578     /*
7579      * Render the Roo.bootstrap object from renderder
7580      */
7581     renderCellObject : function(r)
7582     {
7583         var _this = this;
7584         
7585         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7586         
7587         var t = r.cfg.render(r.container);
7588         
7589         if(r.cfg.cn){
7590             Roo.each(r.cfg.cn, function(c){
7591                 var child = {
7592                     container: t.getChildContainer(),
7593                     cfg: c
7594                 };
7595                 _this.renderCellObject(child);
7596             })
7597         }
7598     },
7599     
7600     getRowIndex : function(row)
7601     {
7602         var rowIndex = -1;
7603         
7604         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7605             if(el != row){
7606                 return;
7607             }
7608             
7609             rowIndex = index;
7610         });
7611         
7612         return rowIndex;
7613     },
7614      /**
7615      * Returns the grid's underlying element = used by panel.Grid
7616      * @return {Element} The element
7617      */
7618     getGridEl : function(){
7619         return this.el;
7620     },
7621      /**
7622      * Forces a resize - used by panel.Grid
7623      * @return {Element} The element
7624      */
7625     autoSize : function()
7626     {
7627         //var ctr = Roo.get(this.container.dom.parentElement);
7628         var ctr = Roo.get(this.el.dom);
7629         
7630         var thd = this.getGridEl().select('thead',true).first();
7631         var tbd = this.getGridEl().select('tbody', true).first();
7632         var tfd = this.getGridEl().select('tfoot', true).first();
7633         
7634         var cw = ctr.getWidth();
7635         
7636         if (tbd) {
7637             
7638             tbd.setWidth(ctr.getWidth());
7639             // if the body has a max height - and then scrolls - we should perhaps set up the height here
7640             // this needs fixing for various usage - currently only hydra job advers I think..
7641             //tdb.setHeight(
7642             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7643             //); 
7644             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7645             cw -= barsize;
7646         }
7647         cw = Math.max(cw, this.totalWidth);
7648         this.getGridEl().select('tr',true).setWidth(cw);
7649         // resize 'expandable coloumn?
7650         
7651         return; // we doe not have a view in this design..
7652         
7653     },
7654     onBodyScroll: function()
7655     {
7656         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7657         if(this.mainHead){
7658             this.mainHead.setStyle({
7659                 'position' : 'relative',
7660                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7661             });
7662         }
7663         
7664         if(this.lazyLoad){
7665             
7666             var scrollHeight = this.mainBody.dom.scrollHeight;
7667             
7668             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7669             
7670             var height = this.mainBody.getHeight();
7671             
7672             if(scrollHeight - height == scrollTop) {
7673                 
7674                 var total = this.ds.getTotalCount();
7675                 
7676                 if(this.footer.cursor + this.footer.pageSize < total){
7677                     
7678                     this.footer.ds.load({
7679                         params : {
7680                             start : this.footer.cursor + this.footer.pageSize,
7681                             limit : this.footer.pageSize
7682                         },
7683                         add : true
7684                     });
7685                 }
7686             }
7687             
7688         }
7689     },
7690     
7691     onHeaderChange : function()
7692     {
7693         var header = this.renderHeader();
7694         var table = this.el.select('table', true).first();
7695         
7696         this.mainHead.remove();
7697         this.mainHead = table.createChild(header, this.mainBody, false);
7698     },
7699     
7700     onHiddenChange : function(colModel, colIndex, hidden)
7701     {
7702         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7703         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7704         
7705         this.CSS.updateRule(thSelector, "display", "");
7706         this.CSS.updateRule(tdSelector, "display", "");
7707         
7708         if(hidden){
7709             this.CSS.updateRule(thSelector, "display", "none");
7710             this.CSS.updateRule(tdSelector, "display", "none");
7711         }
7712         
7713         this.onHeaderChange();
7714         this.onLoad();
7715     },
7716     
7717     setColumnWidth: function(col_index, width)
7718     {
7719         // width = "md-2 xs-2..."
7720         if(!this.colModel.config[col_index]) {
7721             return;
7722         }
7723         
7724         var w = width.split(" ");
7725         
7726         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7727         
7728         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7729         
7730         
7731         for(var j = 0; j < w.length; j++) {
7732             
7733             if(!w[j]) {
7734                 continue;
7735             }
7736             
7737             var size_cls = w[j].split("-");
7738             
7739             if(!Number.isInteger(size_cls[1] * 1)) {
7740                 continue;
7741             }
7742             
7743             if(!this.colModel.config[col_index][size_cls[0]]) {
7744                 continue;
7745             }
7746             
7747             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7748                 continue;
7749             }
7750             
7751             h_row[0].classList.replace(
7752                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7753                 "col-"+size_cls[0]+"-"+size_cls[1]
7754             );
7755             
7756             for(var i = 0; i < rows.length; i++) {
7757                 
7758                 var size_cls = w[j].split("-");
7759                 
7760                 if(!Number.isInteger(size_cls[1] * 1)) {
7761                     continue;
7762                 }
7763                 
7764                 if(!this.colModel.config[col_index][size_cls[0]]) {
7765                     continue;
7766                 }
7767                 
7768                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7769                     continue;
7770                 }
7771                 
7772                 rows[i].classList.replace(
7773                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7774                     "col-"+size_cls[0]+"-"+size_cls[1]
7775                 );
7776             }
7777             
7778             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7779         }
7780     }
7781 });
7782
7783  
7784
7785  /*
7786  * - LGPL
7787  *
7788  * table cell
7789  * 
7790  */
7791
7792 /**
7793  * @class Roo.bootstrap.TableCell
7794  * @extends Roo.bootstrap.Component
7795  * Bootstrap TableCell class
7796  * @cfg {String} html cell contain text
7797  * @cfg {String} cls cell class
7798  * @cfg {String} tag cell tag (td|th) default td
7799  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7800  * @cfg {String} align Aligns the content in a cell
7801  * @cfg {String} axis Categorizes cells
7802  * @cfg {String} bgcolor Specifies the background color of a cell
7803  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7804  * @cfg {Number} colspan Specifies the number of columns a cell should span
7805  * @cfg {String} headers Specifies one or more header cells a cell is related to
7806  * @cfg {Number} height Sets the height of a cell
7807  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7808  * @cfg {Number} rowspan Sets the number of rows a cell should span
7809  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7810  * @cfg {String} valign Vertical aligns the content in a cell
7811  * @cfg {Number} width Specifies the width of a cell
7812  * 
7813  * @constructor
7814  * Create a new TableCell
7815  * @param {Object} config The config object
7816  */
7817
7818 Roo.bootstrap.TableCell = function(config){
7819     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7820 };
7821
7822 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7823     
7824     html: false,
7825     cls: false,
7826     tag: false,
7827     abbr: false,
7828     align: false,
7829     axis: false,
7830     bgcolor: false,
7831     charoff: false,
7832     colspan: false,
7833     headers: false,
7834     height: false,
7835     nowrap: false,
7836     rowspan: false,
7837     scope: false,
7838     valign: false,
7839     width: false,
7840     
7841     
7842     getAutoCreate : function(){
7843         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7844         
7845         cfg = {
7846             tag: 'td'
7847         };
7848         
7849         if(this.tag){
7850             cfg.tag = this.tag;
7851         }
7852         
7853         if (this.html) {
7854             cfg.html=this.html
7855         }
7856         if (this.cls) {
7857             cfg.cls=this.cls
7858         }
7859         if (this.abbr) {
7860             cfg.abbr=this.abbr
7861         }
7862         if (this.align) {
7863             cfg.align=this.align
7864         }
7865         if (this.axis) {
7866             cfg.axis=this.axis
7867         }
7868         if (this.bgcolor) {
7869             cfg.bgcolor=this.bgcolor
7870         }
7871         if (this.charoff) {
7872             cfg.charoff=this.charoff
7873         }
7874         if (this.colspan) {
7875             cfg.colspan=this.colspan
7876         }
7877         if (this.headers) {
7878             cfg.headers=this.headers
7879         }
7880         if (this.height) {
7881             cfg.height=this.height
7882         }
7883         if (this.nowrap) {
7884             cfg.nowrap=this.nowrap
7885         }
7886         if (this.rowspan) {
7887             cfg.rowspan=this.rowspan
7888         }
7889         if (this.scope) {
7890             cfg.scope=this.scope
7891         }
7892         if (this.valign) {
7893             cfg.valign=this.valign
7894         }
7895         if (this.width) {
7896             cfg.width=this.width
7897         }
7898         
7899         
7900         return cfg;
7901     }
7902    
7903 });
7904
7905  
7906
7907  /*
7908  * - LGPL
7909  *
7910  * table row
7911  * 
7912  */
7913
7914 /**
7915  * @class Roo.bootstrap.TableRow
7916  * @extends Roo.bootstrap.Component
7917  * Bootstrap TableRow class
7918  * @cfg {String} cls row class
7919  * @cfg {String} align Aligns the content in a table row
7920  * @cfg {String} bgcolor Specifies a background color for a table row
7921  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7922  * @cfg {String} valign Vertical aligns the content in a table row
7923  * 
7924  * @constructor
7925  * Create a new TableRow
7926  * @param {Object} config The config object
7927  */
7928
7929 Roo.bootstrap.TableRow = function(config){
7930     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7931 };
7932
7933 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7934     
7935     cls: false,
7936     align: false,
7937     bgcolor: false,
7938     charoff: false,
7939     valign: false,
7940     
7941     getAutoCreate : function(){
7942         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7943         
7944         cfg = {
7945             tag: 'tr'
7946         };
7947             
7948         if(this.cls){
7949             cfg.cls = this.cls;
7950         }
7951         if(this.align){
7952             cfg.align = this.align;
7953         }
7954         if(this.bgcolor){
7955             cfg.bgcolor = this.bgcolor;
7956         }
7957         if(this.charoff){
7958             cfg.charoff = this.charoff;
7959         }
7960         if(this.valign){
7961             cfg.valign = this.valign;
7962         }
7963         
7964         return cfg;
7965     }
7966    
7967 });
7968
7969  
7970
7971  /*
7972  * - LGPL
7973  *
7974  * table body
7975  * 
7976  */
7977
7978 /**
7979  * @class Roo.bootstrap.TableBody
7980  * @extends Roo.bootstrap.Component
7981  * Bootstrap TableBody class
7982  * @cfg {String} cls element class
7983  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7984  * @cfg {String} align Aligns the content inside the element
7985  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7986  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7987  * 
7988  * @constructor
7989  * Create a new TableBody
7990  * @param {Object} config The config object
7991  */
7992
7993 Roo.bootstrap.TableBody = function(config){
7994     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7995 };
7996
7997 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7998     
7999     cls: false,
8000     tag: false,
8001     align: false,
8002     charoff: false,
8003     valign: false,
8004     
8005     getAutoCreate : function(){
8006         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8007         
8008         cfg = {
8009             tag: 'tbody'
8010         };
8011             
8012         if (this.cls) {
8013             cfg.cls=this.cls
8014         }
8015         if(this.tag){
8016             cfg.tag = this.tag;
8017         }
8018         
8019         if(this.align){
8020             cfg.align = this.align;
8021         }
8022         if(this.charoff){
8023             cfg.charoff = this.charoff;
8024         }
8025         if(this.valign){
8026             cfg.valign = this.valign;
8027         }
8028         
8029         return cfg;
8030     }
8031     
8032     
8033 //    initEvents : function()
8034 //    {
8035 //        
8036 //        if(!this.store){
8037 //            return;
8038 //        }
8039 //        
8040 //        this.store = Roo.factory(this.store, Roo.data);
8041 //        this.store.on('load', this.onLoad, this);
8042 //        
8043 //        this.store.load();
8044 //        
8045 //    },
8046 //    
8047 //    onLoad: function () 
8048 //    {   
8049 //        this.fireEvent('load', this);
8050 //    }
8051 //    
8052 //   
8053 });
8054
8055  
8056
8057  /*
8058  * Based on:
8059  * Ext JS Library 1.1.1
8060  * Copyright(c) 2006-2007, Ext JS, LLC.
8061  *
8062  * Originally Released Under LGPL - original licence link has changed is not relivant.
8063  *
8064  * Fork - LGPL
8065  * <script type="text/javascript">
8066  */
8067
8068 // as we use this in bootstrap.
8069 Roo.namespace('Roo.form');
8070  /**
8071  * @class Roo.form.Action
8072  * Internal Class used to handle form actions
8073  * @constructor
8074  * @param {Roo.form.BasicForm} el The form element or its id
8075  * @param {Object} config Configuration options
8076  */
8077
8078  
8079  
8080 // define the action interface
8081 Roo.form.Action = function(form, options){
8082     this.form = form;
8083     this.options = options || {};
8084 };
8085 /**
8086  * Client Validation Failed
8087  * @const 
8088  */
8089 Roo.form.Action.CLIENT_INVALID = 'client';
8090 /**
8091  * Server Validation Failed
8092  * @const 
8093  */
8094 Roo.form.Action.SERVER_INVALID = 'server';
8095  /**
8096  * Connect to Server Failed
8097  * @const 
8098  */
8099 Roo.form.Action.CONNECT_FAILURE = 'connect';
8100 /**
8101  * Reading Data from Server Failed
8102  * @const 
8103  */
8104 Roo.form.Action.LOAD_FAILURE = 'load';
8105
8106 Roo.form.Action.prototype = {
8107     type : 'default',
8108     failureType : undefined,
8109     response : undefined,
8110     result : undefined,
8111
8112     // interface method
8113     run : function(options){
8114
8115     },
8116
8117     // interface method
8118     success : function(response){
8119
8120     },
8121
8122     // interface method
8123     handleResponse : function(response){
8124
8125     },
8126
8127     // default connection failure
8128     failure : function(response){
8129         
8130         this.response = response;
8131         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8132         this.form.afterAction(this, false);
8133     },
8134
8135     processResponse : function(response){
8136         this.response = response;
8137         if(!response.responseText){
8138             return true;
8139         }
8140         this.result = this.handleResponse(response);
8141         return this.result;
8142     },
8143
8144     // utility functions used internally
8145     getUrl : function(appendParams){
8146         var url = this.options.url || this.form.url || this.form.el.dom.action;
8147         if(appendParams){
8148             var p = this.getParams();
8149             if(p){
8150                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8151             }
8152         }
8153         return url;
8154     },
8155
8156     getMethod : function(){
8157         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8158     },
8159
8160     getParams : function(){
8161         var bp = this.form.baseParams;
8162         var p = this.options.params;
8163         if(p){
8164             if(typeof p == "object"){
8165                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8166             }else if(typeof p == 'string' && bp){
8167                 p += '&' + Roo.urlEncode(bp);
8168             }
8169         }else if(bp){
8170             p = Roo.urlEncode(bp);
8171         }
8172         return p;
8173     },
8174
8175     createCallback : function(){
8176         return {
8177             success: this.success,
8178             failure: this.failure,
8179             scope: this,
8180             timeout: (this.form.timeout*1000),
8181             upload: this.form.fileUpload ? this.success : undefined
8182         };
8183     }
8184 };
8185
8186 Roo.form.Action.Submit = function(form, options){
8187     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8188 };
8189
8190 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8191     type : 'submit',
8192
8193     haveProgress : false,
8194     uploadComplete : false,
8195     
8196     // uploadProgress indicator.
8197     uploadProgress : function()
8198     {
8199         if (!this.form.progressUrl) {
8200             return;
8201         }
8202         
8203         if (!this.haveProgress) {
8204             Roo.MessageBox.progress("Uploading", "Uploading");
8205         }
8206         if (this.uploadComplete) {
8207            Roo.MessageBox.hide();
8208            return;
8209         }
8210         
8211         this.haveProgress = true;
8212    
8213         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8214         
8215         var c = new Roo.data.Connection();
8216         c.request({
8217             url : this.form.progressUrl,
8218             params: {
8219                 id : uid
8220             },
8221             method: 'GET',
8222             success : function(req){
8223                //console.log(data);
8224                 var rdata = false;
8225                 var edata;
8226                 try  {
8227                    rdata = Roo.decode(req.responseText)
8228                 } catch (e) {
8229                     Roo.log("Invalid data from server..");
8230                     Roo.log(edata);
8231                     return;
8232                 }
8233                 if (!rdata || !rdata.success) {
8234                     Roo.log(rdata);
8235                     Roo.MessageBox.alert(Roo.encode(rdata));
8236                     return;
8237                 }
8238                 var data = rdata.data;
8239                 
8240                 if (this.uploadComplete) {
8241                    Roo.MessageBox.hide();
8242                    return;
8243                 }
8244                    
8245                 if (data){
8246                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8247                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8248                     );
8249                 }
8250                 this.uploadProgress.defer(2000,this);
8251             },
8252        
8253             failure: function(data) {
8254                 Roo.log('progress url failed ');
8255                 Roo.log(data);
8256             },
8257             scope : this
8258         });
8259            
8260     },
8261     
8262     
8263     run : function()
8264     {
8265         // run get Values on the form, so it syncs any secondary forms.
8266         this.form.getValues();
8267         
8268         var o = this.options;
8269         var method = this.getMethod();
8270         var isPost = method == 'POST';
8271         if(o.clientValidation === false || this.form.isValid()){
8272             
8273             if (this.form.progressUrl) {
8274                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8275                     (new Date() * 1) + '' + Math.random());
8276                     
8277             } 
8278             
8279             
8280             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8281                 form:this.form.el.dom,
8282                 url:this.getUrl(!isPost),
8283                 method: method,
8284                 params:isPost ? this.getParams() : null,
8285                 isUpload: this.form.fileUpload,
8286                 formData : this.form.formData
8287             }));
8288             
8289             this.uploadProgress();
8290
8291         }else if (o.clientValidation !== false){ // client validation failed
8292             this.failureType = Roo.form.Action.CLIENT_INVALID;
8293             this.form.afterAction(this, false);
8294         }
8295     },
8296
8297     success : function(response)
8298     {
8299         this.uploadComplete= true;
8300         if (this.haveProgress) {
8301             Roo.MessageBox.hide();
8302         }
8303         
8304         
8305         var result = this.processResponse(response);
8306         if(result === true || result.success){
8307             this.form.afterAction(this, true);
8308             return;
8309         }
8310         if(result.errors){
8311             this.form.markInvalid(result.errors);
8312             this.failureType = Roo.form.Action.SERVER_INVALID;
8313         }
8314         this.form.afterAction(this, false);
8315     },
8316     failure : function(response)
8317     {
8318         this.uploadComplete= true;
8319         if (this.haveProgress) {
8320             Roo.MessageBox.hide();
8321         }
8322         
8323         this.response = response;
8324         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8325         this.form.afterAction(this, false);
8326     },
8327     
8328     handleResponse : function(response){
8329         if(this.form.errorReader){
8330             var rs = this.form.errorReader.read(response);
8331             var errors = [];
8332             if(rs.records){
8333                 for(var i = 0, len = rs.records.length; i < len; i++) {
8334                     var r = rs.records[i];
8335                     errors[i] = r.data;
8336                 }
8337             }
8338             if(errors.length < 1){
8339                 errors = null;
8340             }
8341             return {
8342                 success : rs.success,
8343                 errors : errors
8344             };
8345         }
8346         var ret = false;
8347         try {
8348             ret = Roo.decode(response.responseText);
8349         } catch (e) {
8350             ret = {
8351                 success: false,
8352                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8353                 errors : []
8354             };
8355         }
8356         return ret;
8357         
8358     }
8359 });
8360
8361
8362 Roo.form.Action.Load = function(form, options){
8363     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8364     this.reader = this.form.reader;
8365 };
8366
8367 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8368     type : 'load',
8369
8370     run : function(){
8371         
8372         Roo.Ajax.request(Roo.apply(
8373                 this.createCallback(), {
8374                     method:this.getMethod(),
8375                     url:this.getUrl(false),
8376                     params:this.getParams()
8377         }));
8378     },
8379
8380     success : function(response){
8381         
8382         var result = this.processResponse(response);
8383         if(result === true || !result.success || !result.data){
8384             this.failureType = Roo.form.Action.LOAD_FAILURE;
8385             this.form.afterAction(this, false);
8386             return;
8387         }
8388         this.form.clearInvalid();
8389         this.form.setValues(result.data);
8390         this.form.afterAction(this, true);
8391     },
8392
8393     handleResponse : function(response){
8394         if(this.form.reader){
8395             var rs = this.form.reader.read(response);
8396             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8397             return {
8398                 success : rs.success,
8399                 data : data
8400             };
8401         }
8402         return Roo.decode(response.responseText);
8403     }
8404 });
8405
8406 Roo.form.Action.ACTION_TYPES = {
8407     'load' : Roo.form.Action.Load,
8408     'submit' : Roo.form.Action.Submit
8409 };/*
8410  * - LGPL
8411  *
8412  * form
8413  *
8414  */
8415
8416 /**
8417  * @class Roo.bootstrap.Form
8418  * @extends Roo.bootstrap.Component
8419  * Bootstrap Form class
8420  * @cfg {String} method  GET | POST (default POST)
8421  * @cfg {String} labelAlign top | left (default top)
8422  * @cfg {String} align left  | right - for navbars
8423  * @cfg {Boolean} loadMask load mask when submit (default true)
8424
8425  *
8426  * @constructor
8427  * Create a new Form
8428  * @param {Object} config The config object
8429  */
8430
8431
8432 Roo.bootstrap.Form = function(config){
8433     
8434     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8435     
8436     Roo.bootstrap.Form.popover.apply();
8437     
8438     this.addEvents({
8439         /**
8440          * @event clientvalidation
8441          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8442          * @param {Form} this
8443          * @param {Boolean} valid true if the form has passed client-side validation
8444          */
8445         clientvalidation: true,
8446         /**
8447          * @event beforeaction
8448          * Fires before any action is performed. Return false to cancel the action.
8449          * @param {Form} this
8450          * @param {Action} action The action to be performed
8451          */
8452         beforeaction: true,
8453         /**
8454          * @event actionfailed
8455          * Fires when an action fails.
8456          * @param {Form} this
8457          * @param {Action} action The action that failed
8458          */
8459         actionfailed : true,
8460         /**
8461          * @event actioncomplete
8462          * Fires when an action is completed.
8463          * @param {Form} this
8464          * @param {Action} action The action that completed
8465          */
8466         actioncomplete : true
8467     });
8468 };
8469
8470 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8471
8472      /**
8473      * @cfg {String} method
8474      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8475      */
8476     method : 'POST',
8477     /**
8478      * @cfg {String} url
8479      * The URL to use for form actions if one isn't supplied in the action options.
8480      */
8481     /**
8482      * @cfg {Boolean} fileUpload
8483      * Set to true if this form is a file upload.
8484      */
8485
8486     /**
8487      * @cfg {Object} baseParams
8488      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8489      */
8490
8491     /**
8492      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8493      */
8494     timeout: 30,
8495     /**
8496      * @cfg {Sting} align (left|right) for navbar forms
8497      */
8498     align : 'left',
8499
8500     // private
8501     activeAction : null,
8502
8503     /**
8504      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8505      * element by passing it or its id or mask the form itself by passing in true.
8506      * @type Mixed
8507      */
8508     waitMsgTarget : false,
8509
8510     loadMask : true,
8511     
8512     /**
8513      * @cfg {Boolean} errorMask (true|false) default false
8514      */
8515     errorMask : false,
8516     
8517     /**
8518      * @cfg {Number} maskOffset Default 100
8519      */
8520     maskOffset : 100,
8521     
8522     /**
8523      * @cfg {Boolean} maskBody
8524      */
8525     maskBody : false,
8526
8527     getAutoCreate : function(){
8528
8529         var cfg = {
8530             tag: 'form',
8531             method : this.method || 'POST',
8532             id : this.id || Roo.id(),
8533             cls : ''
8534         };
8535         if (this.parent().xtype.match(/^Nav/)) {
8536             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8537
8538         }
8539
8540         if (this.labelAlign == 'left' ) {
8541             cfg.cls += ' form-horizontal';
8542         }
8543
8544
8545         return cfg;
8546     },
8547     initEvents : function()
8548     {
8549         this.el.on('submit', this.onSubmit, this);
8550         // this was added as random key presses on the form where triggering form submit.
8551         this.el.on('keypress', function(e) {
8552             if (e.getCharCode() != 13) {
8553                 return true;
8554             }
8555             // we might need to allow it for textareas.. and some other items.
8556             // check e.getTarget().
8557
8558             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8559                 return true;
8560             }
8561
8562             Roo.log("keypress blocked");
8563
8564             e.preventDefault();
8565             return false;
8566         });
8567         
8568     },
8569     // private
8570     onSubmit : function(e){
8571         e.stopEvent();
8572     },
8573
8574      /**
8575      * Returns true if client-side validation on the form is successful.
8576      * @return Boolean
8577      */
8578     isValid : function(){
8579         var items = this.getItems();
8580         var valid = true;
8581         var target = false;
8582         
8583         items.each(function(f){
8584             
8585             if(f.validate()){
8586                 return;
8587             }
8588             
8589             Roo.log('invalid field: ' + f.name);
8590             
8591             valid = false;
8592
8593             if(!target && f.el.isVisible(true)){
8594                 target = f;
8595             }
8596            
8597         });
8598         
8599         if(this.errorMask && !valid){
8600             Roo.bootstrap.Form.popover.mask(this, target);
8601         }
8602         
8603         return valid;
8604     },
8605     
8606     /**
8607      * Returns true if any fields in this form have changed since their original load.
8608      * @return Boolean
8609      */
8610     isDirty : function(){
8611         var dirty = false;
8612         var items = this.getItems();
8613         items.each(function(f){
8614            if(f.isDirty()){
8615                dirty = true;
8616                return false;
8617            }
8618            return true;
8619         });
8620         return dirty;
8621     },
8622      /**
8623      * Performs a predefined action (submit or load) or custom actions you define on this form.
8624      * @param {String} actionName The name of the action type
8625      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8626      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8627      * accept other config options):
8628      * <pre>
8629 Property          Type             Description
8630 ----------------  ---------------  ----------------------------------------------------------------------------------
8631 url               String           The url for the action (defaults to the form's url)
8632 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8633 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8634 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8635                                    validate the form on the client (defaults to false)
8636      * </pre>
8637      * @return {BasicForm} this
8638      */
8639     doAction : function(action, options){
8640         if(typeof action == 'string'){
8641             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8642         }
8643         if(this.fireEvent('beforeaction', this, action) !== false){
8644             this.beforeAction(action);
8645             action.run.defer(100, action);
8646         }
8647         return this;
8648     },
8649
8650     // private
8651     beforeAction : function(action){
8652         var o = action.options;
8653         
8654         if(this.loadMask){
8655             
8656             if(this.maskBody){
8657                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8658             } else {
8659                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8660             }
8661         }
8662         // not really supported yet.. ??
8663
8664         //if(this.waitMsgTarget === true){
8665         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8666         //}else if(this.waitMsgTarget){
8667         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8668         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8669         //}else {
8670         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8671        // }
8672
8673     },
8674
8675     // private
8676     afterAction : function(action, success){
8677         this.activeAction = null;
8678         var o = action.options;
8679
8680         if(this.loadMask){
8681             
8682             if(this.maskBody){
8683                 Roo.get(document.body).unmask();
8684             } else {
8685                 this.el.unmask();
8686             }
8687         }
8688         
8689         //if(this.waitMsgTarget === true){
8690 //            this.el.unmask();
8691         //}else if(this.waitMsgTarget){
8692         //    this.waitMsgTarget.unmask();
8693         //}else{
8694         //    Roo.MessageBox.updateProgress(1);
8695         //    Roo.MessageBox.hide();
8696        // }
8697         //
8698         if(success){
8699             if(o.reset){
8700                 this.reset();
8701             }
8702             Roo.callback(o.success, o.scope, [this, action]);
8703             this.fireEvent('actioncomplete', this, action);
8704
8705         }else{
8706
8707             // failure condition..
8708             // we have a scenario where updates need confirming.
8709             // eg. if a locking scenario exists..
8710             // we look for { errors : { needs_confirm : true }} in the response.
8711             if (
8712                 (typeof(action.result) != 'undefined')  &&
8713                 (typeof(action.result.errors) != 'undefined')  &&
8714                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8715            ){
8716                 var _t = this;
8717                 Roo.log("not supported yet");
8718                  /*
8719
8720                 Roo.MessageBox.confirm(
8721                     "Change requires confirmation",
8722                     action.result.errorMsg,
8723                     function(r) {
8724                         if (r != 'yes') {
8725                             return;
8726                         }
8727                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8728                     }
8729
8730                 );
8731                 */
8732
8733
8734                 return;
8735             }
8736
8737             Roo.callback(o.failure, o.scope, [this, action]);
8738             // show an error message if no failed handler is set..
8739             if (!this.hasListener('actionfailed')) {
8740                 Roo.log("need to add dialog support");
8741                 /*
8742                 Roo.MessageBox.alert("Error",
8743                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8744                         action.result.errorMsg :
8745                         "Saving Failed, please check your entries or try again"
8746                 );
8747                 */
8748             }
8749
8750             this.fireEvent('actionfailed', this, action);
8751         }
8752
8753     },
8754     /**
8755      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8756      * @param {String} id The value to search for
8757      * @return Field
8758      */
8759     findField : function(id){
8760         var items = this.getItems();
8761         var field = items.get(id);
8762         if(!field){
8763              items.each(function(f){
8764                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8765                     field = f;
8766                     return false;
8767                 }
8768                 return true;
8769             });
8770         }
8771         return field || null;
8772     },
8773      /**
8774      * Mark fields in this form invalid in bulk.
8775      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8776      * @return {BasicForm} this
8777      */
8778     markInvalid : function(errors){
8779         if(errors instanceof Array){
8780             for(var i = 0, len = errors.length; i < len; i++){
8781                 var fieldError = errors[i];
8782                 var f = this.findField(fieldError.id);
8783                 if(f){
8784                     f.markInvalid(fieldError.msg);
8785                 }
8786             }
8787         }else{
8788             var field, id;
8789             for(id in errors){
8790                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8791                     field.markInvalid(errors[id]);
8792                 }
8793             }
8794         }
8795         //Roo.each(this.childForms || [], function (f) {
8796         //    f.markInvalid(errors);
8797         //});
8798
8799         return this;
8800     },
8801
8802     /**
8803      * Set values for fields in this form in bulk.
8804      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8805      * @return {BasicForm} this
8806      */
8807     setValues : function(values){
8808         if(values instanceof Array){ // array of objects
8809             for(var i = 0, len = values.length; i < len; i++){
8810                 var v = values[i];
8811                 var f = this.findField(v.id);
8812                 if(f){
8813                     f.setValue(v.value);
8814                     if(this.trackResetOnLoad){
8815                         f.originalValue = f.getValue();
8816                     }
8817                 }
8818             }
8819         }else{ // object hash
8820             var field, id;
8821             for(id in values){
8822                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8823
8824                     if (field.setFromData &&
8825                         field.valueField &&
8826                         field.displayField &&
8827                         // combos' with local stores can
8828                         // be queried via setValue()
8829                         // to set their value..
8830                         (field.store && !field.store.isLocal)
8831                         ) {
8832                         // it's a combo
8833                         var sd = { };
8834                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8835                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8836                         field.setFromData(sd);
8837
8838                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8839                         
8840                         field.setFromData(values);
8841                         
8842                     } else {
8843                         field.setValue(values[id]);
8844                     }
8845
8846
8847                     if(this.trackResetOnLoad){
8848                         field.originalValue = field.getValue();
8849                     }
8850                 }
8851             }
8852         }
8853
8854         //Roo.each(this.childForms || [], function (f) {
8855         //    f.setValues(values);
8856         //});
8857
8858         return this;
8859     },
8860
8861     /**
8862      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8863      * they are returned as an array.
8864      * @param {Boolean} asString
8865      * @return {Object}
8866      */
8867     getValues : function(asString){
8868         //if (this.childForms) {
8869             // copy values from the child forms
8870         //    Roo.each(this.childForms, function (f) {
8871         //        this.setValues(f.getValues());
8872         //    }, this);
8873         //}
8874
8875
8876
8877         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8878         if(asString === true){
8879             return fs;
8880         }
8881         return Roo.urlDecode(fs);
8882     },
8883
8884     /**
8885      * Returns the fields in this form as an object with key/value pairs.
8886      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8887      * @return {Object}
8888      */
8889     getFieldValues : function(with_hidden)
8890     {
8891         var items = this.getItems();
8892         var ret = {};
8893         items.each(function(f){
8894             
8895             if (!f.getName()) {
8896                 return;
8897             }
8898             
8899             var v = f.getValue();
8900             
8901             if (f.inputType =='radio') {
8902                 if (typeof(ret[f.getName()]) == 'undefined') {
8903                     ret[f.getName()] = ''; // empty..
8904                 }
8905
8906                 if (!f.el.dom.checked) {
8907                     return;
8908
8909                 }
8910                 v = f.el.dom.value;
8911
8912             }
8913             
8914             if(f.xtype == 'MoneyField'){
8915                 ret[f.currencyName] = f.getCurrency();
8916             }
8917
8918             // not sure if this supported any more..
8919             if ((typeof(v) == 'object') && f.getRawValue) {
8920                 v = f.getRawValue() ; // dates..
8921             }
8922             // combo boxes where name != hiddenName...
8923             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8924                 ret[f.name] = f.getRawValue();
8925             }
8926             ret[f.getName()] = v;
8927         });
8928
8929         return ret;
8930     },
8931
8932     /**
8933      * Clears all invalid messages in this form.
8934      * @return {BasicForm} this
8935      */
8936     clearInvalid : function(){
8937         var items = this.getItems();
8938
8939         items.each(function(f){
8940            f.clearInvalid();
8941         });
8942
8943         return this;
8944     },
8945
8946     /**
8947      * Resets this form.
8948      * @return {BasicForm} this
8949      */
8950     reset : function(){
8951         var items = this.getItems();
8952         items.each(function(f){
8953             f.reset();
8954         });
8955
8956         Roo.each(this.childForms || [], function (f) {
8957             f.reset();
8958         });
8959
8960
8961         return this;
8962     },
8963     
8964     getItems : function()
8965     {
8966         var r=new Roo.util.MixedCollection(false, function(o){
8967             return o.id || (o.id = Roo.id());
8968         });
8969         var iter = function(el) {
8970             if (el.inputEl) {
8971                 r.add(el);
8972             }
8973             if (!el.items) {
8974                 return;
8975             }
8976             Roo.each(el.items,function(e) {
8977                 iter(e);
8978             });
8979         };
8980
8981         iter(this);
8982         return r;
8983     },
8984     
8985     hideFields : function(items)
8986     {
8987         Roo.each(items, function(i){
8988             
8989             var f = this.findField(i);
8990             
8991             if(!f){
8992                 return;
8993             }
8994             
8995             f.hide();
8996             
8997         }, this);
8998     },
8999     
9000     showFields : function(items)
9001     {
9002         Roo.each(items, function(i){
9003             
9004             var f = this.findField(i);
9005             
9006             if(!f){
9007                 return;
9008             }
9009             
9010             f.show();
9011             
9012         }, this);
9013     }
9014
9015 });
9016
9017 Roo.apply(Roo.bootstrap.Form, {
9018     
9019     popover : {
9020         
9021         padding : 5,
9022         
9023         isApplied : false,
9024         
9025         isMasked : false,
9026         
9027         form : false,
9028         
9029         target : false,
9030         
9031         toolTip : false,
9032         
9033         intervalID : false,
9034         
9035         maskEl : false,
9036         
9037         apply : function()
9038         {
9039             if(this.isApplied){
9040                 return;
9041             }
9042             
9043             this.maskEl = {
9044                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9045                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9046                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9047                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9048             };
9049             
9050             this.maskEl.top.enableDisplayMode("block");
9051             this.maskEl.left.enableDisplayMode("block");
9052             this.maskEl.bottom.enableDisplayMode("block");
9053             this.maskEl.right.enableDisplayMode("block");
9054             
9055             this.toolTip = new Roo.bootstrap.Tooltip({
9056                 cls : 'roo-form-error-popover',
9057                 alignment : {
9058                     'left' : ['r-l', [-2,0], 'right'],
9059                     'right' : ['l-r', [2,0], 'left'],
9060                     'bottom' : ['tl-bl', [0,2], 'top'],
9061                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9062                 }
9063             });
9064             
9065             this.toolTip.render(Roo.get(document.body));
9066
9067             this.toolTip.el.enableDisplayMode("block");
9068             
9069             Roo.get(document.body).on('click', function(){
9070                 this.unmask();
9071             }, this);
9072             
9073             Roo.get(document.body).on('touchstart', function(){
9074                 this.unmask();
9075             }, this);
9076             
9077             this.isApplied = true
9078         },
9079         
9080         mask : function(form, target)
9081         {
9082             this.form = form;
9083             
9084             this.target = target;
9085             
9086             if(!this.form.errorMask || !target.el){
9087                 return;
9088             }
9089             
9090             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9091             
9092             Roo.log(scrollable);
9093             
9094             var ot = this.target.el.calcOffsetsTo(scrollable);
9095             
9096             var scrollTo = ot[1] - this.form.maskOffset;
9097             
9098             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9099             
9100             scrollable.scrollTo('top', scrollTo);
9101             
9102             var box = this.target.el.getBox();
9103             Roo.log(box);
9104             var zIndex = Roo.bootstrap.Modal.zIndex++;
9105
9106             
9107             this.maskEl.top.setStyle('position', 'absolute');
9108             this.maskEl.top.setStyle('z-index', zIndex);
9109             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9110             this.maskEl.top.setLeft(0);
9111             this.maskEl.top.setTop(0);
9112             this.maskEl.top.show();
9113             
9114             this.maskEl.left.setStyle('position', 'absolute');
9115             this.maskEl.left.setStyle('z-index', zIndex);
9116             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9117             this.maskEl.left.setLeft(0);
9118             this.maskEl.left.setTop(box.y - this.padding);
9119             this.maskEl.left.show();
9120
9121             this.maskEl.bottom.setStyle('position', 'absolute');
9122             this.maskEl.bottom.setStyle('z-index', zIndex);
9123             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9124             this.maskEl.bottom.setLeft(0);
9125             this.maskEl.bottom.setTop(box.bottom + this.padding);
9126             this.maskEl.bottom.show();
9127
9128             this.maskEl.right.setStyle('position', 'absolute');
9129             this.maskEl.right.setStyle('z-index', zIndex);
9130             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9131             this.maskEl.right.setLeft(box.right + this.padding);
9132             this.maskEl.right.setTop(box.y - this.padding);
9133             this.maskEl.right.show();
9134
9135             this.toolTip.bindEl = this.target.el;
9136
9137             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9138
9139             var tip = this.target.blankText;
9140
9141             if(this.target.getValue() !== '' ) {
9142                 
9143                 if (this.target.invalidText.length) {
9144                     tip = this.target.invalidText;
9145                 } else if (this.target.regexText.length){
9146                     tip = this.target.regexText;
9147                 }
9148             }
9149
9150             this.toolTip.show(tip);
9151
9152             this.intervalID = window.setInterval(function() {
9153                 Roo.bootstrap.Form.popover.unmask();
9154             }, 10000);
9155
9156             window.onwheel = function(){ return false;};
9157             
9158             (function(){ this.isMasked = true; }).defer(500, this);
9159             
9160         },
9161         
9162         unmask : function()
9163         {
9164             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9165                 return;
9166             }
9167             
9168             this.maskEl.top.setStyle('position', 'absolute');
9169             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9170             this.maskEl.top.hide();
9171
9172             this.maskEl.left.setStyle('position', 'absolute');
9173             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9174             this.maskEl.left.hide();
9175
9176             this.maskEl.bottom.setStyle('position', 'absolute');
9177             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9178             this.maskEl.bottom.hide();
9179
9180             this.maskEl.right.setStyle('position', 'absolute');
9181             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9182             this.maskEl.right.hide();
9183             
9184             this.toolTip.hide();
9185             
9186             this.toolTip.el.hide();
9187             
9188             window.onwheel = function(){ return true;};
9189             
9190             if(this.intervalID){
9191                 window.clearInterval(this.intervalID);
9192                 this.intervalID = false;
9193             }
9194             
9195             this.isMasked = false;
9196             
9197         }
9198         
9199     }
9200     
9201 });
9202
9203 /*
9204  * Based on:
9205  * Ext JS Library 1.1.1
9206  * Copyright(c) 2006-2007, Ext JS, LLC.
9207  *
9208  * Originally Released Under LGPL - original licence link has changed is not relivant.
9209  *
9210  * Fork - LGPL
9211  * <script type="text/javascript">
9212  */
9213 /**
9214  * @class Roo.form.VTypes
9215  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9216  * @singleton
9217  */
9218 Roo.form.VTypes = function(){
9219     // closure these in so they are only created once.
9220     var alpha = /^[a-zA-Z_]+$/;
9221     var alphanum = /^[a-zA-Z0-9_]+$/;
9222     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9223     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9224
9225     // All these messages and functions are configurable
9226     return {
9227         /**
9228          * The function used to validate email addresses
9229          * @param {String} value The email address
9230          */
9231         'email' : function(v){
9232             return email.test(v);
9233         },
9234         /**
9235          * The error text to display when the email validation function returns false
9236          * @type String
9237          */
9238         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9239         /**
9240          * The keystroke filter mask to be applied on email input
9241          * @type RegExp
9242          */
9243         'emailMask' : /[a-z0-9_\.\-@]/i,
9244
9245         /**
9246          * The function used to validate URLs
9247          * @param {String} value The URL
9248          */
9249         'url' : function(v){
9250             return url.test(v);
9251         },
9252         /**
9253          * The error text to display when the url validation function returns false
9254          * @type String
9255          */
9256         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9257         
9258         /**
9259          * The function used to validate alpha values
9260          * @param {String} value The value
9261          */
9262         'alpha' : function(v){
9263             return alpha.test(v);
9264         },
9265         /**
9266          * The error text to display when the alpha validation function returns false
9267          * @type String
9268          */
9269         'alphaText' : 'This field should only contain letters and _',
9270         /**
9271          * The keystroke filter mask to be applied on alpha input
9272          * @type RegExp
9273          */
9274         'alphaMask' : /[a-z_]/i,
9275
9276         /**
9277          * The function used to validate alphanumeric values
9278          * @param {String} value The value
9279          */
9280         'alphanum' : function(v){
9281             return alphanum.test(v);
9282         },
9283         /**
9284          * The error text to display when the alphanumeric validation function returns false
9285          * @type String
9286          */
9287         'alphanumText' : 'This field should only contain letters, numbers and _',
9288         /**
9289          * The keystroke filter mask to be applied on alphanumeric input
9290          * @type RegExp
9291          */
9292         'alphanumMask' : /[a-z0-9_]/i
9293     };
9294 }();/*
9295  * - LGPL
9296  *
9297  * Input
9298  * 
9299  */
9300
9301 /**
9302  * @class Roo.bootstrap.Input
9303  * @extends Roo.bootstrap.Component
9304  * Bootstrap Input class
9305  * @cfg {Boolean} disabled is it disabled
9306  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9307  * @cfg {String} name name of the input
9308  * @cfg {string} fieldLabel - the label associated
9309  * @cfg {string} placeholder - placeholder to put in text.
9310  * @cfg {string}  before - input group add on before
9311  * @cfg {string} after - input group add on after
9312  * @cfg {string} size - (lg|sm) or leave empty..
9313  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9314  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9315  * @cfg {Number} md colspan out of 12 for computer-sized screens
9316  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9317  * @cfg {string} value default value of the input
9318  * @cfg {Number} labelWidth set the width of label 
9319  * @cfg {Number} labellg set the width of label (1-12)
9320  * @cfg {Number} labelmd set the width of label (1-12)
9321  * @cfg {Number} labelsm set the width of label (1-12)
9322  * @cfg {Number} labelxs set the width of label (1-12)
9323  * @cfg {String} labelAlign (top|left)
9324  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9325  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9326  * @cfg {String} indicatorpos (left|right) default left
9327  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9328  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9329
9330  * @cfg {String} align (left|center|right) Default left
9331  * @cfg {Boolean} forceFeedback (true|false) Default false
9332  * 
9333  * @constructor
9334  * Create a new Input
9335  * @param {Object} config The config object
9336  */
9337
9338 Roo.bootstrap.Input = function(config){
9339     
9340     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9341     
9342     this.addEvents({
9343         /**
9344          * @event focus
9345          * Fires when this field receives input focus.
9346          * @param {Roo.form.Field} this
9347          */
9348         focus : true,
9349         /**
9350          * @event blur
9351          * Fires when this field loses input focus.
9352          * @param {Roo.form.Field} this
9353          */
9354         blur : true,
9355         /**
9356          * @event specialkey
9357          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9358          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9359          * @param {Roo.form.Field} this
9360          * @param {Roo.EventObject} e The event object
9361          */
9362         specialkey : true,
9363         /**
9364          * @event change
9365          * Fires just before the field blurs if the field value has changed.
9366          * @param {Roo.form.Field} this
9367          * @param {Mixed} newValue The new value
9368          * @param {Mixed} oldValue The original value
9369          */
9370         change : true,
9371         /**
9372          * @event invalid
9373          * Fires after the field has been marked as invalid.
9374          * @param {Roo.form.Field} this
9375          * @param {String} msg The validation message
9376          */
9377         invalid : true,
9378         /**
9379          * @event valid
9380          * Fires after the field has been validated with no errors.
9381          * @param {Roo.form.Field} this
9382          */
9383         valid : true,
9384          /**
9385          * @event keyup
9386          * Fires after the key up
9387          * @param {Roo.form.Field} this
9388          * @param {Roo.EventObject}  e The event Object
9389          */
9390         keyup : true
9391     });
9392 };
9393
9394 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9395      /**
9396      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9397       automatic validation (defaults to "keyup").
9398      */
9399     validationEvent : "keyup",
9400      /**
9401      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9402      */
9403     validateOnBlur : true,
9404     /**
9405      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9406      */
9407     validationDelay : 250,
9408      /**
9409      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9410      */
9411     focusClass : "x-form-focus",  // not needed???
9412     
9413        
9414     /**
9415      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9416      */
9417     invalidClass : "has-warning",
9418     
9419     /**
9420      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9421      */
9422     validClass : "has-success",
9423     
9424     /**
9425      * @cfg {Boolean} hasFeedback (true|false) default true
9426      */
9427     hasFeedback : true,
9428     
9429     /**
9430      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9431      */
9432     invalidFeedbackClass : "glyphicon-warning-sign",
9433     
9434     /**
9435      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9436      */
9437     validFeedbackClass : "glyphicon-ok",
9438     
9439     /**
9440      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9441      */
9442     selectOnFocus : false,
9443     
9444      /**
9445      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9446      */
9447     maskRe : null,
9448        /**
9449      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9450      */
9451     vtype : null,
9452     
9453       /**
9454      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9455      */
9456     disableKeyFilter : false,
9457     
9458        /**
9459      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9460      */
9461     disabled : false,
9462      /**
9463      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9464      */
9465     allowBlank : true,
9466     /**
9467      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9468      */
9469     blankText : "Please complete this mandatory field",
9470     
9471      /**
9472      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9473      */
9474     minLength : 0,
9475     /**
9476      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9477      */
9478     maxLength : Number.MAX_VALUE,
9479     /**
9480      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9481      */
9482     minLengthText : "The minimum length for this field is {0}",
9483     /**
9484      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9485      */
9486     maxLengthText : "The maximum length for this field is {0}",
9487   
9488     
9489     /**
9490      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9491      * If available, this function will be called only after the basic validators all return true, and will be passed the
9492      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9493      */
9494     validator : null,
9495     /**
9496      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9497      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9498      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9499      */
9500     regex : null,
9501     /**
9502      * @cfg {String} regexText -- Depricated - use Invalid Text
9503      */
9504     regexText : "",
9505     
9506     /**
9507      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9508      */
9509     invalidText : "",
9510     
9511     
9512     
9513     autocomplete: false,
9514     
9515     
9516     fieldLabel : '',
9517     inputType : 'text',
9518     
9519     name : false,
9520     placeholder: false,
9521     before : false,
9522     after : false,
9523     size : false,
9524     hasFocus : false,
9525     preventMark: false,
9526     isFormField : true,
9527     value : '',
9528     labelWidth : 2,
9529     labelAlign : false,
9530     readOnly : false,
9531     align : false,
9532     formatedValue : false,
9533     forceFeedback : false,
9534     
9535     indicatorpos : 'left',
9536     
9537     labellg : 0,
9538     labelmd : 0,
9539     labelsm : 0,
9540     labelxs : 0,
9541     
9542     capture : '',
9543     accept : '',
9544     
9545     parentLabelAlign : function()
9546     {
9547         var parent = this;
9548         while (parent.parent()) {
9549             parent = parent.parent();
9550             if (typeof(parent.labelAlign) !='undefined') {
9551                 return parent.labelAlign;
9552             }
9553         }
9554         return 'left';
9555         
9556     },
9557     
9558     getAutoCreate : function()
9559     {
9560         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9561         
9562         var id = Roo.id();
9563         
9564         var cfg = {};
9565         
9566         if(this.inputType != 'hidden'){
9567             cfg.cls = 'form-group' //input-group
9568         }
9569         
9570         var input =  {
9571             tag: 'input',
9572             id : id,
9573             type : this.inputType,
9574             value : this.value,
9575             cls : 'form-control',
9576             placeholder : this.placeholder || '',
9577             autocomplete : this.autocomplete || 'new-password'
9578         };
9579         
9580         if(this.capture.length){
9581             input.capture = this.capture;
9582         }
9583         
9584         if(this.accept.length){
9585             input.accept = this.accept + "/*";
9586         }
9587         
9588         if(this.align){
9589             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9590         }
9591         
9592         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9593             input.maxLength = this.maxLength;
9594         }
9595         
9596         if (this.disabled) {
9597             input.disabled=true;
9598         }
9599         
9600         if (this.readOnly) {
9601             input.readonly=true;
9602         }
9603         
9604         if (this.name) {
9605             input.name = this.name;
9606         }
9607         
9608         if (this.size) {
9609             input.cls += ' input-' + this.size;
9610         }
9611         
9612         var settings=this;
9613         ['xs','sm','md','lg'].map(function(size){
9614             if (settings[size]) {
9615                 cfg.cls += ' col-' + size + '-' + settings[size];
9616             }
9617         });
9618         
9619         var inputblock = input;
9620         
9621         var feedback = {
9622             tag: 'span',
9623             cls: 'glyphicon form-control-feedback'
9624         };
9625             
9626         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9627             
9628             inputblock = {
9629                 cls : 'has-feedback',
9630                 cn :  [
9631                     input,
9632                     feedback
9633                 ] 
9634             };  
9635         }
9636         
9637         if (this.before || this.after) {
9638             
9639             inputblock = {
9640                 cls : 'input-group',
9641                 cn :  [] 
9642             };
9643             
9644             if (this.before && typeof(this.before) == 'string') {
9645                 
9646                 inputblock.cn.push({
9647                     tag :'span',
9648                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9649                     html : this.before
9650                 });
9651             }
9652             if (this.before && typeof(this.before) == 'object') {
9653                 this.before = Roo.factory(this.before);
9654                 
9655                 inputblock.cn.push({
9656                     tag :'span',
9657                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9658                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9659                 });
9660             }
9661             
9662             inputblock.cn.push(input);
9663             
9664             if (this.after && typeof(this.after) == 'string') {
9665                 inputblock.cn.push({
9666                     tag :'span',
9667                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9668                     html : this.after
9669                 });
9670             }
9671             if (this.after && typeof(this.after) == 'object') {
9672                 this.after = Roo.factory(this.after);
9673                 
9674                 inputblock.cn.push({
9675                     tag :'span',
9676                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9677                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9678                 });
9679             }
9680             
9681             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9682                 inputblock.cls += ' has-feedback';
9683                 inputblock.cn.push(feedback);
9684             }
9685         };
9686         var indicator = {
9687             tag : 'i',
9688             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9689             tooltip : 'This field is required'
9690         };
9691         if (Roo.bootstrap.version == 4) {
9692             indicator = {
9693                 tag : 'i',
9694                 style : 'display-none'
9695             };
9696         }
9697         if (align ==='left' && this.fieldLabel.length) {
9698             
9699             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9700             
9701             cfg.cn = [
9702                 indicator,
9703                 {
9704                     tag: 'label',
9705                     'for' :  id,
9706                     cls : 'control-label col-form-label',
9707                     html : this.fieldLabel
9708
9709                 },
9710                 {
9711                     cls : "", 
9712                     cn: [
9713                         inputblock
9714                     ]
9715                 }
9716             ];
9717             
9718             var labelCfg = cfg.cn[1];
9719             var contentCfg = cfg.cn[2];
9720             
9721             if(this.indicatorpos == 'right'){
9722                 cfg.cn = [
9723                     {
9724                         tag: 'label',
9725                         'for' :  id,
9726                         cls : 'control-label col-form-label',
9727                         cn : [
9728                             {
9729                                 tag : 'span',
9730                                 html : this.fieldLabel
9731                             },
9732                             indicator
9733                         ]
9734                     },
9735                     {
9736                         cls : "",
9737                         cn: [
9738                             inputblock
9739                         ]
9740                     }
9741
9742                 ];
9743                 
9744                 labelCfg = cfg.cn[0];
9745                 contentCfg = cfg.cn[1];
9746             
9747             }
9748             
9749             if(this.labelWidth > 12){
9750                 labelCfg.style = "width: " + this.labelWidth + 'px';
9751             }
9752             
9753             if(this.labelWidth < 13 && this.labelmd == 0){
9754                 this.labelmd = this.labelWidth;
9755             }
9756             
9757             if(this.labellg > 0){
9758                 labelCfg.cls += ' col-lg-' + this.labellg;
9759                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9760             }
9761             
9762             if(this.labelmd > 0){
9763                 labelCfg.cls += ' col-md-' + this.labelmd;
9764                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9765             }
9766             
9767             if(this.labelsm > 0){
9768                 labelCfg.cls += ' col-sm-' + this.labelsm;
9769                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9770             }
9771             
9772             if(this.labelxs > 0){
9773                 labelCfg.cls += ' col-xs-' + this.labelxs;
9774                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9775             }
9776             
9777             
9778         } else if ( this.fieldLabel.length) {
9779                 
9780             cfg.cn = [
9781                 {
9782                     tag : 'i',
9783                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9784                     tooltip : 'This field is required'
9785                 },
9786                 {
9787                     tag: 'label',
9788                    //cls : 'input-group-addon',
9789                     html : this.fieldLabel
9790
9791                 },
9792
9793                inputblock
9794
9795            ];
9796            
9797            if(this.indicatorpos == 'right'){
9798                 
9799                 cfg.cn = [
9800                     {
9801                         tag: 'label',
9802                        //cls : 'input-group-addon',
9803                         html : this.fieldLabel
9804
9805                     },
9806                     {
9807                         tag : 'i',
9808                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9809                         tooltip : 'This field is required'
9810                     },
9811
9812                    inputblock
9813
9814                ];
9815
9816             }
9817
9818         } else {
9819             
9820             cfg.cn = [
9821
9822                     inputblock
9823
9824             ];
9825                 
9826                 
9827         };
9828         
9829         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9830            cfg.cls += ' navbar-form';
9831         }
9832         
9833         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9834             // on BS4 we do this only if not form 
9835             cfg.cls += ' navbar-form';
9836             cfg.tag = 'li';
9837         }
9838         
9839         return cfg;
9840         
9841     },
9842     /**
9843      * return the real input element.
9844      */
9845     inputEl: function ()
9846     {
9847         return this.el.select('input.form-control',true).first();
9848     },
9849     
9850     tooltipEl : function()
9851     {
9852         return this.inputEl();
9853     },
9854     
9855     indicatorEl : function()
9856     {
9857         if (Roo.bootstrap.version == 4) {
9858             return false; // not enabled in v4 yet.
9859         }
9860         
9861         var indicator = this.el.select('i.roo-required-indicator',true).first();
9862         
9863         if(!indicator){
9864             return false;
9865         }
9866         
9867         return indicator;
9868         
9869     },
9870     
9871     setDisabled : function(v)
9872     {
9873         var i  = this.inputEl().dom;
9874         if (!v) {
9875             i.removeAttribute('disabled');
9876             return;
9877             
9878         }
9879         i.setAttribute('disabled','true');
9880     },
9881     initEvents : function()
9882     {
9883           
9884         this.inputEl().on("keydown" , this.fireKey,  this);
9885         this.inputEl().on("focus", this.onFocus,  this);
9886         this.inputEl().on("blur", this.onBlur,  this);
9887         
9888         this.inputEl().relayEvent('keyup', this);
9889         
9890         this.indicator = this.indicatorEl();
9891         
9892         if(this.indicator){
9893             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9894         }
9895  
9896         // reference to original value for reset
9897         this.originalValue = this.getValue();
9898         //Roo.form.TextField.superclass.initEvents.call(this);
9899         if(this.validationEvent == 'keyup'){
9900             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9901             this.inputEl().on('keyup', this.filterValidation, this);
9902         }
9903         else if(this.validationEvent !== false){
9904             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9905         }
9906         
9907         if(this.selectOnFocus){
9908             this.on("focus", this.preFocus, this);
9909             
9910         }
9911         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9912             this.inputEl().on("keypress", this.filterKeys, this);
9913         } else {
9914             this.inputEl().relayEvent('keypress', this);
9915         }
9916        /* if(this.grow){
9917             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9918             this.el.on("click", this.autoSize,  this);
9919         }
9920         */
9921         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9922             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9923         }
9924         
9925         if (typeof(this.before) == 'object') {
9926             this.before.render(this.el.select('.roo-input-before',true).first());
9927         }
9928         if (typeof(this.after) == 'object') {
9929             this.after.render(this.el.select('.roo-input-after',true).first());
9930         }
9931         
9932         this.inputEl().on('change', this.onChange, this);
9933         
9934     },
9935     filterValidation : function(e){
9936         if(!e.isNavKeyPress()){
9937             this.validationTask.delay(this.validationDelay);
9938         }
9939     },
9940      /**
9941      * Validates the field value
9942      * @return {Boolean} True if the value is valid, else false
9943      */
9944     validate : function(){
9945         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9946         if(this.disabled || this.validateValue(this.getRawValue())){
9947             this.markValid();
9948             return true;
9949         }
9950         
9951         this.markInvalid();
9952         return false;
9953     },
9954     
9955     
9956     /**
9957      * Validates a value according to the field's validation rules and marks the field as invalid
9958      * if the validation fails
9959      * @param {Mixed} value The value to validate
9960      * @return {Boolean} True if the value is valid, else false
9961      */
9962     validateValue : function(value)
9963     {
9964         if(this.getVisibilityEl().hasClass('hidden')){
9965             return true;
9966         }
9967         
9968         if(value.length < 1)  { // if it's blank
9969             if(this.allowBlank){
9970                 return true;
9971             }
9972             return false;
9973         }
9974         
9975         if(value.length < this.minLength){
9976             return false;
9977         }
9978         if(value.length > this.maxLength){
9979             return false;
9980         }
9981         if(this.vtype){
9982             var vt = Roo.form.VTypes;
9983             if(!vt[this.vtype](value, this)){
9984                 return false;
9985             }
9986         }
9987         if(typeof this.validator == "function"){
9988             var msg = this.validator(value);
9989             if(msg !== true){
9990                 return false;
9991             }
9992             if (typeof(msg) == 'string') {
9993                 this.invalidText = msg;
9994             }
9995         }
9996         
9997         if(this.regex && !this.regex.test(value)){
9998             return false;
9999         }
10000         
10001         return true;
10002     },
10003     
10004      // private
10005     fireKey : function(e){
10006         //Roo.log('field ' + e.getKey());
10007         if(e.isNavKeyPress()){
10008             this.fireEvent("specialkey", this, e);
10009         }
10010     },
10011     focus : function (selectText){
10012         if(this.rendered){
10013             this.inputEl().focus();
10014             if(selectText === true){
10015                 this.inputEl().dom.select();
10016             }
10017         }
10018         return this;
10019     } ,
10020     
10021     onFocus : function(){
10022         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10023            // this.el.addClass(this.focusClass);
10024         }
10025         if(!this.hasFocus){
10026             this.hasFocus = true;
10027             this.startValue = this.getValue();
10028             this.fireEvent("focus", this);
10029         }
10030     },
10031     
10032     beforeBlur : Roo.emptyFn,
10033
10034     
10035     // private
10036     onBlur : function(){
10037         this.beforeBlur();
10038         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10039             //this.el.removeClass(this.focusClass);
10040         }
10041         this.hasFocus = false;
10042         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10043             this.validate();
10044         }
10045         var v = this.getValue();
10046         if(String(v) !== String(this.startValue)){
10047             this.fireEvent('change', this, v, this.startValue);
10048         }
10049         this.fireEvent("blur", this);
10050     },
10051     
10052     onChange : function(e)
10053     {
10054         var v = this.getValue();
10055         if(String(v) !== String(this.startValue)){
10056             this.fireEvent('change', this, v, this.startValue);
10057         }
10058         
10059     },
10060     
10061     /**
10062      * Resets the current field value to the originally loaded value and clears any validation messages
10063      */
10064     reset : function(){
10065         this.setValue(this.originalValue);
10066         this.validate();
10067     },
10068      /**
10069      * Returns the name of the field
10070      * @return {Mixed} name The name field
10071      */
10072     getName: function(){
10073         return this.name;
10074     },
10075      /**
10076      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10077      * @return {Mixed} value The field value
10078      */
10079     getValue : function(){
10080         
10081         var v = this.inputEl().getValue();
10082         
10083         return v;
10084     },
10085     /**
10086      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10087      * @return {Mixed} value The field value
10088      */
10089     getRawValue : function(){
10090         var v = this.inputEl().getValue();
10091         
10092         return v;
10093     },
10094     
10095     /**
10096      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10097      * @param {Mixed} value The value to set
10098      */
10099     setRawValue : function(v){
10100         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10101     },
10102     
10103     selectText : function(start, end){
10104         var v = this.getRawValue();
10105         if(v.length > 0){
10106             start = start === undefined ? 0 : start;
10107             end = end === undefined ? v.length : end;
10108             var d = this.inputEl().dom;
10109             if(d.setSelectionRange){
10110                 d.setSelectionRange(start, end);
10111             }else if(d.createTextRange){
10112                 var range = d.createTextRange();
10113                 range.moveStart("character", start);
10114                 range.moveEnd("character", v.length-end);
10115                 range.select();
10116             }
10117         }
10118     },
10119     
10120     /**
10121      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10122      * @param {Mixed} value The value to set
10123      */
10124     setValue : function(v){
10125         this.value = v;
10126         if(this.rendered){
10127             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10128             this.validate();
10129         }
10130     },
10131     
10132     /*
10133     processValue : function(value){
10134         if(this.stripCharsRe){
10135             var newValue = value.replace(this.stripCharsRe, '');
10136             if(newValue !== value){
10137                 this.setRawValue(newValue);
10138                 return newValue;
10139             }
10140         }
10141         return value;
10142     },
10143   */
10144     preFocus : function(){
10145         
10146         if(this.selectOnFocus){
10147             this.inputEl().dom.select();
10148         }
10149     },
10150     filterKeys : function(e){
10151         var k = e.getKey();
10152         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10153             return;
10154         }
10155         var c = e.getCharCode(), cc = String.fromCharCode(c);
10156         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10157             return;
10158         }
10159         if(!this.maskRe.test(cc)){
10160             e.stopEvent();
10161         }
10162     },
10163      /**
10164      * Clear any invalid styles/messages for this field
10165      */
10166     clearInvalid : function(){
10167         
10168         if(!this.el || this.preventMark){ // not rendered
10169             return;
10170         }
10171         
10172         
10173         this.el.removeClass([this.invalidClass, 'is-invalid']);
10174         
10175         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10176             
10177             var feedback = this.el.select('.form-control-feedback', true).first();
10178             
10179             if(feedback){
10180                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10181             }
10182             
10183         }
10184         
10185         if(this.indicator){
10186             this.indicator.removeClass('visible');
10187             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10188         }
10189         
10190         this.fireEvent('valid', this);
10191     },
10192     
10193      /**
10194      * Mark this field as valid
10195      */
10196     markValid : function()
10197     {
10198         if(!this.el  || this.preventMark){ // not rendered...
10199             return;
10200         }
10201         
10202         this.el.removeClass([this.invalidClass, this.validClass]);
10203         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10204
10205         var feedback = this.el.select('.form-control-feedback', true).first();
10206             
10207         if(feedback){
10208             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10209         }
10210         
10211         if(this.indicator){
10212             this.indicator.removeClass('visible');
10213             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10214         }
10215         
10216         if(this.disabled){
10217             return;
10218         }
10219         
10220         if(this.allowBlank && !this.getRawValue().length){
10221             return;
10222         }
10223         if (Roo.bootstrap.version == 3) {
10224             this.el.addClass(this.validClass);
10225         } else {
10226             this.inputEl().addClass('is-valid');
10227         }
10228
10229         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10230             
10231             var feedback = this.el.select('.form-control-feedback', true).first();
10232             
10233             if(feedback){
10234                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10235                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10236             }
10237             
10238         }
10239         
10240         this.fireEvent('valid', this);
10241     },
10242     
10243      /**
10244      * Mark this field as invalid
10245      * @param {String} msg The validation message
10246      */
10247     markInvalid : function(msg)
10248     {
10249         if(!this.el  || this.preventMark){ // not rendered
10250             return;
10251         }
10252         
10253         this.el.removeClass([this.invalidClass, this.validClass]);
10254         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10255         
10256         var feedback = this.el.select('.form-control-feedback', true).first();
10257             
10258         if(feedback){
10259             this.el.select('.form-control-feedback', true).first().removeClass(
10260                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10261         }
10262
10263         if(this.disabled){
10264             return;
10265         }
10266         
10267         if(this.allowBlank && !this.getRawValue().length){
10268             return;
10269         }
10270         
10271         if(this.indicator){
10272             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10273             this.indicator.addClass('visible');
10274         }
10275         if (Roo.bootstrap.version == 3) {
10276             this.el.addClass(this.invalidClass);
10277         } else {
10278             this.inputEl().addClass('is-invalid');
10279         }
10280         
10281         
10282         
10283         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10284             
10285             var feedback = this.el.select('.form-control-feedback', true).first();
10286             
10287             if(feedback){
10288                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10289                 
10290                 if(this.getValue().length || this.forceFeedback){
10291                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10292                 }
10293                 
10294             }
10295             
10296         }
10297         
10298         this.fireEvent('invalid', this, msg);
10299     },
10300     // private
10301     SafariOnKeyDown : function(event)
10302     {
10303         // this is a workaround for a password hang bug on chrome/ webkit.
10304         if (this.inputEl().dom.type != 'password') {
10305             return;
10306         }
10307         
10308         var isSelectAll = false;
10309         
10310         if(this.inputEl().dom.selectionEnd > 0){
10311             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10312         }
10313         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10314             event.preventDefault();
10315             this.setValue('');
10316             return;
10317         }
10318         
10319         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10320             
10321             event.preventDefault();
10322             // this is very hacky as keydown always get's upper case.
10323             //
10324             var cc = String.fromCharCode(event.getCharCode());
10325             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10326             
10327         }
10328     },
10329     adjustWidth : function(tag, w){
10330         tag = tag.toLowerCase();
10331         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10332             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10333                 if(tag == 'input'){
10334                     return w + 2;
10335                 }
10336                 if(tag == 'textarea'){
10337                     return w-2;
10338                 }
10339             }else if(Roo.isOpera){
10340                 if(tag == 'input'){
10341                     return w + 2;
10342                 }
10343                 if(tag == 'textarea'){
10344                     return w-2;
10345                 }
10346             }
10347         }
10348         return w;
10349     },
10350     
10351     setFieldLabel : function(v)
10352     {
10353         if(!this.rendered){
10354             return;
10355         }
10356         
10357         if(this.indicatorEl()){
10358             var ar = this.el.select('label > span',true);
10359             
10360             if (ar.elements.length) {
10361                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10362                 this.fieldLabel = v;
10363                 return;
10364             }
10365             
10366             var br = this.el.select('label',true);
10367             
10368             if(br.elements.length) {
10369                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10370                 this.fieldLabel = v;
10371                 return;
10372             }
10373             
10374             Roo.log('Cannot Found any of label > span || label in input');
10375             return;
10376         }
10377         
10378         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10379         this.fieldLabel = v;
10380         
10381         
10382     }
10383 });
10384
10385  
10386 /*
10387  * - LGPL
10388  *
10389  * Input
10390  * 
10391  */
10392
10393 /**
10394  * @class Roo.bootstrap.TextArea
10395  * @extends Roo.bootstrap.Input
10396  * Bootstrap TextArea class
10397  * @cfg {Number} cols Specifies the visible width of a text area
10398  * @cfg {Number} rows Specifies the visible number of lines in a text area
10399  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10400  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10401  * @cfg {string} html text
10402  * 
10403  * @constructor
10404  * Create a new TextArea
10405  * @param {Object} config The config object
10406  */
10407
10408 Roo.bootstrap.TextArea = function(config){
10409     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10410    
10411 };
10412
10413 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10414      
10415     cols : false,
10416     rows : 5,
10417     readOnly : false,
10418     warp : 'soft',
10419     resize : false,
10420     value: false,
10421     html: false,
10422     
10423     getAutoCreate : function(){
10424         
10425         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10426         
10427         var id = Roo.id();
10428         
10429         var cfg = {};
10430         
10431         if(this.inputType != 'hidden'){
10432             cfg.cls = 'form-group' //input-group
10433         }
10434         
10435         var input =  {
10436             tag: 'textarea',
10437             id : id,
10438             warp : this.warp,
10439             rows : this.rows,
10440             value : this.value || '',
10441             html: this.html || '',
10442             cls : 'form-control',
10443             placeholder : this.placeholder || '' 
10444             
10445         };
10446         
10447         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10448             input.maxLength = this.maxLength;
10449         }
10450         
10451         if(this.resize){
10452             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10453         }
10454         
10455         if(this.cols){
10456             input.cols = this.cols;
10457         }
10458         
10459         if (this.readOnly) {
10460             input.readonly = true;
10461         }
10462         
10463         if (this.name) {
10464             input.name = this.name;
10465         }
10466         
10467         if (this.size) {
10468             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10469         }
10470         
10471         var settings=this;
10472         ['xs','sm','md','lg'].map(function(size){
10473             if (settings[size]) {
10474                 cfg.cls += ' col-' + size + '-' + settings[size];
10475             }
10476         });
10477         
10478         var inputblock = input;
10479         
10480         if(this.hasFeedback && !this.allowBlank){
10481             
10482             var feedback = {
10483                 tag: 'span',
10484                 cls: 'glyphicon form-control-feedback'
10485             };
10486
10487             inputblock = {
10488                 cls : 'has-feedback',
10489                 cn :  [
10490                     input,
10491                     feedback
10492                 ] 
10493             };  
10494         }
10495         
10496         
10497         if (this.before || this.after) {
10498             
10499             inputblock = {
10500                 cls : 'input-group',
10501                 cn :  [] 
10502             };
10503             if (this.before) {
10504                 inputblock.cn.push({
10505                     tag :'span',
10506                     cls : 'input-group-addon',
10507                     html : this.before
10508                 });
10509             }
10510             
10511             inputblock.cn.push(input);
10512             
10513             if(this.hasFeedback && !this.allowBlank){
10514                 inputblock.cls += ' has-feedback';
10515                 inputblock.cn.push(feedback);
10516             }
10517             
10518             if (this.after) {
10519                 inputblock.cn.push({
10520                     tag :'span',
10521                     cls : 'input-group-addon',
10522                     html : this.after
10523                 });
10524             }
10525             
10526         }
10527         
10528         if (align ==='left' && this.fieldLabel.length) {
10529             cfg.cn = [
10530                 {
10531                     tag: 'label',
10532                     'for' :  id,
10533                     cls : 'control-label',
10534                     html : this.fieldLabel
10535                 },
10536                 {
10537                     cls : "",
10538                     cn: [
10539                         inputblock
10540                     ]
10541                 }
10542
10543             ];
10544             
10545             if(this.labelWidth > 12){
10546                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10547             }
10548
10549             if(this.labelWidth < 13 && this.labelmd == 0){
10550                 this.labelmd = this.labelWidth;
10551             }
10552
10553             if(this.labellg > 0){
10554                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10555                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10556             }
10557
10558             if(this.labelmd > 0){
10559                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10560                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10561             }
10562
10563             if(this.labelsm > 0){
10564                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10565                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10566             }
10567
10568             if(this.labelxs > 0){
10569                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10570                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10571             }
10572             
10573         } else if ( this.fieldLabel.length) {
10574             cfg.cn = [
10575
10576                {
10577                    tag: 'label',
10578                    //cls : 'input-group-addon',
10579                    html : this.fieldLabel
10580
10581                },
10582
10583                inputblock
10584
10585            ];
10586
10587         } else {
10588
10589             cfg.cn = [
10590
10591                 inputblock
10592
10593             ];
10594                 
10595         }
10596         
10597         if (this.disabled) {
10598             input.disabled=true;
10599         }
10600         
10601         return cfg;
10602         
10603     },
10604     /**
10605      * return the real textarea element.
10606      */
10607     inputEl: function ()
10608     {
10609         return this.el.select('textarea.form-control',true).first();
10610     },
10611     
10612     /**
10613      * Clear any invalid styles/messages for this field
10614      */
10615     clearInvalid : function()
10616     {
10617         
10618         if(!this.el || this.preventMark){ // not rendered
10619             return;
10620         }
10621         
10622         var label = this.el.select('label', true).first();
10623         var icon = this.el.select('i.fa-star', true).first();
10624         
10625         if(label && icon){
10626             icon.remove();
10627         }
10628         this.el.removeClass( this.validClass);
10629         this.inputEl().removeClass('is-invalid');
10630          
10631         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10632             
10633             var feedback = this.el.select('.form-control-feedback', true).first();
10634             
10635             if(feedback){
10636                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10637             }
10638             
10639         }
10640         
10641         this.fireEvent('valid', this);
10642     },
10643     
10644      /**
10645      * Mark this field as valid
10646      */
10647     markValid : function()
10648     {
10649         if(!this.el  || this.preventMark){ // not rendered
10650             return;
10651         }
10652         
10653         this.el.removeClass([this.invalidClass, this.validClass]);
10654         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10655         
10656         var feedback = this.el.select('.form-control-feedback', true).first();
10657             
10658         if(feedback){
10659             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10660         }
10661
10662         if(this.disabled || this.allowBlank){
10663             return;
10664         }
10665         
10666         var label = this.el.select('label', true).first();
10667         var icon = this.el.select('i.fa-star', true).first();
10668         
10669         if(label && icon){
10670             icon.remove();
10671         }
10672         if (Roo.bootstrap.version == 3) {
10673             this.el.addClass(this.validClass);
10674         } else {
10675             this.inputEl().addClass('is-valid');
10676         }
10677         
10678         
10679         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10680             
10681             var feedback = this.el.select('.form-control-feedback', true).first();
10682             
10683             if(feedback){
10684                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10685                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10686             }
10687             
10688         }
10689         
10690         this.fireEvent('valid', this);
10691     },
10692     
10693      /**
10694      * Mark this field as invalid
10695      * @param {String} msg The validation message
10696      */
10697     markInvalid : function(msg)
10698     {
10699         if(!this.el  || this.preventMark){ // not rendered
10700             return;
10701         }
10702         
10703         this.el.removeClass([this.invalidClass, this.validClass]);
10704         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10705         
10706         var feedback = this.el.select('.form-control-feedback', true).first();
10707             
10708         if(feedback){
10709             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10710         }
10711
10712         if(this.disabled || this.allowBlank){
10713             return;
10714         }
10715         
10716         var label = this.el.select('label', true).first();
10717         var icon = this.el.select('i.fa-star', true).first();
10718         
10719         if(!this.getValue().length && label && !icon){
10720             this.el.createChild({
10721                 tag : 'i',
10722                 cls : 'text-danger fa fa-lg fa-star',
10723                 tooltip : 'This field is required',
10724                 style : 'margin-right:5px;'
10725             }, label, true);
10726         }
10727         
10728         if (Roo.bootstrap.version == 3) {
10729             this.el.addClass(this.invalidClass);
10730         } else {
10731             this.inputEl().addClass('is-invalid');
10732         }
10733         
10734         // fixme ... this may be depricated need to test..
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             var feedback = this.el.select('.form-control-feedback', true).first();
10738             
10739             if(feedback){
10740                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10741                 
10742                 if(this.getValue().length || this.forceFeedback){
10743                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10744                 }
10745                 
10746             }
10747             
10748         }
10749         
10750         this.fireEvent('invalid', this, msg);
10751     }
10752 });
10753
10754  
10755 /*
10756  * - LGPL
10757  *
10758  * trigger field - base class for combo..
10759  * 
10760  */
10761  
10762 /**
10763  * @class Roo.bootstrap.TriggerField
10764  * @extends Roo.bootstrap.Input
10765  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10766  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10767  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10768  * for which you can provide a custom implementation.  For example:
10769  * <pre><code>
10770 var trigger = new Roo.bootstrap.TriggerField();
10771 trigger.onTriggerClick = myTriggerFn;
10772 trigger.applyTo('my-field');
10773 </code></pre>
10774  *
10775  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10776  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10777  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10778  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10779  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10780
10781  * @constructor
10782  * Create a new TriggerField.
10783  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10784  * to the base TextField)
10785  */
10786 Roo.bootstrap.TriggerField = function(config){
10787     this.mimicing = false;
10788     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10789 };
10790
10791 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10792     /**
10793      * @cfg {String} triggerClass A CSS class to apply to the trigger
10794      */
10795      /**
10796      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10797      */
10798     hideTrigger:false,
10799
10800     /**
10801      * @cfg {Boolean} removable (true|false) special filter default false
10802      */
10803     removable : false,
10804     
10805     /** @cfg {Boolean} grow @hide */
10806     /** @cfg {Number} growMin @hide */
10807     /** @cfg {Number} growMax @hide */
10808
10809     /**
10810      * @hide 
10811      * @method
10812      */
10813     autoSize: Roo.emptyFn,
10814     // private
10815     monitorTab : true,
10816     // private
10817     deferHeight : true,
10818
10819     
10820     actionMode : 'wrap',
10821     
10822     caret : false,
10823     
10824     
10825     getAutoCreate : function(){
10826        
10827         var align = this.labelAlign || this.parentLabelAlign();
10828         
10829         var id = Roo.id();
10830         
10831         var cfg = {
10832             cls: 'form-group' //input-group
10833         };
10834         
10835         
10836         var input =  {
10837             tag: 'input',
10838             id : id,
10839             type : this.inputType,
10840             cls : 'form-control',
10841             autocomplete: 'new-password',
10842             placeholder : this.placeholder || '' 
10843             
10844         };
10845         if (this.name) {
10846             input.name = this.name;
10847         }
10848         if (this.size) {
10849             input.cls += ' input-' + this.size;
10850         }
10851         
10852         if (this.disabled) {
10853             input.disabled=true;
10854         }
10855         
10856         var inputblock = input;
10857         
10858         if(this.hasFeedback && !this.allowBlank){
10859             
10860             var feedback = {
10861                 tag: 'span',
10862                 cls: 'glyphicon form-control-feedback'
10863             };
10864             
10865             if(this.removable && !this.editable && !this.tickable){
10866                 inputblock = {
10867                     cls : 'has-feedback',
10868                     cn :  [
10869                         inputblock,
10870                         {
10871                             tag: 'button',
10872                             html : 'x',
10873                             cls : 'roo-combo-removable-btn close'
10874                         },
10875                         feedback
10876                     ] 
10877                 };
10878             } else {
10879                 inputblock = {
10880                     cls : 'has-feedback',
10881                     cn :  [
10882                         inputblock,
10883                         feedback
10884                     ] 
10885                 };
10886             }
10887
10888         } else {
10889             if(this.removable && !this.editable && !this.tickable){
10890                 inputblock = {
10891                     cls : 'roo-removable',
10892                     cn :  [
10893                         inputblock,
10894                         {
10895                             tag: 'button',
10896                             html : 'x',
10897                             cls : 'roo-combo-removable-btn close'
10898                         }
10899                     ] 
10900                 };
10901             }
10902         }
10903         
10904         if (this.before || this.after) {
10905             
10906             inputblock = {
10907                 cls : 'input-group',
10908                 cn :  [] 
10909             };
10910             if (this.before) {
10911                 inputblock.cn.push({
10912                     tag :'span',
10913                     cls : 'input-group-addon input-group-prepend input-group-text',
10914                     html : this.before
10915                 });
10916             }
10917             
10918             inputblock.cn.push(input);
10919             
10920             if(this.hasFeedback && !this.allowBlank){
10921                 inputblock.cls += ' has-feedback';
10922                 inputblock.cn.push(feedback);
10923             }
10924             
10925             if (this.after) {
10926                 inputblock.cn.push({
10927                     tag :'span',
10928                     cls : 'input-group-addon input-group-append input-group-text',
10929                     html : this.after
10930                 });
10931             }
10932             
10933         };
10934         
10935       
10936         
10937         var ibwrap = inputblock;
10938         
10939         if(this.multiple){
10940             ibwrap = {
10941                 tag: 'ul',
10942                 cls: 'roo-select2-choices',
10943                 cn:[
10944                     {
10945                         tag: 'li',
10946                         cls: 'roo-select2-search-field',
10947                         cn: [
10948
10949                             inputblock
10950                         ]
10951                     }
10952                 ]
10953             };
10954                 
10955         }
10956         
10957         var combobox = {
10958             cls: 'roo-select2-container input-group',
10959             cn: [
10960                  {
10961                     tag: 'input',
10962                     type : 'hidden',
10963                     cls: 'form-hidden-field'
10964                 },
10965                 ibwrap
10966             ]
10967         };
10968         
10969         if(!this.multiple && this.showToggleBtn){
10970             
10971             var caret = {
10972                         tag: 'span',
10973                         cls: 'caret'
10974              };
10975             if (this.caret != false) {
10976                 caret = {
10977                      tag: 'i',
10978                      cls: 'fa fa-' + this.caret
10979                 };
10980                 
10981             }
10982             
10983             combobox.cn.push({
10984                 tag :'span',
10985                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10986                 cn : [
10987                     Roo.bootstrap.version == 3 ? caret : '',
10988                     {
10989                         tag: 'span',
10990                         cls: 'combobox-clear',
10991                         cn  : [
10992                             {
10993                                 tag : 'i',
10994                                 cls: 'icon-remove'
10995                             }
10996                         ]
10997                     }
10998                 ]
10999
11000             })
11001         }
11002         
11003         if(this.multiple){
11004             combobox.cls += ' roo-select2-container-multi';
11005         }
11006          var indicator = {
11007             tag : 'i',
11008             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11009             tooltip : 'This field is required'
11010         };
11011         if (Roo.bootstrap.version == 4) {
11012             indicator = {
11013                 tag : 'i',
11014                 style : 'display:none'
11015             };
11016         }
11017         
11018         
11019         if (align ==='left' && this.fieldLabel.length) {
11020             
11021             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11022
11023             cfg.cn = [
11024                 indicator,
11025                 {
11026                     tag: 'label',
11027                     'for' :  id,
11028                     cls : 'control-label',
11029                     html : this.fieldLabel
11030
11031                 },
11032                 {
11033                     cls : "", 
11034                     cn: [
11035                         combobox
11036                     ]
11037                 }
11038
11039             ];
11040             
11041             var labelCfg = cfg.cn[1];
11042             var contentCfg = cfg.cn[2];
11043             
11044             if(this.indicatorpos == 'right'){
11045                 cfg.cn = [
11046                     {
11047                         tag: 'label',
11048                         'for' :  id,
11049                         cls : 'control-label',
11050                         cn : [
11051                             {
11052                                 tag : 'span',
11053                                 html : this.fieldLabel
11054                             },
11055                             indicator
11056                         ]
11057                     },
11058                     {
11059                         cls : "", 
11060                         cn: [
11061                             combobox
11062                         ]
11063                     }
11064
11065                 ];
11066                 
11067                 labelCfg = cfg.cn[0];
11068                 contentCfg = cfg.cn[1];
11069             }
11070             
11071             if(this.labelWidth > 12){
11072                 labelCfg.style = "width: " + this.labelWidth + 'px';
11073             }
11074             
11075             if(this.labelWidth < 13 && this.labelmd == 0){
11076                 this.labelmd = this.labelWidth;
11077             }
11078             
11079             if(this.labellg > 0){
11080                 labelCfg.cls += ' col-lg-' + this.labellg;
11081                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11082             }
11083             
11084             if(this.labelmd > 0){
11085                 labelCfg.cls += ' col-md-' + this.labelmd;
11086                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11087             }
11088             
11089             if(this.labelsm > 0){
11090                 labelCfg.cls += ' col-sm-' + this.labelsm;
11091                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11092             }
11093             
11094             if(this.labelxs > 0){
11095                 labelCfg.cls += ' col-xs-' + this.labelxs;
11096                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11097             }
11098             
11099         } else if ( this.fieldLabel.length) {
11100 //                Roo.log(" label");
11101             cfg.cn = [
11102                 indicator,
11103                {
11104                    tag: 'label',
11105                    //cls : 'input-group-addon',
11106                    html : this.fieldLabel
11107
11108                },
11109
11110                combobox
11111
11112             ];
11113             
11114             if(this.indicatorpos == 'right'){
11115                 
11116                 cfg.cn = [
11117                     {
11118                        tag: 'label',
11119                        cn : [
11120                            {
11121                                tag : 'span',
11122                                html : this.fieldLabel
11123                            },
11124                            indicator
11125                        ]
11126
11127                     },
11128                     combobox
11129
11130                 ];
11131
11132             }
11133
11134         } else {
11135             
11136 //                Roo.log(" no label && no align");
11137                 cfg = combobox
11138                      
11139                 
11140         }
11141         
11142         var settings=this;
11143         ['xs','sm','md','lg'].map(function(size){
11144             if (settings[size]) {
11145                 cfg.cls += ' col-' + size + '-' + settings[size];
11146             }
11147         });
11148         
11149         return cfg;
11150         
11151     },
11152     
11153     
11154     
11155     // private
11156     onResize : function(w, h){
11157 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11158 //        if(typeof w == 'number'){
11159 //            var x = w - this.trigger.getWidth();
11160 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11161 //            this.trigger.setStyle('left', x+'px');
11162 //        }
11163     },
11164
11165     // private
11166     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11167
11168     // private
11169     getResizeEl : function(){
11170         return this.inputEl();
11171     },
11172
11173     // private
11174     getPositionEl : function(){
11175         return this.inputEl();
11176     },
11177
11178     // private
11179     alignErrorIcon : function(){
11180         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11181     },
11182
11183     // private
11184     initEvents : function(){
11185         
11186         this.createList();
11187         
11188         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11189         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11190         if(!this.multiple && this.showToggleBtn){
11191             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11192             if(this.hideTrigger){
11193                 this.trigger.setDisplayed(false);
11194             }
11195             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11196         }
11197         
11198         if(this.multiple){
11199             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11200         }
11201         
11202         if(this.removable && !this.editable && !this.tickable){
11203             var close = this.closeTriggerEl();
11204             
11205             if(close){
11206                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11207                 close.on('click', this.removeBtnClick, this, close);
11208             }
11209         }
11210         
11211         //this.trigger.addClassOnOver('x-form-trigger-over');
11212         //this.trigger.addClassOnClick('x-form-trigger-click');
11213         
11214         //if(!this.width){
11215         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11216         //}
11217     },
11218     
11219     closeTriggerEl : function()
11220     {
11221         var close = this.el.select('.roo-combo-removable-btn', true).first();
11222         return close ? close : false;
11223     },
11224     
11225     removeBtnClick : function(e, h, el)
11226     {
11227         e.preventDefault();
11228         
11229         if(this.fireEvent("remove", this) !== false){
11230             this.reset();
11231             this.fireEvent("afterremove", this)
11232         }
11233     },
11234     
11235     createList : function()
11236     {
11237         this.list = Roo.get(document.body).createChild({
11238             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11239             cls: 'typeahead typeahead-long dropdown-menu',
11240             style: 'display:none'
11241         });
11242         
11243         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11244         
11245     },
11246
11247     // private
11248     initTrigger : function(){
11249        
11250     },
11251
11252     // private
11253     onDestroy : function(){
11254         if(this.trigger){
11255             this.trigger.removeAllListeners();
11256           //  this.trigger.remove();
11257         }
11258         //if(this.wrap){
11259         //    this.wrap.remove();
11260         //}
11261         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11262     },
11263
11264     // private
11265     onFocus : function(){
11266         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11267         /*
11268         if(!this.mimicing){
11269             this.wrap.addClass('x-trigger-wrap-focus');
11270             this.mimicing = true;
11271             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11272             if(this.monitorTab){
11273                 this.el.on("keydown", this.checkTab, this);
11274             }
11275         }
11276         */
11277     },
11278
11279     // private
11280     checkTab : function(e){
11281         if(e.getKey() == e.TAB){
11282             this.triggerBlur();
11283         }
11284     },
11285
11286     // private
11287     onBlur : function(){
11288         // do nothing
11289     },
11290
11291     // private
11292     mimicBlur : function(e, t){
11293         /*
11294         if(!this.wrap.contains(t) && this.validateBlur()){
11295             this.triggerBlur();
11296         }
11297         */
11298     },
11299
11300     // private
11301     triggerBlur : function(){
11302         this.mimicing = false;
11303         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11304         if(this.monitorTab){
11305             this.el.un("keydown", this.checkTab, this);
11306         }
11307         //this.wrap.removeClass('x-trigger-wrap-focus');
11308         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11309     },
11310
11311     // private
11312     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11313     validateBlur : function(e, t){
11314         return true;
11315     },
11316
11317     // private
11318     onDisable : function(){
11319         this.inputEl().dom.disabled = true;
11320         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11321         //if(this.wrap){
11322         //    this.wrap.addClass('x-item-disabled');
11323         //}
11324     },
11325
11326     // private
11327     onEnable : function(){
11328         this.inputEl().dom.disabled = false;
11329         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11330         //if(this.wrap){
11331         //    this.el.removeClass('x-item-disabled');
11332         //}
11333     },
11334
11335     // private
11336     onShow : function(){
11337         var ae = this.getActionEl();
11338         
11339         if(ae){
11340             ae.dom.style.display = '';
11341             ae.dom.style.visibility = 'visible';
11342         }
11343     },
11344
11345     // private
11346     
11347     onHide : function(){
11348         var ae = this.getActionEl();
11349         ae.dom.style.display = 'none';
11350     },
11351
11352     /**
11353      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11354      * by an implementing function.
11355      * @method
11356      * @param {EventObject} e
11357      */
11358     onTriggerClick : Roo.emptyFn
11359 });
11360  /*
11361  * Based on:
11362  * Ext JS Library 1.1.1
11363  * Copyright(c) 2006-2007, Ext JS, LLC.
11364  *
11365  * Originally Released Under LGPL - original licence link has changed is not relivant.
11366  *
11367  * Fork - LGPL
11368  * <script type="text/javascript">
11369  */
11370
11371
11372 /**
11373  * @class Roo.data.SortTypes
11374  * @singleton
11375  * Defines the default sorting (casting?) comparison functions used when sorting data.
11376  */
11377 Roo.data.SortTypes = {
11378     /**
11379      * Default sort that does nothing
11380      * @param {Mixed} s The value being converted
11381      * @return {Mixed} The comparison value
11382      */
11383     none : function(s){
11384         return s;
11385     },
11386     
11387     /**
11388      * The regular expression used to strip tags
11389      * @type {RegExp}
11390      * @property
11391      */
11392     stripTagsRE : /<\/?[^>]+>/gi,
11393     
11394     /**
11395      * Strips all HTML tags to sort on text only
11396      * @param {Mixed} s The value being converted
11397      * @return {String} The comparison value
11398      */
11399     asText : function(s){
11400         return String(s).replace(this.stripTagsRE, "");
11401     },
11402     
11403     /**
11404      * Strips all HTML tags to sort on text only - Case insensitive
11405      * @param {Mixed} s The value being converted
11406      * @return {String} The comparison value
11407      */
11408     asUCText : function(s){
11409         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11410     },
11411     
11412     /**
11413      * Case insensitive string
11414      * @param {Mixed} s The value being converted
11415      * @return {String} The comparison value
11416      */
11417     asUCString : function(s) {
11418         return String(s).toUpperCase();
11419     },
11420     
11421     /**
11422      * Date sorting
11423      * @param {Mixed} s The value being converted
11424      * @return {Number} The comparison value
11425      */
11426     asDate : function(s) {
11427         if(!s){
11428             return 0;
11429         }
11430         if(s instanceof Date){
11431             return s.getTime();
11432         }
11433         return Date.parse(String(s));
11434     },
11435     
11436     /**
11437      * Float sorting
11438      * @param {Mixed} s The value being converted
11439      * @return {Float} The comparison value
11440      */
11441     asFloat : function(s) {
11442         var val = parseFloat(String(s).replace(/,/g, ""));
11443         if(isNaN(val)) {
11444             val = 0;
11445         }
11446         return val;
11447     },
11448     
11449     /**
11450      * Integer sorting
11451      * @param {Mixed} s The value being converted
11452      * @return {Number} The comparison value
11453      */
11454     asInt : function(s) {
11455         var val = parseInt(String(s).replace(/,/g, ""));
11456         if(isNaN(val)) {
11457             val = 0;
11458         }
11459         return val;
11460     }
11461 };/*
11462  * Based on:
11463  * Ext JS Library 1.1.1
11464  * Copyright(c) 2006-2007, Ext JS, LLC.
11465  *
11466  * Originally Released Under LGPL - original licence link has changed is not relivant.
11467  *
11468  * Fork - LGPL
11469  * <script type="text/javascript">
11470  */
11471
11472 /**
11473 * @class Roo.data.Record
11474  * Instances of this class encapsulate both record <em>definition</em> information, and record
11475  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11476  * to access Records cached in an {@link Roo.data.Store} object.<br>
11477  * <p>
11478  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11479  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11480  * objects.<br>
11481  * <p>
11482  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11483  * @constructor
11484  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11485  * {@link #create}. The parameters are the same.
11486  * @param {Array} data An associative Array of data values keyed by the field name.
11487  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11488  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11489  * not specified an integer id is generated.
11490  */
11491 Roo.data.Record = function(data, id){
11492     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11493     this.data = data;
11494 };
11495
11496 /**
11497  * Generate a constructor for a specific record layout.
11498  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11499  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11500  * Each field definition object may contain the following properties: <ul>
11501  * <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,
11502  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11503  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11504  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11505  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11506  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11507  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11508  * this may be omitted.</p></li>
11509  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11510  * <ul><li>auto (Default, implies no conversion)</li>
11511  * <li>string</li>
11512  * <li>int</li>
11513  * <li>float</li>
11514  * <li>boolean</li>
11515  * <li>date</li></ul></p></li>
11516  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11517  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11518  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11519  * by the Reader into an object that will be stored in the Record. It is passed the
11520  * following parameters:<ul>
11521  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11522  * </ul></p></li>
11523  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11524  * </ul>
11525  * <br>usage:<br><pre><code>
11526 var TopicRecord = Roo.data.Record.create(
11527     {name: 'title', mapping: 'topic_title'},
11528     {name: 'author', mapping: 'username'},
11529     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11530     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11531     {name: 'lastPoster', mapping: 'user2'},
11532     {name: 'excerpt', mapping: 'post_text'}
11533 );
11534
11535 var myNewRecord = new TopicRecord({
11536     title: 'Do my job please',
11537     author: 'noobie',
11538     totalPosts: 1,
11539     lastPost: new Date(),
11540     lastPoster: 'Animal',
11541     excerpt: 'No way dude!'
11542 });
11543 myStore.add(myNewRecord);
11544 </code></pre>
11545  * @method create
11546  * @static
11547  */
11548 Roo.data.Record.create = function(o){
11549     var f = function(){
11550         f.superclass.constructor.apply(this, arguments);
11551     };
11552     Roo.extend(f, Roo.data.Record);
11553     var p = f.prototype;
11554     p.fields = new Roo.util.MixedCollection(false, function(field){
11555         return field.name;
11556     });
11557     for(var i = 0, len = o.length; i < len; i++){
11558         p.fields.add(new Roo.data.Field(o[i]));
11559     }
11560     f.getField = function(name){
11561         return p.fields.get(name);  
11562     };
11563     return f;
11564 };
11565
11566 Roo.data.Record.AUTO_ID = 1000;
11567 Roo.data.Record.EDIT = 'edit';
11568 Roo.data.Record.REJECT = 'reject';
11569 Roo.data.Record.COMMIT = 'commit';
11570
11571 Roo.data.Record.prototype = {
11572     /**
11573      * Readonly flag - true if this record has been modified.
11574      * @type Boolean
11575      */
11576     dirty : false,
11577     editing : false,
11578     error: null,
11579     modified: null,
11580
11581     // private
11582     join : function(store){
11583         this.store = store;
11584     },
11585
11586     /**
11587      * Set the named field to the specified value.
11588      * @param {String} name The name of the field to set.
11589      * @param {Object} value The value to set the field to.
11590      */
11591     set : function(name, value){
11592         if(this.data[name] == value){
11593             return;
11594         }
11595         this.dirty = true;
11596         if(!this.modified){
11597             this.modified = {};
11598         }
11599         if(typeof this.modified[name] == 'undefined'){
11600             this.modified[name] = this.data[name];
11601         }
11602         this.data[name] = value;
11603         if(!this.editing && this.store){
11604             this.store.afterEdit(this);
11605         }       
11606     },
11607
11608     /**
11609      * Get the value of the named field.
11610      * @param {String} name The name of the field to get the value of.
11611      * @return {Object} The value of the field.
11612      */
11613     get : function(name){
11614         return this.data[name]; 
11615     },
11616
11617     // private
11618     beginEdit : function(){
11619         this.editing = true;
11620         this.modified = {}; 
11621     },
11622
11623     // private
11624     cancelEdit : function(){
11625         this.editing = false;
11626         delete this.modified;
11627     },
11628
11629     // private
11630     endEdit : function(){
11631         this.editing = false;
11632         if(this.dirty && this.store){
11633             this.store.afterEdit(this);
11634         }
11635     },
11636
11637     /**
11638      * Usually called by the {@link Roo.data.Store} which owns the Record.
11639      * Rejects all changes made to the Record since either creation, or the last commit operation.
11640      * Modified fields are reverted to their original values.
11641      * <p>
11642      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11643      * of reject operations.
11644      */
11645     reject : function(){
11646         var m = this.modified;
11647         for(var n in m){
11648             if(typeof m[n] != "function"){
11649                 this.data[n] = m[n];
11650             }
11651         }
11652         this.dirty = false;
11653         delete this.modified;
11654         this.editing = false;
11655         if(this.store){
11656             this.store.afterReject(this);
11657         }
11658     },
11659
11660     /**
11661      * Usually called by the {@link Roo.data.Store} which owns the Record.
11662      * Commits all changes made to the Record since either creation, or the last commit operation.
11663      * <p>
11664      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11665      * of commit operations.
11666      */
11667     commit : function(){
11668         this.dirty = false;
11669         delete this.modified;
11670         this.editing = false;
11671         if(this.store){
11672             this.store.afterCommit(this);
11673         }
11674     },
11675
11676     // private
11677     hasError : function(){
11678         return this.error != null;
11679     },
11680
11681     // private
11682     clearError : function(){
11683         this.error = null;
11684     },
11685
11686     /**
11687      * Creates a copy of this record.
11688      * @param {String} id (optional) A new record id if you don't want to use this record's id
11689      * @return {Record}
11690      */
11691     copy : function(newId) {
11692         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11693     }
11694 };/*
11695  * Based on:
11696  * Ext JS Library 1.1.1
11697  * Copyright(c) 2006-2007, Ext JS, LLC.
11698  *
11699  * Originally Released Under LGPL - original licence link has changed is not relivant.
11700  *
11701  * Fork - LGPL
11702  * <script type="text/javascript">
11703  */
11704
11705
11706
11707 /**
11708  * @class Roo.data.Store
11709  * @extends Roo.util.Observable
11710  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11711  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11712  * <p>
11713  * 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
11714  * has no knowledge of the format of the data returned by the Proxy.<br>
11715  * <p>
11716  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11717  * instances from the data object. These records are cached and made available through accessor functions.
11718  * @constructor
11719  * Creates a new Store.
11720  * @param {Object} config A config object containing the objects needed for the Store to access data,
11721  * and read the data into Records.
11722  */
11723 Roo.data.Store = function(config){
11724     this.data = new Roo.util.MixedCollection(false);
11725     this.data.getKey = function(o){
11726         return o.id;
11727     };
11728     this.baseParams = {};
11729     // private
11730     this.paramNames = {
11731         "start" : "start",
11732         "limit" : "limit",
11733         "sort" : "sort",
11734         "dir" : "dir",
11735         "multisort" : "_multisort"
11736     };
11737
11738     if(config && config.data){
11739         this.inlineData = config.data;
11740         delete config.data;
11741     }
11742
11743     Roo.apply(this, config);
11744     
11745     if(this.reader){ // reader passed
11746         this.reader = Roo.factory(this.reader, Roo.data);
11747         this.reader.xmodule = this.xmodule || false;
11748         if(!this.recordType){
11749             this.recordType = this.reader.recordType;
11750         }
11751         if(this.reader.onMetaChange){
11752             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11753         }
11754     }
11755
11756     if(this.recordType){
11757         this.fields = this.recordType.prototype.fields;
11758     }
11759     this.modified = [];
11760
11761     this.addEvents({
11762         /**
11763          * @event datachanged
11764          * Fires when the data cache has changed, and a widget which is using this Store
11765          * as a Record cache should refresh its view.
11766          * @param {Store} this
11767          */
11768         datachanged : true,
11769         /**
11770          * @event metachange
11771          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11772          * @param {Store} this
11773          * @param {Object} meta The JSON metadata
11774          */
11775         metachange : true,
11776         /**
11777          * @event add
11778          * Fires when Records have been added to the Store
11779          * @param {Store} this
11780          * @param {Roo.data.Record[]} records The array of Records added
11781          * @param {Number} index The index at which the record(s) were added
11782          */
11783         add : true,
11784         /**
11785          * @event remove
11786          * Fires when a Record has been removed from the Store
11787          * @param {Store} this
11788          * @param {Roo.data.Record} record The Record that was removed
11789          * @param {Number} index The index at which the record was removed
11790          */
11791         remove : true,
11792         /**
11793          * @event update
11794          * Fires when a Record has been updated
11795          * @param {Store} this
11796          * @param {Roo.data.Record} record The Record that was updated
11797          * @param {String} operation The update operation being performed.  Value may be one of:
11798          * <pre><code>
11799  Roo.data.Record.EDIT
11800  Roo.data.Record.REJECT
11801  Roo.data.Record.COMMIT
11802          * </code></pre>
11803          */
11804         update : true,
11805         /**
11806          * @event clear
11807          * Fires when the data cache has been cleared.
11808          * @param {Store} this
11809          */
11810         clear : true,
11811         /**
11812          * @event beforeload
11813          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11814          * the load action will be canceled.
11815          * @param {Store} this
11816          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11817          */
11818         beforeload : true,
11819         /**
11820          * @event beforeloadadd
11821          * Fires after a new set of Records has been loaded.
11822          * @param {Store} this
11823          * @param {Roo.data.Record[]} records The Records that were loaded
11824          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11825          */
11826         beforeloadadd : true,
11827         /**
11828          * @event load
11829          * Fires after a new set of Records has been loaded, before they are added to the store.
11830          * @param {Store} this
11831          * @param {Roo.data.Record[]} records The Records that were loaded
11832          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11833          * @params {Object} return from reader
11834          */
11835         load : true,
11836         /**
11837          * @event loadexception
11838          * Fires if an exception occurs in the Proxy during loading.
11839          * Called with the signature of the Proxy's "loadexception" event.
11840          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11841          * 
11842          * @param {Proxy} 
11843          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11844          * @param {Object} load options 
11845          * @param {Object} jsonData from your request (normally this contains the Exception)
11846          */
11847         loadexception : true
11848     });
11849     
11850     if(this.proxy){
11851         this.proxy = Roo.factory(this.proxy, Roo.data);
11852         this.proxy.xmodule = this.xmodule || false;
11853         this.relayEvents(this.proxy,  ["loadexception"]);
11854     }
11855     this.sortToggle = {};
11856     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11857
11858     Roo.data.Store.superclass.constructor.call(this);
11859
11860     if(this.inlineData){
11861         this.loadData(this.inlineData);
11862         delete this.inlineData;
11863     }
11864 };
11865
11866 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11867      /**
11868     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11869     * without a remote query - used by combo/forms at present.
11870     */
11871     
11872     /**
11873     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11874     */
11875     /**
11876     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11877     */
11878     /**
11879     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11880     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11881     */
11882     /**
11883     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11884     * on any HTTP request
11885     */
11886     /**
11887     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11888     */
11889     /**
11890     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11891     */
11892     multiSort: false,
11893     /**
11894     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11895     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11896     */
11897     remoteSort : false,
11898
11899     /**
11900     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11901      * loaded or when a record is removed. (defaults to false).
11902     */
11903     pruneModifiedRecords : false,
11904
11905     // private
11906     lastOptions : null,
11907
11908     /**
11909      * Add Records to the Store and fires the add event.
11910      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11911      */
11912     add : function(records){
11913         records = [].concat(records);
11914         for(var i = 0, len = records.length; i < len; i++){
11915             records[i].join(this);
11916         }
11917         var index = this.data.length;
11918         this.data.addAll(records);
11919         this.fireEvent("add", this, records, index);
11920     },
11921
11922     /**
11923      * Remove a Record from the Store and fires the remove event.
11924      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11925      */
11926     remove : function(record){
11927         var index = this.data.indexOf(record);
11928         this.data.removeAt(index);
11929  
11930         if(this.pruneModifiedRecords){
11931             this.modified.remove(record);
11932         }
11933         this.fireEvent("remove", this, record, index);
11934     },
11935
11936     /**
11937      * Remove all Records from the Store and fires the clear event.
11938      */
11939     removeAll : function(){
11940         this.data.clear();
11941         if(this.pruneModifiedRecords){
11942             this.modified = [];
11943         }
11944         this.fireEvent("clear", this);
11945     },
11946
11947     /**
11948      * Inserts Records to the Store at the given index and fires the add event.
11949      * @param {Number} index The start index at which to insert the passed Records.
11950      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11951      */
11952     insert : function(index, records){
11953         records = [].concat(records);
11954         for(var i = 0, len = records.length; i < len; i++){
11955             this.data.insert(index, records[i]);
11956             records[i].join(this);
11957         }
11958         this.fireEvent("add", this, records, index);
11959     },
11960
11961     /**
11962      * Get the index within the cache of the passed Record.
11963      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11964      * @return {Number} The index of the passed Record. Returns -1 if not found.
11965      */
11966     indexOf : function(record){
11967         return this.data.indexOf(record);
11968     },
11969
11970     /**
11971      * Get the index within the cache of the Record with the passed id.
11972      * @param {String} id The id of the Record to find.
11973      * @return {Number} The index of the Record. Returns -1 if not found.
11974      */
11975     indexOfId : function(id){
11976         return this.data.indexOfKey(id);
11977     },
11978
11979     /**
11980      * Get the Record with the specified id.
11981      * @param {String} id The id of the Record to find.
11982      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11983      */
11984     getById : function(id){
11985         return this.data.key(id);
11986     },
11987
11988     /**
11989      * Get the Record at the specified index.
11990      * @param {Number} index The index of the Record to find.
11991      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11992      */
11993     getAt : function(index){
11994         return this.data.itemAt(index);
11995     },
11996
11997     /**
11998      * Returns a range of Records between specified indices.
11999      * @param {Number} startIndex (optional) The starting index (defaults to 0)
12000      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12001      * @return {Roo.data.Record[]} An array of Records
12002      */
12003     getRange : function(start, end){
12004         return this.data.getRange(start, end);
12005     },
12006
12007     // private
12008     storeOptions : function(o){
12009         o = Roo.apply({}, o);
12010         delete o.callback;
12011         delete o.scope;
12012         this.lastOptions = o;
12013     },
12014
12015     /**
12016      * Loads the Record cache from the configured Proxy using the configured Reader.
12017      * <p>
12018      * If using remote paging, then the first load call must specify the <em>start</em>
12019      * and <em>limit</em> properties in the options.params property to establish the initial
12020      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12021      * <p>
12022      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12023      * and this call will return before the new data has been loaded. Perform any post-processing
12024      * in a callback function, or in a "load" event handler.</strong>
12025      * <p>
12026      * @param {Object} options An object containing properties which control loading options:<ul>
12027      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12028      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12029      * passed the following arguments:<ul>
12030      * <li>r : Roo.data.Record[]</li>
12031      * <li>options: Options object from the load call</li>
12032      * <li>success: Boolean success indicator</li></ul></li>
12033      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12034      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12035      * </ul>
12036      */
12037     load : function(options){
12038         options = options || {};
12039         if(this.fireEvent("beforeload", this, options) !== false){
12040             this.storeOptions(options);
12041             var p = Roo.apply(options.params || {}, this.baseParams);
12042             // if meta was not loaded from remote source.. try requesting it.
12043             if (!this.reader.metaFromRemote) {
12044                 p._requestMeta = 1;
12045             }
12046             if(this.sortInfo && this.remoteSort){
12047                 var pn = this.paramNames;
12048                 p[pn["sort"]] = this.sortInfo.field;
12049                 p[pn["dir"]] = this.sortInfo.direction;
12050             }
12051             if (this.multiSort) {
12052                 var pn = this.paramNames;
12053                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12054             }
12055             
12056             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12057         }
12058     },
12059
12060     /**
12061      * Reloads the Record cache from the configured Proxy using the configured Reader and
12062      * the options from the last load operation performed.
12063      * @param {Object} options (optional) An object containing properties which may override the options
12064      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12065      * the most recently used options are reused).
12066      */
12067     reload : function(options){
12068         this.load(Roo.applyIf(options||{}, this.lastOptions));
12069     },
12070
12071     // private
12072     // Called as a callback by the Reader during a load operation.
12073     loadRecords : function(o, options, success){
12074         if(!o || success === false){
12075             if(success !== false){
12076                 this.fireEvent("load", this, [], options, o);
12077             }
12078             if(options.callback){
12079                 options.callback.call(options.scope || this, [], options, false);
12080             }
12081             return;
12082         }
12083         // if data returned failure - throw an exception.
12084         if (o.success === false) {
12085             // show a message if no listener is registered.
12086             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12087                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12088             }
12089             // loadmask wil be hooked into this..
12090             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12091             return;
12092         }
12093         var r = o.records, t = o.totalRecords || r.length;
12094         
12095         this.fireEvent("beforeloadadd", this, r, options, o);
12096         
12097         if(!options || options.add !== true){
12098             if(this.pruneModifiedRecords){
12099                 this.modified = [];
12100             }
12101             for(var i = 0, len = r.length; i < len; i++){
12102                 r[i].join(this);
12103             }
12104             if(this.snapshot){
12105                 this.data = this.snapshot;
12106                 delete this.snapshot;
12107             }
12108             this.data.clear();
12109             this.data.addAll(r);
12110             this.totalLength = t;
12111             this.applySort();
12112             this.fireEvent("datachanged", this);
12113         }else{
12114             this.totalLength = Math.max(t, this.data.length+r.length);
12115             this.add(r);
12116         }
12117         
12118         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12119                 
12120             var e = new Roo.data.Record({});
12121
12122             e.set(this.parent.displayField, this.parent.emptyTitle);
12123             e.set(this.parent.valueField, '');
12124
12125             this.insert(0, e);
12126         }
12127             
12128         this.fireEvent("load", this, r, options, o);
12129         if(options.callback){
12130             options.callback.call(options.scope || this, r, options, true);
12131         }
12132     },
12133
12134
12135     /**
12136      * Loads data from a passed data block. A Reader which understands the format of the data
12137      * must have been configured in the constructor.
12138      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12139      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12140      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12141      */
12142     loadData : function(o, append){
12143         var r = this.reader.readRecords(o);
12144         this.loadRecords(r, {add: append}, true);
12145     },
12146     
12147      /**
12148      * using 'cn' the nested child reader read the child array into it's child stores.
12149      * @param {Object} rec The record with a 'children array
12150      */
12151     loadDataFromChildren : function(rec)
12152     {
12153         this.loadData(this.reader.toLoadData(rec));
12154     },
12155     
12156
12157     /**
12158      * Gets the number of cached records.
12159      * <p>
12160      * <em>If using paging, this may not be the total size of the dataset. If the data object
12161      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12162      * the data set size</em>
12163      */
12164     getCount : function(){
12165         return this.data.length || 0;
12166     },
12167
12168     /**
12169      * Gets the total number of records in the dataset as returned by the server.
12170      * <p>
12171      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12172      * the dataset size</em>
12173      */
12174     getTotalCount : function(){
12175         return this.totalLength || 0;
12176     },
12177
12178     /**
12179      * Returns the sort state of the Store as an object with two properties:
12180      * <pre><code>
12181  field {String} The name of the field by which the Records are sorted
12182  direction {String} The sort order, "ASC" or "DESC"
12183      * </code></pre>
12184      */
12185     getSortState : function(){
12186         return this.sortInfo;
12187     },
12188
12189     // private
12190     applySort : function(){
12191         if(this.sortInfo && !this.remoteSort){
12192             var s = this.sortInfo, f = s.field;
12193             var st = this.fields.get(f).sortType;
12194             var fn = function(r1, r2){
12195                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12196                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12197             };
12198             this.data.sort(s.direction, fn);
12199             if(this.snapshot && this.snapshot != this.data){
12200                 this.snapshot.sort(s.direction, fn);
12201             }
12202         }
12203     },
12204
12205     /**
12206      * Sets the default sort column and order to be used by the next load operation.
12207      * @param {String} fieldName The name of the field to sort by.
12208      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12209      */
12210     setDefaultSort : function(field, dir){
12211         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12212     },
12213
12214     /**
12215      * Sort the Records.
12216      * If remote sorting is used, the sort is performed on the server, and the cache is
12217      * reloaded. If local sorting is used, the cache is sorted internally.
12218      * @param {String} fieldName The name of the field to sort by.
12219      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12220      */
12221     sort : function(fieldName, dir){
12222         var f = this.fields.get(fieldName);
12223         if(!dir){
12224             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12225             
12226             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12227                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12228             }else{
12229                 dir = f.sortDir;
12230             }
12231         }
12232         this.sortToggle[f.name] = dir;
12233         this.sortInfo = {field: f.name, direction: dir};
12234         if(!this.remoteSort){
12235             this.applySort();
12236             this.fireEvent("datachanged", this);
12237         }else{
12238             this.load(this.lastOptions);
12239         }
12240     },
12241
12242     /**
12243      * Calls the specified function for each of the Records in the cache.
12244      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12245      * Returning <em>false</em> aborts and exits the iteration.
12246      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12247      */
12248     each : function(fn, scope){
12249         this.data.each(fn, scope);
12250     },
12251
12252     /**
12253      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12254      * (e.g., during paging).
12255      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12256      */
12257     getModifiedRecords : function(){
12258         return this.modified;
12259     },
12260
12261     // private
12262     createFilterFn : function(property, value, anyMatch){
12263         if(!value.exec){ // not a regex
12264             value = String(value);
12265             if(value.length == 0){
12266                 return false;
12267             }
12268             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12269         }
12270         return function(r){
12271             return value.test(r.data[property]);
12272         };
12273     },
12274
12275     /**
12276      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12277      * @param {String} property A field on your records
12278      * @param {Number} start The record index to start at (defaults to 0)
12279      * @param {Number} end The last record index to include (defaults to length - 1)
12280      * @return {Number} The sum
12281      */
12282     sum : function(property, start, end){
12283         var rs = this.data.items, v = 0;
12284         start = start || 0;
12285         end = (end || end === 0) ? end : rs.length-1;
12286
12287         for(var i = start; i <= end; i++){
12288             v += (rs[i].data[property] || 0);
12289         }
12290         return v;
12291     },
12292
12293     /**
12294      * Filter the records by a specified property.
12295      * @param {String} field A field on your records
12296      * @param {String/RegExp} value Either a string that the field
12297      * should start with or a RegExp to test against the field
12298      * @param {Boolean} anyMatch True to match any part not just the beginning
12299      */
12300     filter : function(property, value, anyMatch){
12301         var fn = this.createFilterFn(property, value, anyMatch);
12302         return fn ? this.filterBy(fn) : this.clearFilter();
12303     },
12304
12305     /**
12306      * Filter by a function. The specified function will be called with each
12307      * record in this data source. If the function returns true the record is included,
12308      * otherwise it is filtered.
12309      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12310      * @param {Object} scope (optional) The scope of the function (defaults to this)
12311      */
12312     filterBy : function(fn, scope){
12313         this.snapshot = this.snapshot || this.data;
12314         this.data = this.queryBy(fn, scope||this);
12315         this.fireEvent("datachanged", this);
12316     },
12317
12318     /**
12319      * Query the records by a specified property.
12320      * @param {String} field A field on your records
12321      * @param {String/RegExp} value Either a string that the field
12322      * should start with or a RegExp to test against the field
12323      * @param {Boolean} anyMatch True to match any part not just the beginning
12324      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12325      */
12326     query : function(property, value, anyMatch){
12327         var fn = this.createFilterFn(property, value, anyMatch);
12328         return fn ? this.queryBy(fn) : this.data.clone();
12329     },
12330
12331     /**
12332      * Query by a function. The specified function will be called with each
12333      * record in this data source. If the function returns true the record is included
12334      * in the results.
12335      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12336      * @param {Object} scope (optional) The scope of the function (defaults to this)
12337       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12338      **/
12339     queryBy : function(fn, scope){
12340         var data = this.snapshot || this.data;
12341         return data.filterBy(fn, scope||this);
12342     },
12343
12344     /**
12345      * Collects unique values for a particular dataIndex from this store.
12346      * @param {String} dataIndex The property to collect
12347      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12348      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12349      * @return {Array} An array of the unique values
12350      **/
12351     collect : function(dataIndex, allowNull, bypassFilter){
12352         var d = (bypassFilter === true && this.snapshot) ?
12353                 this.snapshot.items : this.data.items;
12354         var v, sv, r = [], l = {};
12355         for(var i = 0, len = d.length; i < len; i++){
12356             v = d[i].data[dataIndex];
12357             sv = String(v);
12358             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12359                 l[sv] = true;
12360                 r[r.length] = v;
12361             }
12362         }
12363         return r;
12364     },
12365
12366     /**
12367      * Revert to a view of the Record cache with no filtering applied.
12368      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12369      */
12370     clearFilter : function(suppressEvent){
12371         if(this.snapshot && this.snapshot != this.data){
12372             this.data = this.snapshot;
12373             delete this.snapshot;
12374             if(suppressEvent !== true){
12375                 this.fireEvent("datachanged", this);
12376             }
12377         }
12378     },
12379
12380     // private
12381     afterEdit : function(record){
12382         if(this.modified.indexOf(record) == -1){
12383             this.modified.push(record);
12384         }
12385         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12386     },
12387     
12388     // private
12389     afterReject : function(record){
12390         this.modified.remove(record);
12391         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12392     },
12393
12394     // private
12395     afterCommit : function(record){
12396         this.modified.remove(record);
12397         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12398     },
12399
12400     /**
12401      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12402      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12403      */
12404     commitChanges : function(){
12405         var m = this.modified.slice(0);
12406         this.modified = [];
12407         for(var i = 0, len = m.length; i < len; i++){
12408             m[i].commit();
12409         }
12410     },
12411
12412     /**
12413      * Cancel outstanding changes on all changed records.
12414      */
12415     rejectChanges : function(){
12416         var m = this.modified.slice(0);
12417         this.modified = [];
12418         for(var i = 0, len = m.length; i < len; i++){
12419             m[i].reject();
12420         }
12421     },
12422
12423     onMetaChange : function(meta, rtype, o){
12424         this.recordType = rtype;
12425         this.fields = rtype.prototype.fields;
12426         delete this.snapshot;
12427         this.sortInfo = meta.sortInfo || this.sortInfo;
12428         this.modified = [];
12429         this.fireEvent('metachange', this, this.reader.meta);
12430     },
12431     
12432     moveIndex : function(data, type)
12433     {
12434         var index = this.indexOf(data);
12435         
12436         var newIndex = index + type;
12437         
12438         this.remove(data);
12439         
12440         this.insert(newIndex, data);
12441         
12442     }
12443 });/*
12444  * Based on:
12445  * Ext JS Library 1.1.1
12446  * Copyright(c) 2006-2007, Ext JS, LLC.
12447  *
12448  * Originally Released Under LGPL - original licence link has changed is not relivant.
12449  *
12450  * Fork - LGPL
12451  * <script type="text/javascript">
12452  */
12453
12454 /**
12455  * @class Roo.data.SimpleStore
12456  * @extends Roo.data.Store
12457  * Small helper class to make creating Stores from Array data easier.
12458  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12459  * @cfg {Array} fields An array of field definition objects, or field name strings.
12460  * @cfg {Object} an existing reader (eg. copied from another store)
12461  * @cfg {Array} data The multi-dimensional array of data
12462  * @constructor
12463  * @param {Object} config
12464  */
12465 Roo.data.SimpleStore = function(config)
12466 {
12467     Roo.data.SimpleStore.superclass.constructor.call(this, {
12468         isLocal : true,
12469         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12470                 id: config.id
12471             },
12472             Roo.data.Record.create(config.fields)
12473         ),
12474         proxy : new Roo.data.MemoryProxy(config.data)
12475     });
12476     this.load();
12477 };
12478 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12479  * Based on:
12480  * Ext JS Library 1.1.1
12481  * Copyright(c) 2006-2007, Ext JS, LLC.
12482  *
12483  * Originally Released Under LGPL - original licence link has changed is not relivant.
12484  *
12485  * Fork - LGPL
12486  * <script type="text/javascript">
12487  */
12488
12489 /**
12490 /**
12491  * @extends Roo.data.Store
12492  * @class Roo.data.JsonStore
12493  * Small helper class to make creating Stores for JSON data easier. <br/>
12494 <pre><code>
12495 var store = new Roo.data.JsonStore({
12496     url: 'get-images.php',
12497     root: 'images',
12498     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12499 });
12500 </code></pre>
12501  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12502  * JsonReader and HttpProxy (unless inline data is provided).</b>
12503  * @cfg {Array} fields An array of field definition objects, or field name strings.
12504  * @constructor
12505  * @param {Object} config
12506  */
12507 Roo.data.JsonStore = function(c){
12508     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12509         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12510         reader: new Roo.data.JsonReader(c, c.fields)
12511     }));
12512 };
12513 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12514  * Based on:
12515  * Ext JS Library 1.1.1
12516  * Copyright(c) 2006-2007, Ext JS, LLC.
12517  *
12518  * Originally Released Under LGPL - original licence link has changed is not relivant.
12519  *
12520  * Fork - LGPL
12521  * <script type="text/javascript">
12522  */
12523
12524  
12525 Roo.data.Field = function(config){
12526     if(typeof config == "string"){
12527         config = {name: config};
12528     }
12529     Roo.apply(this, config);
12530     
12531     if(!this.type){
12532         this.type = "auto";
12533     }
12534     
12535     var st = Roo.data.SortTypes;
12536     // named sortTypes are supported, here we look them up
12537     if(typeof this.sortType == "string"){
12538         this.sortType = st[this.sortType];
12539     }
12540     
12541     // set default sortType for strings and dates
12542     if(!this.sortType){
12543         switch(this.type){
12544             case "string":
12545                 this.sortType = st.asUCString;
12546                 break;
12547             case "date":
12548                 this.sortType = st.asDate;
12549                 break;
12550             default:
12551                 this.sortType = st.none;
12552         }
12553     }
12554
12555     // define once
12556     var stripRe = /[\$,%]/g;
12557
12558     // prebuilt conversion function for this field, instead of
12559     // switching every time we're reading a value
12560     if(!this.convert){
12561         var cv, dateFormat = this.dateFormat;
12562         switch(this.type){
12563             case "":
12564             case "auto":
12565             case undefined:
12566                 cv = function(v){ return v; };
12567                 break;
12568             case "string":
12569                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12570                 break;
12571             case "int":
12572                 cv = function(v){
12573                     return v !== undefined && v !== null && v !== '' ?
12574                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12575                     };
12576                 break;
12577             case "float":
12578                 cv = function(v){
12579                     return v !== undefined && v !== null && v !== '' ?
12580                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12581                     };
12582                 break;
12583             case "bool":
12584             case "boolean":
12585                 cv = function(v){ return v === true || v === "true" || v == 1; };
12586                 break;
12587             case "date":
12588                 cv = function(v){
12589                     if(!v){
12590                         return '';
12591                     }
12592                     if(v instanceof Date){
12593                         return v;
12594                     }
12595                     if(dateFormat){
12596                         if(dateFormat == "timestamp"){
12597                             return new Date(v*1000);
12598                         }
12599                         return Date.parseDate(v, dateFormat);
12600                     }
12601                     var parsed = Date.parse(v);
12602                     return parsed ? new Date(parsed) : null;
12603                 };
12604              break;
12605             
12606         }
12607         this.convert = cv;
12608     }
12609 };
12610
12611 Roo.data.Field.prototype = {
12612     dateFormat: null,
12613     defaultValue: "",
12614     mapping: null,
12615     sortType : null,
12616     sortDir : "ASC"
12617 };/*
12618  * Based on:
12619  * Ext JS Library 1.1.1
12620  * Copyright(c) 2006-2007, Ext JS, LLC.
12621  *
12622  * Originally Released Under LGPL - original licence link has changed is not relivant.
12623  *
12624  * Fork - LGPL
12625  * <script type="text/javascript">
12626  */
12627  
12628 // Base class for reading structured data from a data source.  This class is intended to be
12629 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12630
12631 /**
12632  * @class Roo.data.DataReader
12633  * Base class for reading structured data from a data source.  This class is intended to be
12634  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12635  */
12636
12637 Roo.data.DataReader = function(meta, recordType){
12638     
12639     this.meta = meta;
12640     
12641     this.recordType = recordType instanceof Array ? 
12642         Roo.data.Record.create(recordType) : recordType;
12643 };
12644
12645 Roo.data.DataReader.prototype = {
12646     
12647     
12648     readerType : 'Data',
12649      /**
12650      * Create an empty record
12651      * @param {Object} data (optional) - overlay some values
12652      * @return {Roo.data.Record} record created.
12653      */
12654     newRow :  function(d) {
12655         var da =  {};
12656         this.recordType.prototype.fields.each(function(c) {
12657             switch( c.type) {
12658                 case 'int' : da[c.name] = 0; break;
12659                 case 'date' : da[c.name] = new Date(); break;
12660                 case 'float' : da[c.name] = 0.0; break;
12661                 case 'boolean' : da[c.name] = false; break;
12662                 default : da[c.name] = ""; break;
12663             }
12664             
12665         });
12666         return new this.recordType(Roo.apply(da, d));
12667     }
12668     
12669     
12670 };/*
12671  * Based on:
12672  * Ext JS Library 1.1.1
12673  * Copyright(c) 2006-2007, Ext JS, LLC.
12674  *
12675  * Originally Released Under LGPL - original licence link has changed is not relivant.
12676  *
12677  * Fork - LGPL
12678  * <script type="text/javascript">
12679  */
12680
12681 /**
12682  * @class Roo.data.DataProxy
12683  * @extends Roo.data.Observable
12684  * This class is an abstract base class for implementations which provide retrieval of
12685  * unformatted data objects.<br>
12686  * <p>
12687  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12688  * (of the appropriate type which knows how to parse the data object) to provide a block of
12689  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12690  * <p>
12691  * Custom implementations must implement the load method as described in
12692  * {@link Roo.data.HttpProxy#load}.
12693  */
12694 Roo.data.DataProxy = function(){
12695     this.addEvents({
12696         /**
12697          * @event beforeload
12698          * Fires before a network request is made to retrieve a data object.
12699          * @param {Object} This DataProxy object.
12700          * @param {Object} params The params parameter to the load function.
12701          */
12702         beforeload : true,
12703         /**
12704          * @event load
12705          * Fires before the load method's callback is called.
12706          * @param {Object} This DataProxy object.
12707          * @param {Object} o The data object.
12708          * @param {Object} arg The callback argument object passed to the load function.
12709          */
12710         load : true,
12711         /**
12712          * @event loadexception
12713          * Fires if an Exception occurs during data retrieval.
12714          * @param {Object} This DataProxy object.
12715          * @param {Object} o The data object.
12716          * @param {Object} arg The callback argument object passed to the load function.
12717          * @param {Object} e The Exception.
12718          */
12719         loadexception : true
12720     });
12721     Roo.data.DataProxy.superclass.constructor.call(this);
12722 };
12723
12724 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12725
12726     /**
12727      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12728      */
12729 /*
12730  * Based on:
12731  * Ext JS Library 1.1.1
12732  * Copyright(c) 2006-2007, Ext JS, LLC.
12733  *
12734  * Originally Released Under LGPL - original licence link has changed is not relivant.
12735  *
12736  * Fork - LGPL
12737  * <script type="text/javascript">
12738  */
12739 /**
12740  * @class Roo.data.MemoryProxy
12741  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12742  * to the Reader when its load method is called.
12743  * @constructor
12744  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12745  */
12746 Roo.data.MemoryProxy = function(data){
12747     if (data.data) {
12748         data = data.data;
12749     }
12750     Roo.data.MemoryProxy.superclass.constructor.call(this);
12751     this.data = data;
12752 };
12753
12754 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12755     
12756     /**
12757      * Load data from the requested source (in this case an in-memory
12758      * data object passed to the constructor), read the data object into
12759      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12760      * process that block using the passed callback.
12761      * @param {Object} params This parameter is not used by the MemoryProxy class.
12762      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12763      * object into a block of Roo.data.Records.
12764      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12765      * The function must be passed <ul>
12766      * <li>The Record block object</li>
12767      * <li>The "arg" argument from the load function</li>
12768      * <li>A boolean success indicator</li>
12769      * </ul>
12770      * @param {Object} scope The scope in which to call the callback
12771      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12772      */
12773     load : function(params, reader, callback, scope, arg){
12774         params = params || {};
12775         var result;
12776         try {
12777             result = reader.readRecords(params.data ? params.data :this.data);
12778         }catch(e){
12779             this.fireEvent("loadexception", this, arg, null, e);
12780             callback.call(scope, null, arg, false);
12781             return;
12782         }
12783         callback.call(scope, result, arg, true);
12784     },
12785     
12786     // private
12787     update : function(params, records){
12788         
12789     }
12790 });/*
12791  * Based on:
12792  * Ext JS Library 1.1.1
12793  * Copyright(c) 2006-2007, Ext JS, LLC.
12794  *
12795  * Originally Released Under LGPL - original licence link has changed is not relivant.
12796  *
12797  * Fork - LGPL
12798  * <script type="text/javascript">
12799  */
12800 /**
12801  * @class Roo.data.HttpProxy
12802  * @extends Roo.data.DataProxy
12803  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12804  * configured to reference a certain URL.<br><br>
12805  * <p>
12806  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12807  * from which the running page was served.<br><br>
12808  * <p>
12809  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12810  * <p>
12811  * Be aware that to enable the browser to parse an XML document, the server must set
12812  * the Content-Type header in the HTTP response to "text/xml".
12813  * @constructor
12814  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12815  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12816  * will be used to make the request.
12817  */
12818 Roo.data.HttpProxy = function(conn){
12819     Roo.data.HttpProxy.superclass.constructor.call(this);
12820     // is conn a conn config or a real conn?
12821     this.conn = conn;
12822     this.useAjax = !conn || !conn.events;
12823   
12824 };
12825
12826 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12827     // thse are take from connection...
12828     
12829     /**
12830      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12831      */
12832     /**
12833      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12834      * extra parameters to each request made by this object. (defaults to undefined)
12835      */
12836     /**
12837      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12838      *  to each request made by this object. (defaults to undefined)
12839      */
12840     /**
12841      * @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)
12842      */
12843     /**
12844      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12845      */
12846      /**
12847      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12848      * @type Boolean
12849      */
12850   
12851
12852     /**
12853      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12854      * @type Boolean
12855      */
12856     /**
12857      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12858      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12859      * a finer-grained basis than the DataProxy events.
12860      */
12861     getConnection : function(){
12862         return this.useAjax ? Roo.Ajax : this.conn;
12863     },
12864
12865     /**
12866      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12867      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12868      * process that block using the passed callback.
12869      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12870      * for the request to the remote server.
12871      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12872      * object into a block of Roo.data.Records.
12873      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12874      * The function must be passed <ul>
12875      * <li>The Record block object</li>
12876      * <li>The "arg" argument from the load function</li>
12877      * <li>A boolean success indicator</li>
12878      * </ul>
12879      * @param {Object} scope The scope in which to call the callback
12880      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12881      */
12882     load : function(params, reader, callback, scope, arg){
12883         if(this.fireEvent("beforeload", this, params) !== false){
12884             var  o = {
12885                 params : params || {},
12886                 request: {
12887                     callback : callback,
12888                     scope : scope,
12889                     arg : arg
12890                 },
12891                 reader: reader,
12892                 callback : this.loadResponse,
12893                 scope: this
12894             };
12895             if(this.useAjax){
12896                 Roo.applyIf(o, this.conn);
12897                 if(this.activeRequest){
12898                     Roo.Ajax.abort(this.activeRequest);
12899                 }
12900                 this.activeRequest = Roo.Ajax.request(o);
12901             }else{
12902                 this.conn.request(o);
12903             }
12904         }else{
12905             callback.call(scope||this, null, arg, false);
12906         }
12907     },
12908
12909     // private
12910     loadResponse : function(o, success, response){
12911         delete this.activeRequest;
12912         if(!success){
12913             this.fireEvent("loadexception", this, o, response);
12914             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12915             return;
12916         }
12917         var result;
12918         try {
12919             result = o.reader.read(response);
12920         }catch(e){
12921             this.fireEvent("loadexception", this, o, response, e);
12922             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12923             return;
12924         }
12925         
12926         this.fireEvent("load", this, o, o.request.arg);
12927         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12928     },
12929
12930     // private
12931     update : function(dataSet){
12932
12933     },
12934
12935     // private
12936     updateResponse : function(dataSet){
12937
12938     }
12939 });/*
12940  * Based on:
12941  * Ext JS Library 1.1.1
12942  * Copyright(c) 2006-2007, Ext JS, LLC.
12943  *
12944  * Originally Released Under LGPL - original licence link has changed is not relivant.
12945  *
12946  * Fork - LGPL
12947  * <script type="text/javascript">
12948  */
12949
12950 /**
12951  * @class Roo.data.ScriptTagProxy
12952  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12953  * other than the originating domain of the running page.<br><br>
12954  * <p>
12955  * <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
12956  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12957  * <p>
12958  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12959  * source code that is used as the source inside a &lt;script> tag.<br><br>
12960  * <p>
12961  * In order for the browser to process the returned data, the server must wrap the data object
12962  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12963  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12964  * depending on whether the callback name was passed:
12965  * <p>
12966  * <pre><code>
12967 boolean scriptTag = false;
12968 String cb = request.getParameter("callback");
12969 if (cb != null) {
12970     scriptTag = true;
12971     response.setContentType("text/javascript");
12972 } else {
12973     response.setContentType("application/x-json");
12974 }
12975 Writer out = response.getWriter();
12976 if (scriptTag) {
12977     out.write(cb + "(");
12978 }
12979 out.print(dataBlock.toJsonString());
12980 if (scriptTag) {
12981     out.write(");");
12982 }
12983 </pre></code>
12984  *
12985  * @constructor
12986  * @param {Object} config A configuration object.
12987  */
12988 Roo.data.ScriptTagProxy = function(config){
12989     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12990     Roo.apply(this, config);
12991     this.head = document.getElementsByTagName("head")[0];
12992 };
12993
12994 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12995
12996 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12997     /**
12998      * @cfg {String} url The URL from which to request the data object.
12999      */
13000     /**
13001      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13002      */
13003     timeout : 30000,
13004     /**
13005      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13006      * the server the name of the callback function set up by the load call to process the returned data object.
13007      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13008      * javascript output which calls this named function passing the data object as its only parameter.
13009      */
13010     callbackParam : "callback",
13011     /**
13012      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13013      * name to the request.
13014      */
13015     nocache : true,
13016
13017     /**
13018      * Load data from the configured URL, read the data object into
13019      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13020      * process that block using the passed callback.
13021      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13022      * for the request to the remote server.
13023      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13024      * object into a block of Roo.data.Records.
13025      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13026      * The function must be passed <ul>
13027      * <li>The Record block object</li>
13028      * <li>The "arg" argument from the load function</li>
13029      * <li>A boolean success indicator</li>
13030      * </ul>
13031      * @param {Object} scope The scope in which to call the callback
13032      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13033      */
13034     load : function(params, reader, callback, scope, arg){
13035         if(this.fireEvent("beforeload", this, params) !== false){
13036
13037             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13038
13039             var url = this.url;
13040             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13041             if(this.nocache){
13042                 url += "&_dc=" + (new Date().getTime());
13043             }
13044             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13045             var trans = {
13046                 id : transId,
13047                 cb : "stcCallback"+transId,
13048                 scriptId : "stcScript"+transId,
13049                 params : params,
13050                 arg : arg,
13051                 url : url,
13052                 callback : callback,
13053                 scope : scope,
13054                 reader : reader
13055             };
13056             var conn = this;
13057
13058             window[trans.cb] = function(o){
13059                 conn.handleResponse(o, trans);
13060             };
13061
13062             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13063
13064             if(this.autoAbort !== false){
13065                 this.abort();
13066             }
13067
13068             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13069
13070             var script = document.createElement("script");
13071             script.setAttribute("src", url);
13072             script.setAttribute("type", "text/javascript");
13073             script.setAttribute("id", trans.scriptId);
13074             this.head.appendChild(script);
13075
13076             this.trans = trans;
13077         }else{
13078             callback.call(scope||this, null, arg, false);
13079         }
13080     },
13081
13082     // private
13083     isLoading : function(){
13084         return this.trans ? true : false;
13085     },
13086
13087     /**
13088      * Abort the current server request.
13089      */
13090     abort : function(){
13091         if(this.isLoading()){
13092             this.destroyTrans(this.trans);
13093         }
13094     },
13095
13096     // private
13097     destroyTrans : function(trans, isLoaded){
13098         this.head.removeChild(document.getElementById(trans.scriptId));
13099         clearTimeout(trans.timeoutId);
13100         if(isLoaded){
13101             window[trans.cb] = undefined;
13102             try{
13103                 delete window[trans.cb];
13104             }catch(e){}
13105         }else{
13106             // if hasn't been loaded, wait for load to remove it to prevent script error
13107             window[trans.cb] = function(){
13108                 window[trans.cb] = undefined;
13109                 try{
13110                     delete window[trans.cb];
13111                 }catch(e){}
13112             };
13113         }
13114     },
13115
13116     // private
13117     handleResponse : function(o, trans){
13118         this.trans = false;
13119         this.destroyTrans(trans, true);
13120         var result;
13121         try {
13122             result = trans.reader.readRecords(o);
13123         }catch(e){
13124             this.fireEvent("loadexception", this, o, trans.arg, e);
13125             trans.callback.call(trans.scope||window, null, trans.arg, false);
13126             return;
13127         }
13128         this.fireEvent("load", this, o, trans.arg);
13129         trans.callback.call(trans.scope||window, result, trans.arg, true);
13130     },
13131
13132     // private
13133     handleFailure : function(trans){
13134         this.trans = false;
13135         this.destroyTrans(trans, false);
13136         this.fireEvent("loadexception", this, null, trans.arg);
13137         trans.callback.call(trans.scope||window, null, trans.arg, false);
13138     }
13139 });/*
13140  * Based on:
13141  * Ext JS Library 1.1.1
13142  * Copyright(c) 2006-2007, Ext JS, LLC.
13143  *
13144  * Originally Released Under LGPL - original licence link has changed is not relivant.
13145  *
13146  * Fork - LGPL
13147  * <script type="text/javascript">
13148  */
13149
13150 /**
13151  * @class Roo.data.JsonReader
13152  * @extends Roo.data.DataReader
13153  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13154  * based on mappings in a provided Roo.data.Record constructor.
13155  * 
13156  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13157  * in the reply previously. 
13158  * 
13159  * <p>
13160  * Example code:
13161  * <pre><code>
13162 var RecordDef = Roo.data.Record.create([
13163     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13164     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13165 ]);
13166 var myReader = new Roo.data.JsonReader({
13167     totalProperty: "results",    // The property which contains the total dataset size (optional)
13168     root: "rows",                // The property which contains an Array of row objects
13169     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13170 }, RecordDef);
13171 </code></pre>
13172  * <p>
13173  * This would consume a JSON file like this:
13174  * <pre><code>
13175 { 'results': 2, 'rows': [
13176     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13177     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13178 }
13179 </code></pre>
13180  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13181  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13182  * paged from the remote server.
13183  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13184  * @cfg {String} root name of the property which contains the Array of row objects.
13185  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13186  * @cfg {Array} fields Array of field definition objects
13187  * @constructor
13188  * Create a new JsonReader
13189  * @param {Object} meta Metadata configuration options
13190  * @param {Object} recordType Either an Array of field definition objects,
13191  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13192  */
13193 Roo.data.JsonReader = function(meta, recordType){
13194     
13195     meta = meta || {};
13196     // set some defaults:
13197     Roo.applyIf(meta, {
13198         totalProperty: 'total',
13199         successProperty : 'success',
13200         root : 'data',
13201         id : 'id'
13202     });
13203     
13204     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13205 };
13206 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13207     
13208     readerType : 'Json',
13209     
13210     /**
13211      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13212      * Used by Store query builder to append _requestMeta to params.
13213      * 
13214      */
13215     metaFromRemote : false,
13216     /**
13217      * This method is only used by a DataProxy which has retrieved data from a remote server.
13218      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13219      * @return {Object} data A data block which is used by an Roo.data.Store object as
13220      * a cache of Roo.data.Records.
13221      */
13222     read : function(response){
13223         var json = response.responseText;
13224        
13225         var o = /* eval:var:o */ eval("("+json+")");
13226         if(!o) {
13227             throw {message: "JsonReader.read: Json object not found"};
13228         }
13229         
13230         if(o.metaData){
13231             
13232             delete this.ef;
13233             this.metaFromRemote = true;
13234             this.meta = o.metaData;
13235             this.recordType = Roo.data.Record.create(o.metaData.fields);
13236             this.onMetaChange(this.meta, this.recordType, o);
13237         }
13238         return this.readRecords(o);
13239     },
13240
13241     // private function a store will implement
13242     onMetaChange : function(meta, recordType, o){
13243
13244     },
13245
13246     /**
13247          * @ignore
13248          */
13249     simpleAccess: function(obj, subsc) {
13250         return obj[subsc];
13251     },
13252
13253         /**
13254          * @ignore
13255          */
13256     getJsonAccessor: function(){
13257         var re = /[\[\.]/;
13258         return function(expr) {
13259             try {
13260                 return(re.test(expr))
13261                     ? new Function("obj", "return obj." + expr)
13262                     : function(obj){
13263                         return obj[expr];
13264                     };
13265             } catch(e){}
13266             return Roo.emptyFn;
13267         };
13268     }(),
13269
13270     /**
13271      * Create a data block containing Roo.data.Records from an XML document.
13272      * @param {Object} o An object which contains an Array of row objects in the property specified
13273      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13274      * which contains the total size of the dataset.
13275      * @return {Object} data A data block which is used by an Roo.data.Store object as
13276      * a cache of Roo.data.Records.
13277      */
13278     readRecords : function(o){
13279         /**
13280          * After any data loads, the raw JSON data is available for further custom processing.
13281          * @type Object
13282          */
13283         this.o = o;
13284         var s = this.meta, Record = this.recordType,
13285             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13286
13287 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13288         if (!this.ef) {
13289             if(s.totalProperty) {
13290                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13291                 }
13292                 if(s.successProperty) {
13293                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13294                 }
13295                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13296                 if (s.id) {
13297                         var g = this.getJsonAccessor(s.id);
13298                         this.getId = function(rec) {
13299                                 var r = g(rec);  
13300                                 return (r === undefined || r === "") ? null : r;
13301                         };
13302                 } else {
13303                         this.getId = function(){return null;};
13304                 }
13305             this.ef = [];
13306             for(var jj = 0; jj < fl; jj++){
13307                 f = fi[jj];
13308                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13309                 this.ef[jj] = this.getJsonAccessor(map);
13310             }
13311         }
13312
13313         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13314         if(s.totalProperty){
13315             var vt = parseInt(this.getTotal(o), 10);
13316             if(!isNaN(vt)){
13317                 totalRecords = vt;
13318             }
13319         }
13320         if(s.successProperty){
13321             var vs = this.getSuccess(o);
13322             if(vs === false || vs === 'false'){
13323                 success = false;
13324             }
13325         }
13326         var records = [];
13327         for(var i = 0; i < c; i++){
13328                 var n = root[i];
13329             var values = {};
13330             var id = this.getId(n);
13331             for(var j = 0; j < fl; j++){
13332                 f = fi[j];
13333             var v = this.ef[j](n);
13334             if (!f.convert) {
13335                 Roo.log('missing convert for ' + f.name);
13336                 Roo.log(f);
13337                 continue;
13338             }
13339             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13340             }
13341             var record = new Record(values, id);
13342             record.json = n;
13343             records[i] = record;
13344         }
13345         return {
13346             raw : o,
13347             success : success,
13348             records : records,
13349             totalRecords : totalRecords
13350         };
13351     },
13352     // used when loading children.. @see loadDataFromChildren
13353     toLoadData: function(rec)
13354     {
13355         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13356         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13357         return { data : data, total : data.length };
13358         
13359     }
13360 });/*
13361  * Based on:
13362  * Ext JS Library 1.1.1
13363  * Copyright(c) 2006-2007, Ext JS, LLC.
13364  *
13365  * Originally Released Under LGPL - original licence link has changed is not relivant.
13366  *
13367  * Fork - LGPL
13368  * <script type="text/javascript">
13369  */
13370
13371 /**
13372  * @class Roo.data.ArrayReader
13373  * @extends Roo.data.DataReader
13374  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13375  * Each element of that Array represents a row of data fields. The
13376  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13377  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13378  * <p>
13379  * Example code:.
13380  * <pre><code>
13381 var RecordDef = Roo.data.Record.create([
13382     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13383     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13384 ]);
13385 var myReader = new Roo.data.ArrayReader({
13386     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13387 }, RecordDef);
13388 </code></pre>
13389  * <p>
13390  * This would consume an Array like this:
13391  * <pre><code>
13392 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13393   </code></pre>
13394  
13395  * @constructor
13396  * Create a new JsonReader
13397  * @param {Object} meta Metadata configuration options.
13398  * @param {Object|Array} recordType Either an Array of field definition objects
13399  * 
13400  * @cfg {Array} fields Array of field definition objects
13401  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13402  * as specified to {@link Roo.data.Record#create},
13403  * or an {@link Roo.data.Record} object
13404  *
13405  * 
13406  * created using {@link Roo.data.Record#create}.
13407  */
13408 Roo.data.ArrayReader = function(meta, recordType)
13409 {    
13410     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13411 };
13412
13413 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13414     
13415       /**
13416      * Create a data block containing Roo.data.Records from an XML document.
13417      * @param {Object} o An Array of row objects which represents the dataset.
13418      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13419      * a cache of Roo.data.Records.
13420      */
13421     readRecords : function(o)
13422     {
13423         var sid = this.meta ? this.meta.id : null;
13424         var recordType = this.recordType, fields = recordType.prototype.fields;
13425         var records = [];
13426         var root = o;
13427         for(var i = 0; i < root.length; i++){
13428                 var n = root[i];
13429             var values = {};
13430             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13431             for(var j = 0, jlen = fields.length; j < jlen; j++){
13432                 var f = fields.items[j];
13433                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13434                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13435                 v = f.convert(v);
13436                 values[f.name] = v;
13437             }
13438             var record = new recordType(values, id);
13439             record.json = n;
13440             records[records.length] = record;
13441         }
13442         return {
13443             records : records,
13444             totalRecords : records.length
13445         };
13446     },
13447     // used when loading children.. @see loadDataFromChildren
13448     toLoadData: function(rec)
13449     {
13450         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13451         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13452         
13453     }
13454     
13455     
13456 });/*
13457  * - LGPL
13458  * * 
13459  */
13460
13461 /**
13462  * @class Roo.bootstrap.ComboBox
13463  * @extends Roo.bootstrap.TriggerField
13464  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13465  * @cfg {Boolean} append (true|false) default false
13466  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13467  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13468  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13469  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13470  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13471  * @cfg {Boolean} animate default true
13472  * @cfg {Boolean} emptyResultText only for touch device
13473  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13474  * @cfg {String} emptyTitle default ''
13475  * @constructor
13476  * Create a new ComboBox.
13477  * @param {Object} config Configuration options
13478  */
13479 Roo.bootstrap.ComboBox = function(config){
13480     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13481     this.addEvents({
13482         /**
13483          * @event expand
13484          * Fires when the dropdown list is expanded
13485         * @param {Roo.bootstrap.ComboBox} combo This combo box
13486         */
13487         'expand' : true,
13488         /**
13489          * @event collapse
13490          * Fires when the dropdown list is collapsed
13491         * @param {Roo.bootstrap.ComboBox} combo This combo box
13492         */
13493         'collapse' : true,
13494         /**
13495          * @event beforeselect
13496          * Fires before a list item is selected. Return false to cancel the selection.
13497         * @param {Roo.bootstrap.ComboBox} combo This combo box
13498         * @param {Roo.data.Record} record The data record returned from the underlying store
13499         * @param {Number} index The index of the selected item in the dropdown list
13500         */
13501         'beforeselect' : true,
13502         /**
13503          * @event select
13504          * Fires when a list item is selected
13505         * @param {Roo.bootstrap.ComboBox} combo This combo box
13506         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13507         * @param {Number} index The index of the selected item in the dropdown list
13508         */
13509         'select' : true,
13510         /**
13511          * @event beforequery
13512          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13513          * The event object passed has these properties:
13514         * @param {Roo.bootstrap.ComboBox} combo This combo box
13515         * @param {String} query The query
13516         * @param {Boolean} forceAll true to force "all" query
13517         * @param {Boolean} cancel true to cancel the query
13518         * @param {Object} e The query event object
13519         */
13520         'beforequery': true,
13521          /**
13522          * @event add
13523          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13524         * @param {Roo.bootstrap.ComboBox} combo This combo box
13525         */
13526         'add' : true,
13527         /**
13528          * @event edit
13529          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13530         * @param {Roo.bootstrap.ComboBox} combo This combo box
13531         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13532         */
13533         'edit' : true,
13534         /**
13535          * @event remove
13536          * Fires when the remove value from the combobox array
13537         * @param {Roo.bootstrap.ComboBox} combo This combo box
13538         */
13539         'remove' : true,
13540         /**
13541          * @event afterremove
13542          * Fires when the remove value from the combobox array
13543         * @param {Roo.bootstrap.ComboBox} combo This combo box
13544         */
13545         'afterremove' : true,
13546         /**
13547          * @event specialfilter
13548          * Fires when specialfilter
13549             * @param {Roo.bootstrap.ComboBox} combo This combo box
13550             */
13551         'specialfilter' : true,
13552         /**
13553          * @event tick
13554          * Fires when tick the element
13555             * @param {Roo.bootstrap.ComboBox} combo This combo box
13556             */
13557         'tick' : true,
13558         /**
13559          * @event touchviewdisplay
13560          * Fires when touch view require special display (default is using displayField)
13561             * @param {Roo.bootstrap.ComboBox} combo This combo box
13562             * @param {Object} cfg set html .
13563             */
13564         'touchviewdisplay' : true
13565         
13566     });
13567     
13568     this.item = [];
13569     this.tickItems = [];
13570     
13571     this.selectedIndex = -1;
13572     if(this.mode == 'local'){
13573         if(config.queryDelay === undefined){
13574             this.queryDelay = 10;
13575         }
13576         if(config.minChars === undefined){
13577             this.minChars = 0;
13578         }
13579     }
13580 };
13581
13582 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13583      
13584     /**
13585      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13586      * rendering into an Roo.Editor, defaults to false)
13587      */
13588     /**
13589      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13590      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13591      */
13592     /**
13593      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13594      */
13595     /**
13596      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13597      * the dropdown list (defaults to undefined, with no header element)
13598      */
13599
13600      /**
13601      * @cfg {String/Roo.Template} tpl The template to use to render the output
13602      */
13603      
13604      /**
13605      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13606      */
13607     listWidth: undefined,
13608     /**
13609      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13610      * mode = 'remote' or 'text' if mode = 'local')
13611      */
13612     displayField: undefined,
13613     
13614     /**
13615      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13616      * mode = 'remote' or 'value' if mode = 'local'). 
13617      * Note: use of a valueField requires the user make a selection
13618      * in order for a value to be mapped.
13619      */
13620     valueField: undefined,
13621     /**
13622      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13623      */
13624     modalTitle : '',
13625     
13626     /**
13627      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13628      * field's data value (defaults to the underlying DOM element's name)
13629      */
13630     hiddenName: undefined,
13631     /**
13632      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13633      */
13634     listClass: '',
13635     /**
13636      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13637      */
13638     selectedClass: 'active',
13639     
13640     /**
13641      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13642      */
13643     shadow:'sides',
13644     /**
13645      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13646      * anchor positions (defaults to 'tl-bl')
13647      */
13648     listAlign: 'tl-bl?',
13649     /**
13650      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13651      */
13652     maxHeight: 300,
13653     /**
13654      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13655      * query specified by the allQuery config option (defaults to 'query')
13656      */
13657     triggerAction: 'query',
13658     /**
13659      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13660      * (defaults to 4, does not apply if editable = false)
13661      */
13662     minChars : 4,
13663     /**
13664      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13665      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13666      */
13667     typeAhead: false,
13668     /**
13669      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13670      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13671      */
13672     queryDelay: 500,
13673     /**
13674      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13675      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13676      */
13677     pageSize: 0,
13678     /**
13679      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13680      * when editable = true (defaults to false)
13681      */
13682     selectOnFocus:false,
13683     /**
13684      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13685      */
13686     queryParam: 'query',
13687     /**
13688      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13689      * when mode = 'remote' (defaults to 'Loading...')
13690      */
13691     loadingText: 'Loading...',
13692     /**
13693      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13694      */
13695     resizable: false,
13696     /**
13697      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13698      */
13699     handleHeight : 8,
13700     /**
13701      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13702      * traditional select (defaults to true)
13703      */
13704     editable: true,
13705     /**
13706      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13707      */
13708     allQuery: '',
13709     /**
13710      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13711      */
13712     mode: 'remote',
13713     /**
13714      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13715      * listWidth has a higher value)
13716      */
13717     minListWidth : 70,
13718     /**
13719      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13720      * allow the user to set arbitrary text into the field (defaults to false)
13721      */
13722     forceSelection:false,
13723     /**
13724      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13725      * if typeAhead = true (defaults to 250)
13726      */
13727     typeAheadDelay : 250,
13728     /**
13729      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13730      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13731      */
13732     valueNotFoundText : undefined,
13733     /**
13734      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13735      */
13736     blockFocus : false,
13737     
13738     /**
13739      * @cfg {Boolean} disableClear Disable showing of clear button.
13740      */
13741     disableClear : false,
13742     /**
13743      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13744      */
13745     alwaysQuery : false,
13746     
13747     /**
13748      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13749      */
13750     multiple : false,
13751     
13752     /**
13753      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13754      */
13755     invalidClass : "has-warning",
13756     
13757     /**
13758      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13759      */
13760     validClass : "has-success",
13761     
13762     /**
13763      * @cfg {Boolean} specialFilter (true|false) special filter default false
13764      */
13765     specialFilter : false,
13766     
13767     /**
13768      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13769      */
13770     mobileTouchView : true,
13771     
13772     /**
13773      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13774      */
13775     useNativeIOS : false,
13776     
13777     /**
13778      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13779      */
13780     mobile_restrict_height : false,
13781     
13782     ios_options : false,
13783     
13784     //private
13785     addicon : false,
13786     editicon: false,
13787     
13788     page: 0,
13789     hasQuery: false,
13790     append: false,
13791     loadNext: false,
13792     autoFocus : true,
13793     tickable : false,
13794     btnPosition : 'right',
13795     triggerList : true,
13796     showToggleBtn : true,
13797     animate : true,
13798     emptyResultText: 'Empty',
13799     triggerText : 'Select',
13800     emptyTitle : '',
13801     
13802     // element that contains real text value.. (when hidden is used..)
13803     
13804     getAutoCreate : function()
13805     {   
13806         var cfg = false;
13807         //render
13808         /*
13809          * Render classic select for iso
13810          */
13811         
13812         if(Roo.isIOS && this.useNativeIOS){
13813             cfg = this.getAutoCreateNativeIOS();
13814             return cfg;
13815         }
13816         
13817         /*
13818          * Touch Devices
13819          */
13820         
13821         if(Roo.isTouch && this.mobileTouchView){
13822             cfg = this.getAutoCreateTouchView();
13823             return cfg;;
13824         }
13825         
13826         /*
13827          *  Normal ComboBox
13828          */
13829         if(!this.tickable){
13830             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13831             return cfg;
13832         }
13833         
13834         /*
13835          *  ComboBox with tickable selections
13836          */
13837              
13838         var align = this.labelAlign || this.parentLabelAlign();
13839         
13840         cfg = {
13841             cls : 'form-group roo-combobox-tickable' //input-group
13842         };
13843         
13844         var btn_text_select = '';
13845         var btn_text_done = '';
13846         var btn_text_cancel = '';
13847         
13848         if (this.btn_text_show) {
13849             btn_text_select = 'Select';
13850             btn_text_done = 'Done';
13851             btn_text_cancel = 'Cancel'; 
13852         }
13853         
13854         var buttons = {
13855             tag : 'div',
13856             cls : 'tickable-buttons',
13857             cn : [
13858                 {
13859                     tag : 'button',
13860                     type : 'button',
13861                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13862                     //html : this.triggerText
13863                     html: btn_text_select
13864                 },
13865                 {
13866                     tag : 'button',
13867                     type : 'button',
13868                     name : 'ok',
13869                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13870                     //html : 'Done'
13871                     html: btn_text_done
13872                 },
13873                 {
13874                     tag : 'button',
13875                     type : 'button',
13876                     name : 'cancel',
13877                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13878                     //html : 'Cancel'
13879                     html: btn_text_cancel
13880                 }
13881             ]
13882         };
13883         
13884         if(this.editable){
13885             buttons.cn.unshift({
13886                 tag: 'input',
13887                 cls: 'roo-select2-search-field-input'
13888             });
13889         }
13890         
13891         var _this = this;
13892         
13893         Roo.each(buttons.cn, function(c){
13894             if (_this.size) {
13895                 c.cls += ' btn-' + _this.size;
13896             }
13897
13898             if (_this.disabled) {
13899                 c.disabled = true;
13900             }
13901         });
13902         
13903         var box = {
13904             tag: 'div',
13905             style : 'display: contents',
13906             cn: [
13907                 {
13908                     tag: 'input',
13909                     type : 'hidden',
13910                     cls: 'form-hidden-field'
13911                 },
13912                 {
13913                     tag: 'ul',
13914                     cls: 'roo-select2-choices',
13915                     cn:[
13916                         {
13917                             tag: 'li',
13918                             cls: 'roo-select2-search-field',
13919                             cn: [
13920                                 buttons
13921                             ]
13922                         }
13923                     ]
13924                 }
13925             ]
13926         };
13927         
13928         var combobox = {
13929             cls: 'roo-select2-container input-group roo-select2-container-multi',
13930             cn: [
13931                 
13932                 box
13933 //                {
13934 //                    tag: 'ul',
13935 //                    cls: 'typeahead typeahead-long dropdown-menu',
13936 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13937 //                }
13938             ]
13939         };
13940         
13941         if(this.hasFeedback && !this.allowBlank){
13942             
13943             var feedback = {
13944                 tag: 'span',
13945                 cls: 'glyphicon form-control-feedback'
13946             };
13947
13948             combobox.cn.push(feedback);
13949         }
13950         
13951         var indicator = {
13952             tag : 'i',
13953             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13954             tooltip : 'This field is required'
13955         };
13956         if (Roo.bootstrap.version == 4) {
13957             indicator = {
13958                 tag : 'i',
13959                 style : 'display:none'
13960             };
13961         }
13962         if (align ==='left' && this.fieldLabel.length) {
13963             
13964             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13965             
13966             cfg.cn = [
13967                 indicator,
13968                 {
13969                     tag: 'label',
13970                     'for' :  id,
13971                     cls : 'control-label col-form-label',
13972                     html : this.fieldLabel
13973
13974                 },
13975                 {
13976                     cls : "", 
13977                     cn: [
13978                         combobox
13979                     ]
13980                 }
13981
13982             ];
13983             
13984             var labelCfg = cfg.cn[1];
13985             var contentCfg = cfg.cn[2];
13986             
13987
13988             if(this.indicatorpos == 'right'){
13989                 
13990                 cfg.cn = [
13991                     {
13992                         tag: 'label',
13993                         'for' :  id,
13994                         cls : 'control-label col-form-label',
13995                         cn : [
13996                             {
13997                                 tag : 'span',
13998                                 html : this.fieldLabel
13999                             },
14000                             indicator
14001                         ]
14002                     },
14003                     {
14004                         cls : "",
14005                         cn: [
14006                             combobox
14007                         ]
14008                     }
14009
14010                 ];
14011                 
14012                 
14013                 
14014                 labelCfg = cfg.cn[0];
14015                 contentCfg = cfg.cn[1];
14016             
14017             }
14018             
14019             if(this.labelWidth > 12){
14020                 labelCfg.style = "width: " + this.labelWidth + 'px';
14021             }
14022             
14023             if(this.labelWidth < 13 && this.labelmd == 0){
14024                 this.labelmd = this.labelWidth;
14025             }
14026             
14027             if(this.labellg > 0){
14028                 labelCfg.cls += ' col-lg-' + this.labellg;
14029                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14030             }
14031             
14032             if(this.labelmd > 0){
14033                 labelCfg.cls += ' col-md-' + this.labelmd;
14034                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14035             }
14036             
14037             if(this.labelsm > 0){
14038                 labelCfg.cls += ' col-sm-' + this.labelsm;
14039                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14040             }
14041             
14042             if(this.labelxs > 0){
14043                 labelCfg.cls += ' col-xs-' + this.labelxs;
14044                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14045             }
14046                 
14047                 
14048         } else if ( this.fieldLabel.length) {
14049 //                Roo.log(" label");
14050                  cfg.cn = [
14051                    indicator,
14052                     {
14053                         tag: 'label',
14054                         //cls : 'input-group-addon',
14055                         html : this.fieldLabel
14056                     },
14057                     combobox
14058                 ];
14059                 
14060                 if(this.indicatorpos == 'right'){
14061                     cfg.cn = [
14062                         {
14063                             tag: 'label',
14064                             //cls : 'input-group-addon',
14065                             html : this.fieldLabel
14066                         },
14067                         indicator,
14068                         combobox
14069                     ];
14070                     
14071                 }
14072
14073         } else {
14074             
14075 //                Roo.log(" no label && no align");
14076                 cfg = combobox
14077                      
14078                 
14079         }
14080          
14081         var settings=this;
14082         ['xs','sm','md','lg'].map(function(size){
14083             if (settings[size]) {
14084                 cfg.cls += ' col-' + size + '-' + settings[size];
14085             }
14086         });
14087         
14088         return cfg;
14089         
14090     },
14091     
14092     _initEventsCalled : false,
14093     
14094     // private
14095     initEvents: function()
14096     {   
14097         if (this._initEventsCalled) { // as we call render... prevent looping...
14098             return;
14099         }
14100         this._initEventsCalled = true;
14101         
14102         if (!this.store) {
14103             throw "can not find store for combo";
14104         }
14105         
14106         this.indicator = this.indicatorEl();
14107         
14108         this.store = Roo.factory(this.store, Roo.data);
14109         this.store.parent = this;
14110         
14111         // if we are building from html. then this element is so complex, that we can not really
14112         // use the rendered HTML.
14113         // so we have to trash and replace the previous code.
14114         if (Roo.XComponent.build_from_html) {
14115             // remove this element....
14116             var e = this.el.dom, k=0;
14117             while (e ) { e = e.previousSibling;  ++k;}
14118
14119             this.el.remove();
14120             
14121             this.el=false;
14122             this.rendered = false;
14123             
14124             this.render(this.parent().getChildContainer(true), k);
14125         }
14126         
14127         if(Roo.isIOS && this.useNativeIOS){
14128             this.initIOSView();
14129             return;
14130         }
14131         
14132         /*
14133          * Touch Devices
14134          */
14135         
14136         if(Roo.isTouch && this.mobileTouchView){
14137             this.initTouchView();
14138             return;
14139         }
14140         
14141         if(this.tickable){
14142             this.initTickableEvents();
14143             return;
14144         }
14145         
14146         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14147         
14148         if(this.hiddenName){
14149             
14150             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14151             
14152             this.hiddenField.dom.value =
14153                 this.hiddenValue !== undefined ? this.hiddenValue :
14154                 this.value !== undefined ? this.value : '';
14155
14156             // prevent input submission
14157             this.el.dom.removeAttribute('name');
14158             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14159              
14160              
14161         }
14162         //if(Roo.isGecko){
14163         //    this.el.dom.setAttribute('autocomplete', 'off');
14164         //}
14165         
14166         var cls = 'x-combo-list';
14167         
14168         //this.list = new Roo.Layer({
14169         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14170         //});
14171         
14172         var _this = this;
14173         
14174         (function(){
14175             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14176             _this.list.setWidth(lw);
14177         }).defer(100);
14178         
14179         this.list.on('mouseover', this.onViewOver, this);
14180         this.list.on('mousemove', this.onViewMove, this);
14181         this.list.on('scroll', this.onViewScroll, this);
14182         
14183         /*
14184         this.list.swallowEvent('mousewheel');
14185         this.assetHeight = 0;
14186
14187         if(this.title){
14188             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14189             this.assetHeight += this.header.getHeight();
14190         }
14191
14192         this.innerList = this.list.createChild({cls:cls+'-inner'});
14193         this.innerList.on('mouseover', this.onViewOver, this);
14194         this.innerList.on('mousemove', this.onViewMove, this);
14195         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14196         
14197         if(this.allowBlank && !this.pageSize && !this.disableClear){
14198             this.footer = this.list.createChild({cls:cls+'-ft'});
14199             this.pageTb = new Roo.Toolbar(this.footer);
14200            
14201         }
14202         if(this.pageSize){
14203             this.footer = this.list.createChild({cls:cls+'-ft'});
14204             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14205                     {pageSize: this.pageSize});
14206             
14207         }
14208         
14209         if (this.pageTb && this.allowBlank && !this.disableClear) {
14210             var _this = this;
14211             this.pageTb.add(new Roo.Toolbar.Fill(), {
14212                 cls: 'x-btn-icon x-btn-clear',
14213                 text: '&#160;',
14214                 handler: function()
14215                 {
14216                     _this.collapse();
14217                     _this.clearValue();
14218                     _this.onSelect(false, -1);
14219                 }
14220             });
14221         }
14222         if (this.footer) {
14223             this.assetHeight += this.footer.getHeight();
14224         }
14225         */
14226             
14227         if(!this.tpl){
14228             this.tpl = Roo.bootstrap.version == 4 ?
14229                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14230                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14231         }
14232
14233         this.view = new Roo.View(this.list, this.tpl, {
14234             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14235         });
14236         //this.view.wrapEl.setDisplayed(false);
14237         this.view.on('click', this.onViewClick, this);
14238         
14239         
14240         this.store.on('beforeload', this.onBeforeLoad, this);
14241         this.store.on('load', this.onLoad, this);
14242         this.store.on('loadexception', this.onLoadException, this);
14243         /*
14244         if(this.resizable){
14245             this.resizer = new Roo.Resizable(this.list,  {
14246                pinned:true, handles:'se'
14247             });
14248             this.resizer.on('resize', function(r, w, h){
14249                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14250                 this.listWidth = w;
14251                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14252                 this.restrictHeight();
14253             }, this);
14254             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14255         }
14256         */
14257         if(!this.editable){
14258             this.editable = true;
14259             this.setEditable(false);
14260         }
14261         
14262         /*
14263         
14264         if (typeof(this.events.add.listeners) != 'undefined') {
14265             
14266             this.addicon = this.wrap.createChild(
14267                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14268        
14269             this.addicon.on('click', function(e) {
14270                 this.fireEvent('add', this);
14271             }, this);
14272         }
14273         if (typeof(this.events.edit.listeners) != 'undefined') {
14274             
14275             this.editicon = this.wrap.createChild(
14276                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14277             if (this.addicon) {
14278                 this.editicon.setStyle('margin-left', '40px');
14279             }
14280             this.editicon.on('click', function(e) {
14281                 
14282                 // we fire even  if inothing is selected..
14283                 this.fireEvent('edit', this, this.lastData );
14284                 
14285             }, this);
14286         }
14287         */
14288         
14289         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14290             "up" : function(e){
14291                 this.inKeyMode = true;
14292                 this.selectPrev();
14293             },
14294
14295             "down" : function(e){
14296                 if(!this.isExpanded()){
14297                     this.onTriggerClick();
14298                 }else{
14299                     this.inKeyMode = true;
14300                     this.selectNext();
14301                 }
14302             },
14303
14304             "enter" : function(e){
14305 //                this.onViewClick();
14306                 //return true;
14307                 this.collapse();
14308                 
14309                 if(this.fireEvent("specialkey", this, e)){
14310                     this.onViewClick(false);
14311                 }
14312                 
14313                 return true;
14314             },
14315
14316             "esc" : function(e){
14317                 this.collapse();
14318             },
14319
14320             "tab" : function(e){
14321                 this.collapse();
14322                 
14323                 if(this.fireEvent("specialkey", this, e)){
14324                     this.onViewClick(false);
14325                 }
14326                 
14327                 return true;
14328             },
14329
14330             scope : this,
14331
14332             doRelay : function(foo, bar, hname){
14333                 if(hname == 'down' || this.scope.isExpanded()){
14334                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14335                 }
14336                 return true;
14337             },
14338
14339             forceKeyDown: true
14340         });
14341         
14342         
14343         this.queryDelay = Math.max(this.queryDelay || 10,
14344                 this.mode == 'local' ? 10 : 250);
14345         
14346         
14347         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14348         
14349         if(this.typeAhead){
14350             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14351         }
14352         if(this.editable !== false){
14353             this.inputEl().on("keyup", this.onKeyUp, this);
14354         }
14355         if(this.forceSelection){
14356             this.inputEl().on('blur', this.doForce, this);
14357         }
14358         
14359         if(this.multiple){
14360             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14361             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14362         }
14363     },
14364     
14365     initTickableEvents: function()
14366     {   
14367         this.createList();
14368         
14369         if(this.hiddenName){
14370             
14371             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14372             
14373             this.hiddenField.dom.value =
14374                 this.hiddenValue !== undefined ? this.hiddenValue :
14375                 this.value !== undefined ? this.value : '';
14376
14377             // prevent input submission
14378             this.el.dom.removeAttribute('name');
14379             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14380              
14381              
14382         }
14383         
14384 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14385         
14386         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14387         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14388         if(this.triggerList){
14389             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14390         }
14391          
14392         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14393         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14394         
14395         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14396         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14397         
14398         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14399         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14400         
14401         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14402         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14403         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14404         
14405         this.okBtn.hide();
14406         this.cancelBtn.hide();
14407         
14408         var _this = this;
14409         
14410         (function(){
14411             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14412             _this.list.setWidth(lw);
14413         }).defer(100);
14414         
14415         this.list.on('mouseover', this.onViewOver, this);
14416         this.list.on('mousemove', this.onViewMove, this);
14417         
14418         this.list.on('scroll', this.onViewScroll, this);
14419         
14420         if(!this.tpl){
14421             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14422                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14423         }
14424
14425         this.view = new Roo.View(this.list, this.tpl, {
14426             singleSelect:true,
14427             tickable:true,
14428             parent:this,
14429             store: this.store,
14430             selectedClass: this.selectedClass
14431         });
14432         
14433         //this.view.wrapEl.setDisplayed(false);
14434         this.view.on('click', this.onViewClick, this);
14435         
14436         
14437         
14438         this.store.on('beforeload', this.onBeforeLoad, this);
14439         this.store.on('load', this.onLoad, this);
14440         this.store.on('loadexception', this.onLoadException, this);
14441         
14442         if(this.editable){
14443             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14444                 "up" : function(e){
14445                     this.inKeyMode = true;
14446                     this.selectPrev();
14447                 },
14448
14449                 "down" : function(e){
14450                     this.inKeyMode = true;
14451                     this.selectNext();
14452                 },
14453
14454                 "enter" : function(e){
14455                     if(this.fireEvent("specialkey", this, e)){
14456                         this.onViewClick(false);
14457                     }
14458                     
14459                     return true;
14460                 },
14461
14462                 "esc" : function(e){
14463                     this.onTickableFooterButtonClick(e, false, false);
14464                 },
14465
14466                 "tab" : function(e){
14467                     this.fireEvent("specialkey", this, e);
14468                     
14469                     this.onTickableFooterButtonClick(e, false, false);
14470                     
14471                     return true;
14472                 },
14473
14474                 scope : this,
14475
14476                 doRelay : function(e, fn, key){
14477                     if(this.scope.isExpanded()){
14478                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14479                     }
14480                     return true;
14481                 },
14482
14483                 forceKeyDown: true
14484             });
14485         }
14486         
14487         this.queryDelay = Math.max(this.queryDelay || 10,
14488                 this.mode == 'local' ? 10 : 250);
14489         
14490         
14491         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14492         
14493         if(this.typeAhead){
14494             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14495         }
14496         
14497         if(this.editable !== false){
14498             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14499         }
14500         
14501         this.indicator = this.indicatorEl();
14502         
14503         if(this.indicator){
14504             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14505             this.indicator.hide();
14506         }
14507         
14508     },
14509
14510     onDestroy : function(){
14511         if(this.view){
14512             this.view.setStore(null);
14513             this.view.el.removeAllListeners();
14514             this.view.el.remove();
14515             this.view.purgeListeners();
14516         }
14517         if(this.list){
14518             this.list.dom.innerHTML  = '';
14519         }
14520         
14521         if(this.store){
14522             this.store.un('beforeload', this.onBeforeLoad, this);
14523             this.store.un('load', this.onLoad, this);
14524             this.store.un('loadexception', this.onLoadException, this);
14525         }
14526         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14527     },
14528
14529     // private
14530     fireKey : function(e){
14531         if(e.isNavKeyPress() && !this.list.isVisible()){
14532             this.fireEvent("specialkey", this, e);
14533         }
14534     },
14535
14536     // private
14537     onResize: function(w, h){
14538 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14539 //        
14540 //        if(typeof w != 'number'){
14541 //            // we do not handle it!?!?
14542 //            return;
14543 //        }
14544 //        var tw = this.trigger.getWidth();
14545 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14546 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14547 //        var x = w - tw;
14548 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14549 //            
14550 //        //this.trigger.setStyle('left', x+'px');
14551 //        
14552 //        if(this.list && this.listWidth === undefined){
14553 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14554 //            this.list.setWidth(lw);
14555 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14556 //        }
14557         
14558     
14559         
14560     },
14561
14562     /**
14563      * Allow or prevent the user from directly editing the field text.  If false is passed,
14564      * the user will only be able to select from the items defined in the dropdown list.  This method
14565      * is the runtime equivalent of setting the 'editable' config option at config time.
14566      * @param {Boolean} value True to allow the user to directly edit the field text
14567      */
14568     setEditable : function(value){
14569         if(value == this.editable){
14570             return;
14571         }
14572         this.editable = value;
14573         if(!value){
14574             this.inputEl().dom.setAttribute('readOnly', true);
14575             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14576             this.inputEl().addClass('x-combo-noedit');
14577         }else{
14578             this.inputEl().dom.setAttribute('readOnly', false);
14579             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14580             this.inputEl().removeClass('x-combo-noedit');
14581         }
14582     },
14583
14584     // private
14585     
14586     onBeforeLoad : function(combo,opts){
14587         if(!this.hasFocus){
14588             return;
14589         }
14590          if (!opts.add) {
14591             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14592          }
14593         this.restrictHeight();
14594         this.selectedIndex = -1;
14595     },
14596
14597     // private
14598     onLoad : function(){
14599         
14600         this.hasQuery = false;
14601         
14602         if(!this.hasFocus){
14603             return;
14604         }
14605         
14606         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14607             this.loading.hide();
14608         }
14609         
14610         if(this.store.getCount() > 0){
14611             
14612             this.expand();
14613             this.restrictHeight();
14614             if(this.lastQuery == this.allQuery){
14615                 if(this.editable && !this.tickable){
14616                     this.inputEl().dom.select();
14617                 }
14618                 
14619                 if(
14620                     !this.selectByValue(this.value, true) &&
14621                     this.autoFocus && 
14622                     (
14623                         !this.store.lastOptions ||
14624                         typeof(this.store.lastOptions.add) == 'undefined' || 
14625                         this.store.lastOptions.add != true
14626                     )
14627                 ){
14628                     this.select(0, true);
14629                 }
14630             }else{
14631                 if(this.autoFocus){
14632                     this.selectNext();
14633                 }
14634                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14635                     this.taTask.delay(this.typeAheadDelay);
14636                 }
14637             }
14638         }else{
14639             this.onEmptyResults();
14640         }
14641         
14642         //this.el.focus();
14643     },
14644     // private
14645     onLoadException : function()
14646     {
14647         this.hasQuery = false;
14648         
14649         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14650             this.loading.hide();
14651         }
14652         
14653         if(this.tickable && this.editable){
14654             return;
14655         }
14656         
14657         this.collapse();
14658         // only causes errors at present
14659         //Roo.log(this.store.reader.jsonData);
14660         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14661             // fixme
14662             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14663         //}
14664         
14665         
14666     },
14667     // private
14668     onTypeAhead : function(){
14669         if(this.store.getCount() > 0){
14670             var r = this.store.getAt(0);
14671             var newValue = r.data[this.displayField];
14672             var len = newValue.length;
14673             var selStart = this.getRawValue().length;
14674             
14675             if(selStart != len){
14676                 this.setRawValue(newValue);
14677                 this.selectText(selStart, newValue.length);
14678             }
14679         }
14680     },
14681
14682     // private
14683     onSelect : function(record, index){
14684         
14685         if(this.fireEvent('beforeselect', this, record, index) !== false){
14686         
14687             this.setFromData(index > -1 ? record.data : false);
14688             
14689             this.collapse();
14690             this.fireEvent('select', this, record, index);
14691         }
14692     },
14693
14694     /**
14695      * Returns the currently selected field value or empty string if no value is set.
14696      * @return {String} value The selected value
14697      */
14698     getValue : function()
14699     {
14700         if(Roo.isIOS && this.useNativeIOS){
14701             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14702         }
14703         
14704         if(this.multiple){
14705             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14706         }
14707         
14708         if(this.valueField){
14709             return typeof this.value != 'undefined' ? this.value : '';
14710         }else{
14711             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14712         }
14713     },
14714     
14715     getRawValue : function()
14716     {
14717         if(Roo.isIOS && this.useNativeIOS){
14718             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14719         }
14720         
14721         var v = this.inputEl().getValue();
14722         
14723         return v;
14724     },
14725
14726     /**
14727      * Clears any text/value currently set in the field
14728      */
14729     clearValue : function(){
14730         
14731         if(this.hiddenField){
14732             this.hiddenField.dom.value = '';
14733         }
14734         this.value = '';
14735         this.setRawValue('');
14736         this.lastSelectionText = '';
14737         this.lastData = false;
14738         
14739         var close = this.closeTriggerEl();
14740         
14741         if(close){
14742             close.hide();
14743         }
14744         
14745         this.validate();
14746         
14747     },
14748
14749     /**
14750      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14751      * will be displayed in the field.  If the value does not match the data value of an existing item,
14752      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14753      * Otherwise the field will be blank (although the value will still be set).
14754      * @param {String} value The value to match
14755      */
14756     setValue : function(v)
14757     {
14758         if(Roo.isIOS && this.useNativeIOS){
14759             this.setIOSValue(v);
14760             return;
14761         }
14762         
14763         if(this.multiple){
14764             this.syncValue();
14765             return;
14766         }
14767         
14768         var text = v;
14769         if(this.valueField){
14770             var r = this.findRecord(this.valueField, v);
14771             if(r){
14772                 text = r.data[this.displayField];
14773             }else if(this.valueNotFoundText !== undefined){
14774                 text = this.valueNotFoundText;
14775             }
14776         }
14777         this.lastSelectionText = text;
14778         if(this.hiddenField){
14779             this.hiddenField.dom.value = v;
14780         }
14781         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14782         this.value = v;
14783         
14784         var close = this.closeTriggerEl();
14785         
14786         if(close){
14787             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14788         }
14789         
14790         this.validate();
14791     },
14792     /**
14793      * @property {Object} the last set data for the element
14794      */
14795     
14796     lastData : false,
14797     /**
14798      * Sets the value of the field based on a object which is related to the record format for the store.
14799      * @param {Object} value the value to set as. or false on reset?
14800      */
14801     setFromData : function(o){
14802         
14803         if(this.multiple){
14804             this.addItem(o);
14805             return;
14806         }
14807             
14808         var dv = ''; // display value
14809         var vv = ''; // value value..
14810         this.lastData = o;
14811         if (this.displayField) {
14812             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14813         } else {
14814             // this is an error condition!!!
14815             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14816         }
14817         
14818         if(this.valueField){
14819             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14820         }
14821         
14822         var close = this.closeTriggerEl();
14823         
14824         if(close){
14825             if(dv.length || vv * 1 > 0){
14826                 close.show() ;
14827                 this.blockFocus=true;
14828             } else {
14829                 close.hide();
14830             }             
14831         }
14832         
14833         if(this.hiddenField){
14834             this.hiddenField.dom.value = vv;
14835             
14836             this.lastSelectionText = dv;
14837             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14838             this.value = vv;
14839             return;
14840         }
14841         // no hidden field.. - we store the value in 'value', but still display
14842         // display field!!!!
14843         this.lastSelectionText = dv;
14844         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14845         this.value = vv;
14846         
14847         
14848         
14849     },
14850     // private
14851     reset : function(){
14852         // overridden so that last data is reset..
14853         
14854         if(this.multiple){
14855             this.clearItem();
14856             return;
14857         }
14858         
14859         this.setValue(this.originalValue);
14860         //this.clearInvalid();
14861         this.lastData = false;
14862         if (this.view) {
14863             this.view.clearSelections();
14864         }
14865         
14866         this.validate();
14867     },
14868     // private
14869     findRecord : function(prop, value){
14870         var record;
14871         if(this.store.getCount() > 0){
14872             this.store.each(function(r){
14873                 if(r.data[prop] == value){
14874                     record = r;
14875                     return false;
14876                 }
14877                 return true;
14878             });
14879         }
14880         return record;
14881     },
14882     
14883     getName: function()
14884     {
14885         // returns hidden if it's set..
14886         if (!this.rendered) {return ''};
14887         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14888         
14889     },
14890     // private
14891     onViewMove : function(e, t){
14892         this.inKeyMode = false;
14893     },
14894
14895     // private
14896     onViewOver : function(e, t){
14897         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14898             return;
14899         }
14900         var item = this.view.findItemFromChild(t);
14901         
14902         if(item){
14903             var index = this.view.indexOf(item);
14904             this.select(index, false);
14905         }
14906     },
14907
14908     // private
14909     onViewClick : function(view, doFocus, el, e)
14910     {
14911         var index = this.view.getSelectedIndexes()[0];
14912         
14913         var r = this.store.getAt(index);
14914         
14915         if(this.tickable){
14916             
14917             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14918                 return;
14919             }
14920             
14921             var rm = false;
14922             var _this = this;
14923             
14924             Roo.each(this.tickItems, function(v,k){
14925                 
14926                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14927                     Roo.log(v);
14928                     _this.tickItems.splice(k, 1);
14929                     
14930                     if(typeof(e) == 'undefined' && view == false){
14931                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14932                     }
14933                     
14934                     rm = true;
14935                     return;
14936                 }
14937             });
14938             
14939             if(rm){
14940                 return;
14941             }
14942             
14943             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14944                 this.tickItems.push(r.data);
14945             }
14946             
14947             if(typeof(e) == 'undefined' && view == false){
14948                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14949             }
14950                     
14951             return;
14952         }
14953         
14954         if(r){
14955             this.onSelect(r, index);
14956         }
14957         if(doFocus !== false && !this.blockFocus){
14958             this.inputEl().focus();
14959         }
14960     },
14961
14962     // private
14963     restrictHeight : function(){
14964         //this.innerList.dom.style.height = '';
14965         //var inner = this.innerList.dom;
14966         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14967         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14968         //this.list.beginUpdate();
14969         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14970         this.list.alignTo(this.inputEl(), this.listAlign);
14971         this.list.alignTo(this.inputEl(), this.listAlign);
14972         //this.list.endUpdate();
14973     },
14974
14975     // private
14976     onEmptyResults : function(){
14977         
14978         if(this.tickable && this.editable){
14979             this.hasFocus = false;
14980             this.restrictHeight();
14981             return;
14982         }
14983         
14984         this.collapse();
14985     },
14986
14987     /**
14988      * Returns true if the dropdown list is expanded, else false.
14989      */
14990     isExpanded : function(){
14991         return this.list.isVisible();
14992     },
14993
14994     /**
14995      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14996      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14997      * @param {String} value The data value of the item to select
14998      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14999      * selected item if it is not currently in view (defaults to true)
15000      * @return {Boolean} True if the value matched an item in the list, else false
15001      */
15002     selectByValue : function(v, scrollIntoView){
15003         if(v !== undefined && v !== null){
15004             var r = this.findRecord(this.valueField || this.displayField, v);
15005             if(r){
15006                 this.select(this.store.indexOf(r), scrollIntoView);
15007                 return true;
15008             }
15009         }
15010         return false;
15011     },
15012
15013     /**
15014      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15015      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15016      * @param {Number} index The zero-based index of the list item to select
15017      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15018      * selected item if it is not currently in view (defaults to true)
15019      */
15020     select : function(index, scrollIntoView){
15021         this.selectedIndex = index;
15022         this.view.select(index);
15023         if(scrollIntoView !== false){
15024             var el = this.view.getNode(index);
15025             /*
15026              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15027              */
15028             if(el){
15029                 this.list.scrollChildIntoView(el, false);
15030             }
15031         }
15032     },
15033
15034     // private
15035     selectNext : function(){
15036         var ct = this.store.getCount();
15037         if(ct > 0){
15038             if(this.selectedIndex == -1){
15039                 this.select(0);
15040             }else if(this.selectedIndex < ct-1){
15041                 this.select(this.selectedIndex+1);
15042             }
15043         }
15044     },
15045
15046     // private
15047     selectPrev : function(){
15048         var ct = this.store.getCount();
15049         if(ct > 0){
15050             if(this.selectedIndex == -1){
15051                 this.select(0);
15052             }else if(this.selectedIndex != 0){
15053                 this.select(this.selectedIndex-1);
15054             }
15055         }
15056     },
15057
15058     // private
15059     onKeyUp : function(e){
15060         if(this.editable !== false && !e.isSpecialKey()){
15061             this.lastKey = e.getKey();
15062             this.dqTask.delay(this.queryDelay);
15063         }
15064     },
15065
15066     // private
15067     validateBlur : function(){
15068         return !this.list || !this.list.isVisible();   
15069     },
15070
15071     // private
15072     initQuery : function(){
15073         
15074         var v = this.getRawValue();
15075         
15076         if(this.tickable && this.editable){
15077             v = this.tickableInputEl().getValue();
15078         }
15079         
15080         this.doQuery(v);
15081     },
15082
15083     // private
15084     doForce : function(){
15085         if(this.inputEl().dom.value.length > 0){
15086             this.inputEl().dom.value =
15087                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15088              
15089         }
15090     },
15091
15092     /**
15093      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15094      * query allowing the query action to be canceled if needed.
15095      * @param {String} query The SQL query to execute
15096      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15097      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15098      * saved in the current store (defaults to false)
15099      */
15100     doQuery : function(q, forceAll){
15101         
15102         if(q === undefined || q === null){
15103             q = '';
15104         }
15105         var qe = {
15106             query: q,
15107             forceAll: forceAll,
15108             combo: this,
15109             cancel:false
15110         };
15111         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15112             return false;
15113         }
15114         q = qe.query;
15115         
15116         forceAll = qe.forceAll;
15117         if(forceAll === true || (q.length >= this.minChars)){
15118             
15119             this.hasQuery = true;
15120             
15121             if(this.lastQuery != q || this.alwaysQuery){
15122                 this.lastQuery = q;
15123                 if(this.mode == 'local'){
15124                     this.selectedIndex = -1;
15125                     if(forceAll){
15126                         this.store.clearFilter();
15127                     }else{
15128                         
15129                         if(this.specialFilter){
15130                             this.fireEvent('specialfilter', this);
15131                             this.onLoad();
15132                             return;
15133                         }
15134                         
15135                         this.store.filter(this.displayField, q);
15136                     }
15137                     
15138                     this.store.fireEvent("datachanged", this.store);
15139                     
15140                     this.onLoad();
15141                     
15142                     
15143                 }else{
15144                     
15145                     this.store.baseParams[this.queryParam] = q;
15146                     
15147                     var options = {params : this.getParams(q)};
15148                     
15149                     if(this.loadNext){
15150                         options.add = true;
15151                         options.params.start = this.page * this.pageSize;
15152                     }
15153                     
15154                     this.store.load(options);
15155                     
15156                     /*
15157                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15158                      *  we should expand the list on onLoad
15159                      *  so command out it
15160                      */
15161 //                    this.expand();
15162                 }
15163             }else{
15164                 this.selectedIndex = -1;
15165                 this.onLoad();   
15166             }
15167         }
15168         
15169         this.loadNext = false;
15170     },
15171     
15172     // private
15173     getParams : function(q){
15174         var p = {};
15175         //p[this.queryParam] = q;
15176         
15177         if(this.pageSize){
15178             p.start = 0;
15179             p.limit = this.pageSize;
15180         }
15181         return p;
15182     },
15183
15184     /**
15185      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15186      */
15187     collapse : function(){
15188         if(!this.isExpanded()){
15189             return;
15190         }
15191         
15192         this.list.hide();
15193         
15194         this.hasFocus = false;
15195         
15196         if(this.tickable){
15197             this.okBtn.hide();
15198             this.cancelBtn.hide();
15199             this.trigger.show();
15200             
15201             if(this.editable){
15202                 this.tickableInputEl().dom.value = '';
15203                 this.tickableInputEl().blur();
15204             }
15205             
15206         }
15207         
15208         Roo.get(document).un('mousedown', this.collapseIf, this);
15209         Roo.get(document).un('mousewheel', this.collapseIf, this);
15210         if (!this.editable) {
15211             Roo.get(document).un('keydown', this.listKeyPress, this);
15212         }
15213         this.fireEvent('collapse', this);
15214         
15215         this.validate();
15216     },
15217
15218     // private
15219     collapseIf : function(e){
15220         var in_combo  = e.within(this.el);
15221         var in_list =  e.within(this.list);
15222         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15223         
15224         if (in_combo || in_list || is_list) {
15225             //e.stopPropagation();
15226             return;
15227         }
15228         
15229         if(this.tickable){
15230             this.onTickableFooterButtonClick(e, false, false);
15231         }
15232
15233         this.collapse();
15234         
15235     },
15236
15237     /**
15238      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15239      */
15240     expand : function(){
15241        
15242         if(this.isExpanded() || !this.hasFocus){
15243             return;
15244         }
15245         
15246         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15247         this.list.setWidth(lw);
15248         
15249         Roo.log('expand');
15250         
15251         this.list.show();
15252         
15253         this.restrictHeight();
15254         
15255         if(this.tickable){
15256             
15257             this.tickItems = Roo.apply([], this.item);
15258             
15259             this.okBtn.show();
15260             this.cancelBtn.show();
15261             this.trigger.hide();
15262             
15263             if(this.editable){
15264                 this.tickableInputEl().focus();
15265             }
15266             
15267         }
15268         
15269         Roo.get(document).on('mousedown', this.collapseIf, this);
15270         Roo.get(document).on('mousewheel', this.collapseIf, this);
15271         if (!this.editable) {
15272             Roo.get(document).on('keydown', this.listKeyPress, this);
15273         }
15274         
15275         this.fireEvent('expand', this);
15276     },
15277
15278     // private
15279     // Implements the default empty TriggerField.onTriggerClick function
15280     onTriggerClick : function(e)
15281     {
15282         Roo.log('trigger click');
15283         
15284         if(this.disabled || !this.triggerList){
15285             return;
15286         }
15287         
15288         this.page = 0;
15289         this.loadNext = false;
15290         
15291         if(this.isExpanded()){
15292             this.collapse();
15293             if (!this.blockFocus) {
15294                 this.inputEl().focus();
15295             }
15296             
15297         }else {
15298             this.hasFocus = true;
15299             if(this.triggerAction == 'all') {
15300                 this.doQuery(this.allQuery, true);
15301             } else {
15302                 this.doQuery(this.getRawValue());
15303             }
15304             if (!this.blockFocus) {
15305                 this.inputEl().focus();
15306             }
15307         }
15308     },
15309     
15310     onTickableTriggerClick : function(e)
15311     {
15312         if(this.disabled){
15313             return;
15314         }
15315         
15316         this.page = 0;
15317         this.loadNext = false;
15318         this.hasFocus = true;
15319         
15320         if(this.triggerAction == 'all') {
15321             this.doQuery(this.allQuery, true);
15322         } else {
15323             this.doQuery(this.getRawValue());
15324         }
15325     },
15326     
15327     onSearchFieldClick : function(e)
15328     {
15329         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15330             this.onTickableFooterButtonClick(e, false, false);
15331             return;
15332         }
15333         
15334         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15335             return;
15336         }
15337         
15338         this.page = 0;
15339         this.loadNext = false;
15340         this.hasFocus = true;
15341         
15342         if(this.triggerAction == 'all') {
15343             this.doQuery(this.allQuery, true);
15344         } else {
15345             this.doQuery(this.getRawValue());
15346         }
15347     },
15348     
15349     listKeyPress : function(e)
15350     {
15351         //Roo.log('listkeypress');
15352         // scroll to first matching element based on key pres..
15353         if (e.isSpecialKey()) {
15354             return false;
15355         }
15356         var k = String.fromCharCode(e.getKey()).toUpperCase();
15357         //Roo.log(k);
15358         var match  = false;
15359         var csel = this.view.getSelectedNodes();
15360         var cselitem = false;
15361         if (csel.length) {
15362             var ix = this.view.indexOf(csel[0]);
15363             cselitem  = this.store.getAt(ix);
15364             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15365                 cselitem = false;
15366             }
15367             
15368         }
15369         
15370         this.store.each(function(v) { 
15371             if (cselitem) {
15372                 // start at existing selection.
15373                 if (cselitem.id == v.id) {
15374                     cselitem = false;
15375                 }
15376                 return true;
15377             }
15378                 
15379             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15380                 match = this.store.indexOf(v);
15381                 return false;
15382             }
15383             return true;
15384         }, this);
15385         
15386         if (match === false) {
15387             return true; // no more action?
15388         }
15389         // scroll to?
15390         this.view.select(match);
15391         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15392         sn.scrollIntoView(sn.dom.parentNode, false);
15393     },
15394     
15395     onViewScroll : function(e, t){
15396         
15397         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){
15398             return;
15399         }
15400         
15401         this.hasQuery = true;
15402         
15403         this.loading = this.list.select('.loading', true).first();
15404         
15405         if(this.loading === null){
15406             this.list.createChild({
15407                 tag: 'div',
15408                 cls: 'loading roo-select2-more-results roo-select2-active',
15409                 html: 'Loading more results...'
15410             });
15411             
15412             this.loading = this.list.select('.loading', true).first();
15413             
15414             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15415             
15416             this.loading.hide();
15417         }
15418         
15419         this.loading.show();
15420         
15421         var _combo = this;
15422         
15423         this.page++;
15424         this.loadNext = true;
15425         
15426         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15427         
15428         return;
15429     },
15430     
15431     addItem : function(o)
15432     {   
15433         var dv = ''; // display value
15434         
15435         if (this.displayField) {
15436             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15437         } else {
15438             // this is an error condition!!!
15439             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15440         }
15441         
15442         if(!dv.length){
15443             return;
15444         }
15445         
15446         var choice = this.choices.createChild({
15447             tag: 'li',
15448             cls: 'roo-select2-search-choice',
15449             cn: [
15450                 {
15451                     tag: 'div',
15452                     html: dv
15453                 },
15454                 {
15455                     tag: 'a',
15456                     href: '#',
15457                     cls: 'roo-select2-search-choice-close fa fa-times',
15458                     tabindex: '-1'
15459                 }
15460             ]
15461             
15462         }, this.searchField);
15463         
15464         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15465         
15466         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15467         
15468         this.item.push(o);
15469         
15470         this.lastData = o;
15471         
15472         this.syncValue();
15473         
15474         this.inputEl().dom.value = '';
15475         
15476         this.validate();
15477     },
15478     
15479     onRemoveItem : function(e, _self, o)
15480     {
15481         e.preventDefault();
15482         
15483         this.lastItem = Roo.apply([], this.item);
15484         
15485         var index = this.item.indexOf(o.data) * 1;
15486         
15487         if( index < 0){
15488             Roo.log('not this item?!');
15489             return;
15490         }
15491         
15492         this.item.splice(index, 1);
15493         o.item.remove();
15494         
15495         this.syncValue();
15496         
15497         this.fireEvent('remove', this, e);
15498         
15499         this.validate();
15500         
15501     },
15502     
15503     syncValue : function()
15504     {
15505         if(!this.item.length){
15506             this.clearValue();
15507             return;
15508         }
15509             
15510         var value = [];
15511         var _this = this;
15512         Roo.each(this.item, function(i){
15513             if(_this.valueField){
15514                 value.push(i[_this.valueField]);
15515                 return;
15516             }
15517
15518             value.push(i);
15519         });
15520
15521         this.value = value.join(',');
15522
15523         if(this.hiddenField){
15524             this.hiddenField.dom.value = this.value;
15525         }
15526         
15527         this.store.fireEvent("datachanged", this.store);
15528         
15529         this.validate();
15530     },
15531     
15532     clearItem : function()
15533     {
15534         if(!this.multiple){
15535             return;
15536         }
15537         
15538         this.item = [];
15539         
15540         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15541            c.remove();
15542         });
15543         
15544         this.syncValue();
15545         
15546         this.validate();
15547         
15548         if(this.tickable && !Roo.isTouch){
15549             this.view.refresh();
15550         }
15551     },
15552     
15553     inputEl: function ()
15554     {
15555         if(Roo.isIOS && this.useNativeIOS){
15556             return this.el.select('select.roo-ios-select', true).first();
15557         }
15558         
15559         if(Roo.isTouch && this.mobileTouchView){
15560             return this.el.select('input.form-control',true).first();
15561         }
15562         
15563         if(this.tickable){
15564             return this.searchField;
15565         }
15566         
15567         return this.el.select('input.form-control',true).first();
15568     },
15569     
15570     onTickableFooterButtonClick : function(e, btn, el)
15571     {
15572         e.preventDefault();
15573         
15574         this.lastItem = Roo.apply([], this.item);
15575         
15576         if(btn && btn.name == 'cancel'){
15577             this.tickItems = Roo.apply([], this.item);
15578             this.collapse();
15579             return;
15580         }
15581         
15582         this.clearItem();
15583         
15584         var _this = this;
15585         
15586         Roo.each(this.tickItems, function(o){
15587             _this.addItem(o);
15588         });
15589         
15590         this.collapse();
15591         
15592     },
15593     
15594     validate : function()
15595     {
15596         if(this.getVisibilityEl().hasClass('hidden')){
15597             return true;
15598         }
15599         
15600         var v = this.getRawValue();
15601         
15602         if(this.multiple){
15603             v = this.getValue();
15604         }
15605         
15606         if(this.disabled || this.allowBlank || v.length){
15607             this.markValid();
15608             return true;
15609         }
15610         
15611         this.markInvalid();
15612         return false;
15613     },
15614     
15615     tickableInputEl : function()
15616     {
15617         if(!this.tickable || !this.editable){
15618             return this.inputEl();
15619         }
15620         
15621         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15622     },
15623     
15624     
15625     getAutoCreateTouchView : function()
15626     {
15627         var id = Roo.id();
15628         
15629         var cfg = {
15630             cls: 'form-group' //input-group
15631         };
15632         
15633         var input =  {
15634             tag: 'input',
15635             id : id,
15636             type : this.inputType,
15637             cls : 'form-control x-combo-noedit',
15638             autocomplete: 'new-password',
15639             placeholder : this.placeholder || '',
15640             readonly : true
15641         };
15642         
15643         if (this.name) {
15644             input.name = this.name;
15645         }
15646         
15647         if (this.size) {
15648             input.cls += ' input-' + this.size;
15649         }
15650         
15651         if (this.disabled) {
15652             input.disabled = true;
15653         }
15654         
15655         var inputblock = {
15656             cls : '',
15657             cn : [
15658                 input
15659             ]
15660         };
15661         
15662         if(this.before){
15663             inputblock.cls += ' input-group';
15664             
15665             inputblock.cn.unshift({
15666                 tag :'span',
15667                 cls : 'input-group-addon input-group-prepend input-group-text',
15668                 html : this.before
15669             });
15670         }
15671         
15672         if(this.removable && !this.multiple){
15673             inputblock.cls += ' roo-removable';
15674             
15675             inputblock.cn.push({
15676                 tag: 'button',
15677                 html : 'x',
15678                 cls : 'roo-combo-removable-btn close'
15679             });
15680         }
15681
15682         if(this.hasFeedback && !this.allowBlank){
15683             
15684             inputblock.cls += ' has-feedback';
15685             
15686             inputblock.cn.push({
15687                 tag: 'span',
15688                 cls: 'glyphicon form-control-feedback'
15689             });
15690             
15691         }
15692         
15693         if (this.after) {
15694             
15695             inputblock.cls += (this.before) ? '' : ' input-group';
15696             
15697             inputblock.cn.push({
15698                 tag :'span',
15699                 cls : 'input-group-addon input-group-append input-group-text',
15700                 html : this.after
15701             });
15702         }
15703
15704         
15705         var ibwrap = inputblock;
15706         
15707         if(this.multiple){
15708             ibwrap = {
15709                 tag: 'ul',
15710                 cls: 'roo-select2-choices',
15711                 cn:[
15712                     {
15713                         tag: 'li',
15714                         cls: 'roo-select2-search-field',
15715                         cn: [
15716
15717                             inputblock
15718                         ]
15719                     }
15720                 ]
15721             };
15722         
15723             
15724         }
15725         
15726         var combobox = {
15727             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15728             cn: [
15729                 {
15730                     tag: 'input',
15731                     type : 'hidden',
15732                     cls: 'form-hidden-field'
15733                 },
15734                 ibwrap
15735             ]
15736         };
15737         
15738         if(!this.multiple && this.showToggleBtn){
15739             
15740             var caret = {
15741                 cls: 'caret'
15742             };
15743             
15744             if (this.caret != false) {
15745                 caret = {
15746                      tag: 'i',
15747                      cls: 'fa fa-' + this.caret
15748                 };
15749                 
15750             }
15751             
15752             combobox.cn.push({
15753                 tag :'span',
15754                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15755                 cn : [
15756                     Roo.bootstrap.version == 3 ? caret : '',
15757                     {
15758                         tag: 'span',
15759                         cls: 'combobox-clear',
15760                         cn  : [
15761                             {
15762                                 tag : 'i',
15763                                 cls: 'icon-remove'
15764                             }
15765                         ]
15766                     }
15767                 ]
15768
15769             })
15770         }
15771         
15772         if(this.multiple){
15773             combobox.cls += ' roo-select2-container-multi';
15774         }
15775         
15776         var align = this.labelAlign || this.parentLabelAlign();
15777         
15778         if (align ==='left' && this.fieldLabel.length) {
15779
15780             cfg.cn = [
15781                 {
15782                    tag : 'i',
15783                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15784                    tooltip : 'This field is required'
15785                 },
15786                 {
15787                     tag: 'label',
15788                     cls : 'control-label col-form-label',
15789                     html : this.fieldLabel
15790
15791                 },
15792                 {
15793                     cls : '', 
15794                     cn: [
15795                         combobox
15796                     ]
15797                 }
15798             ];
15799             
15800             var labelCfg = cfg.cn[1];
15801             var contentCfg = cfg.cn[2];
15802             
15803
15804             if(this.indicatorpos == 'right'){
15805                 cfg.cn = [
15806                     {
15807                         tag: 'label',
15808                         'for' :  id,
15809                         cls : 'control-label col-form-label',
15810                         cn : [
15811                             {
15812                                 tag : 'span',
15813                                 html : this.fieldLabel
15814                             },
15815                             {
15816                                 tag : 'i',
15817                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15818                                 tooltip : 'This field is required'
15819                             }
15820                         ]
15821                     },
15822                     {
15823                         cls : "",
15824                         cn: [
15825                             combobox
15826                         ]
15827                     }
15828
15829                 ];
15830                 
15831                 labelCfg = cfg.cn[0];
15832                 contentCfg = cfg.cn[1];
15833             }
15834             
15835            
15836             
15837             if(this.labelWidth > 12){
15838                 labelCfg.style = "width: " + this.labelWidth + 'px';
15839             }
15840             
15841             if(this.labelWidth < 13 && this.labelmd == 0){
15842                 this.labelmd = this.labelWidth;
15843             }
15844             
15845             if(this.labellg > 0){
15846                 labelCfg.cls += ' col-lg-' + this.labellg;
15847                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15848             }
15849             
15850             if(this.labelmd > 0){
15851                 labelCfg.cls += ' col-md-' + this.labelmd;
15852                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15853             }
15854             
15855             if(this.labelsm > 0){
15856                 labelCfg.cls += ' col-sm-' + this.labelsm;
15857                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15858             }
15859             
15860             if(this.labelxs > 0){
15861                 labelCfg.cls += ' col-xs-' + this.labelxs;
15862                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15863             }
15864                 
15865                 
15866         } else if ( this.fieldLabel.length) {
15867             cfg.cn = [
15868                 {
15869                    tag : 'i',
15870                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15871                    tooltip : 'This field is required'
15872                 },
15873                 {
15874                     tag: 'label',
15875                     cls : 'control-label',
15876                     html : this.fieldLabel
15877
15878                 },
15879                 {
15880                     cls : '', 
15881                     cn: [
15882                         combobox
15883                     ]
15884                 }
15885             ];
15886             
15887             if(this.indicatorpos == 'right'){
15888                 cfg.cn = [
15889                     {
15890                         tag: 'label',
15891                         cls : 'control-label',
15892                         html : this.fieldLabel,
15893                         cn : [
15894                             {
15895                                tag : 'i',
15896                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15897                                tooltip : 'This field is required'
15898                             }
15899                         ]
15900                     },
15901                     {
15902                         cls : '', 
15903                         cn: [
15904                             combobox
15905                         ]
15906                     }
15907                 ];
15908             }
15909         } else {
15910             cfg.cn = combobox;    
15911         }
15912         
15913         
15914         var settings = this;
15915         
15916         ['xs','sm','md','lg'].map(function(size){
15917             if (settings[size]) {
15918                 cfg.cls += ' col-' + size + '-' + settings[size];
15919             }
15920         });
15921         
15922         return cfg;
15923     },
15924     
15925     initTouchView : function()
15926     {
15927         this.renderTouchView();
15928         
15929         this.touchViewEl.on('scroll', function(){
15930             this.el.dom.scrollTop = 0;
15931         }, this);
15932         
15933         this.originalValue = this.getValue();
15934         
15935         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15936         
15937         this.inputEl().on("click", this.showTouchView, this);
15938         if (this.triggerEl) {
15939             this.triggerEl.on("click", this.showTouchView, this);
15940         }
15941         
15942         
15943         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15944         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15945         
15946         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15947         
15948         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15949         this.store.on('load', this.onTouchViewLoad, this);
15950         this.store.on('loadexception', this.onTouchViewLoadException, this);
15951         
15952         if(this.hiddenName){
15953             
15954             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15955             
15956             this.hiddenField.dom.value =
15957                 this.hiddenValue !== undefined ? this.hiddenValue :
15958                 this.value !== undefined ? this.value : '';
15959         
15960             this.el.dom.removeAttribute('name');
15961             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15962         }
15963         
15964         if(this.multiple){
15965             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15966             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15967         }
15968         
15969         if(this.removable && !this.multiple){
15970             var close = this.closeTriggerEl();
15971             if(close){
15972                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15973                 close.on('click', this.removeBtnClick, this, close);
15974             }
15975         }
15976         /*
15977          * fix the bug in Safari iOS8
15978          */
15979         this.inputEl().on("focus", function(e){
15980             document.activeElement.blur();
15981         }, this);
15982         
15983         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15984         
15985         return;
15986         
15987         
15988     },
15989     
15990     renderTouchView : function()
15991     {
15992         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15993         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15994         
15995         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15996         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15997         
15998         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15999         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16000         this.touchViewBodyEl.setStyle('overflow', 'auto');
16001         
16002         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16003         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16004         
16005         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16006         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16007         
16008     },
16009     
16010     showTouchView : function()
16011     {
16012         if(this.disabled){
16013             return;
16014         }
16015         
16016         this.touchViewHeaderEl.hide();
16017
16018         if(this.modalTitle.length){
16019             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16020             this.touchViewHeaderEl.show();
16021         }
16022
16023         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16024         this.touchViewEl.show();
16025
16026         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16027         
16028         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16029         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16030
16031         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16032
16033         if(this.modalTitle.length){
16034             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16035         }
16036         
16037         this.touchViewBodyEl.setHeight(bodyHeight);
16038
16039         if(this.animate){
16040             var _this = this;
16041             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16042         }else{
16043             this.touchViewEl.addClass('in');
16044         }
16045         
16046         if(this._touchViewMask){
16047             Roo.get(document.body).addClass("x-body-masked");
16048             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16049             this._touchViewMask.setStyle('z-index', 10000);
16050             this._touchViewMask.addClass('show');
16051         }
16052         
16053         this.doTouchViewQuery();
16054         
16055     },
16056     
16057     hideTouchView : function()
16058     {
16059         this.touchViewEl.removeClass('in');
16060
16061         if(this.animate){
16062             var _this = this;
16063             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16064         }else{
16065             this.touchViewEl.setStyle('display', 'none');
16066         }
16067         
16068         if(this._touchViewMask){
16069             this._touchViewMask.removeClass('show');
16070             Roo.get(document.body).removeClass("x-body-masked");
16071         }
16072     },
16073     
16074     setTouchViewValue : function()
16075     {
16076         if(this.multiple){
16077             this.clearItem();
16078         
16079             var _this = this;
16080
16081             Roo.each(this.tickItems, function(o){
16082                 this.addItem(o);
16083             }, this);
16084         }
16085         
16086         this.hideTouchView();
16087     },
16088     
16089     doTouchViewQuery : function()
16090     {
16091         var qe = {
16092             query: '',
16093             forceAll: true,
16094             combo: this,
16095             cancel:false
16096         };
16097         
16098         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16099             return false;
16100         }
16101         
16102         if(!this.alwaysQuery || this.mode == 'local'){
16103             this.onTouchViewLoad();
16104             return;
16105         }
16106         
16107         this.store.load();
16108     },
16109     
16110     onTouchViewBeforeLoad : function(combo,opts)
16111     {
16112         return;
16113     },
16114
16115     // private
16116     onTouchViewLoad : function()
16117     {
16118         if(this.store.getCount() < 1){
16119             this.onTouchViewEmptyResults();
16120             return;
16121         }
16122         
16123         this.clearTouchView();
16124         
16125         var rawValue = this.getRawValue();
16126         
16127         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16128         
16129         this.tickItems = [];
16130         
16131         this.store.data.each(function(d, rowIndex){
16132             var row = this.touchViewListGroup.createChild(template);
16133             
16134             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16135                 row.addClass(d.data.cls);
16136             }
16137             
16138             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16139                 var cfg = {
16140                     data : d.data,
16141                     html : d.data[this.displayField]
16142                 };
16143                 
16144                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16145                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16146                 }
16147             }
16148             row.removeClass('selected');
16149             if(!this.multiple && this.valueField &&
16150                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16151             {
16152                 // radio buttons..
16153                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16154                 row.addClass('selected');
16155             }
16156             
16157             if(this.multiple && this.valueField &&
16158                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16159             {
16160                 
16161                 // checkboxes...
16162                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16163                 this.tickItems.push(d.data);
16164             }
16165             
16166             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16167             
16168         }, this);
16169         
16170         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16171         
16172         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16173
16174         if(this.modalTitle.length){
16175             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16176         }
16177
16178         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16179         
16180         if(this.mobile_restrict_height && listHeight < bodyHeight){
16181             this.touchViewBodyEl.setHeight(listHeight);
16182         }
16183         
16184         var _this = this;
16185         
16186         if(firstChecked && listHeight > bodyHeight){
16187             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16188         }
16189         
16190     },
16191     
16192     onTouchViewLoadException : function()
16193     {
16194         this.hideTouchView();
16195     },
16196     
16197     onTouchViewEmptyResults : function()
16198     {
16199         this.clearTouchView();
16200         
16201         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16202         
16203         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16204         
16205     },
16206     
16207     clearTouchView : function()
16208     {
16209         this.touchViewListGroup.dom.innerHTML = '';
16210     },
16211     
16212     onTouchViewClick : function(e, el, o)
16213     {
16214         e.preventDefault();
16215         
16216         var row = o.row;
16217         var rowIndex = o.rowIndex;
16218         
16219         var r = this.store.getAt(rowIndex);
16220         
16221         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16222             
16223             if(!this.multiple){
16224                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16225                     c.dom.removeAttribute('checked');
16226                 }, this);
16227
16228                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16229
16230                 this.setFromData(r.data);
16231
16232                 var close = this.closeTriggerEl();
16233
16234                 if(close){
16235                     close.show();
16236                 }
16237
16238                 this.hideTouchView();
16239
16240                 this.fireEvent('select', this, r, rowIndex);
16241
16242                 return;
16243             }
16244
16245             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16246                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16247                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16248                 return;
16249             }
16250
16251             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16252             this.addItem(r.data);
16253             this.tickItems.push(r.data);
16254         }
16255     },
16256     
16257     getAutoCreateNativeIOS : function()
16258     {
16259         var cfg = {
16260             cls: 'form-group' //input-group,
16261         };
16262         
16263         var combobox =  {
16264             tag: 'select',
16265             cls : 'roo-ios-select'
16266         };
16267         
16268         if (this.name) {
16269             combobox.name = this.name;
16270         }
16271         
16272         if (this.disabled) {
16273             combobox.disabled = true;
16274         }
16275         
16276         var settings = this;
16277         
16278         ['xs','sm','md','lg'].map(function(size){
16279             if (settings[size]) {
16280                 cfg.cls += ' col-' + size + '-' + settings[size];
16281             }
16282         });
16283         
16284         cfg.cn = combobox;
16285         
16286         return cfg;
16287         
16288     },
16289     
16290     initIOSView : function()
16291     {
16292         this.store.on('load', this.onIOSViewLoad, this);
16293         
16294         return;
16295     },
16296     
16297     onIOSViewLoad : function()
16298     {
16299         if(this.store.getCount() < 1){
16300             return;
16301         }
16302         
16303         this.clearIOSView();
16304         
16305         if(this.allowBlank) {
16306             
16307             var default_text = '-- SELECT --';
16308             
16309             if(this.placeholder.length){
16310                 default_text = this.placeholder;
16311             }
16312             
16313             if(this.emptyTitle.length){
16314                 default_text += ' - ' + this.emptyTitle + ' -';
16315             }
16316             
16317             var opt = this.inputEl().createChild({
16318                 tag: 'option',
16319                 value : 0,
16320                 html : default_text
16321             });
16322             
16323             var o = {};
16324             o[this.valueField] = 0;
16325             o[this.displayField] = default_text;
16326             
16327             this.ios_options.push({
16328                 data : o,
16329                 el : opt
16330             });
16331             
16332         }
16333         
16334         this.store.data.each(function(d, rowIndex){
16335             
16336             var html = '';
16337             
16338             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16339                 html = d.data[this.displayField];
16340             }
16341             
16342             var value = '';
16343             
16344             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16345                 value = d.data[this.valueField];
16346             }
16347             
16348             var option = {
16349                 tag: 'option',
16350                 value : value,
16351                 html : html
16352             };
16353             
16354             if(this.value == d.data[this.valueField]){
16355                 option['selected'] = true;
16356             }
16357             
16358             var opt = this.inputEl().createChild(option);
16359             
16360             this.ios_options.push({
16361                 data : d.data,
16362                 el : opt
16363             });
16364             
16365         }, this);
16366         
16367         this.inputEl().on('change', function(){
16368            this.fireEvent('select', this);
16369         }, this);
16370         
16371     },
16372     
16373     clearIOSView: function()
16374     {
16375         this.inputEl().dom.innerHTML = '';
16376         
16377         this.ios_options = [];
16378     },
16379     
16380     setIOSValue: function(v)
16381     {
16382         this.value = v;
16383         
16384         if(!this.ios_options){
16385             return;
16386         }
16387         
16388         Roo.each(this.ios_options, function(opts){
16389            
16390            opts.el.dom.removeAttribute('selected');
16391            
16392            if(opts.data[this.valueField] != v){
16393                return;
16394            }
16395            
16396            opts.el.dom.setAttribute('selected', true);
16397            
16398         }, this);
16399     }
16400
16401     /** 
16402     * @cfg {Boolean} grow 
16403     * @hide 
16404     */
16405     /** 
16406     * @cfg {Number} growMin 
16407     * @hide 
16408     */
16409     /** 
16410     * @cfg {Number} growMax 
16411     * @hide 
16412     */
16413     /**
16414      * @hide
16415      * @method autoSize
16416      */
16417 });
16418
16419 Roo.apply(Roo.bootstrap.ComboBox,  {
16420     
16421     header : {
16422         tag: 'div',
16423         cls: 'modal-header',
16424         cn: [
16425             {
16426                 tag: 'h4',
16427                 cls: 'modal-title'
16428             }
16429         ]
16430     },
16431     
16432     body : {
16433         tag: 'div',
16434         cls: 'modal-body',
16435         cn: [
16436             {
16437                 tag: 'ul',
16438                 cls: 'list-group'
16439             }
16440         ]
16441     },
16442     
16443     listItemRadio : {
16444         tag: 'li',
16445         cls: 'list-group-item',
16446         cn: [
16447             {
16448                 tag: 'span',
16449                 cls: 'roo-combobox-list-group-item-value'
16450             },
16451             {
16452                 tag: 'div',
16453                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16454                 cn: [
16455                     {
16456                         tag: 'input',
16457                         type: 'radio'
16458                     },
16459                     {
16460                         tag: 'label'
16461                     }
16462                 ]
16463             }
16464         ]
16465     },
16466     
16467     listItemCheckbox : {
16468         tag: 'li',
16469         cls: 'list-group-item',
16470         cn: [
16471             {
16472                 tag: 'span',
16473                 cls: 'roo-combobox-list-group-item-value'
16474             },
16475             {
16476                 tag: 'div',
16477                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16478                 cn: [
16479                     {
16480                         tag: 'input',
16481                         type: 'checkbox'
16482                     },
16483                     {
16484                         tag: 'label'
16485                     }
16486                 ]
16487             }
16488         ]
16489     },
16490     
16491     emptyResult : {
16492         tag: 'div',
16493         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16494     },
16495     
16496     footer : {
16497         tag: 'div',
16498         cls: 'modal-footer',
16499         cn: [
16500             {
16501                 tag: 'div',
16502                 cls: 'row',
16503                 cn: [
16504                     {
16505                         tag: 'div',
16506                         cls: 'col-xs-6 text-left',
16507                         cn: {
16508                             tag: 'button',
16509                             cls: 'btn btn-danger roo-touch-view-cancel',
16510                             html: 'Cancel'
16511                         }
16512                     },
16513                     {
16514                         tag: 'div',
16515                         cls: 'col-xs-6 text-right',
16516                         cn: {
16517                             tag: 'button',
16518                             cls: 'btn btn-success roo-touch-view-ok',
16519                             html: 'OK'
16520                         }
16521                     }
16522                 ]
16523             }
16524         ]
16525         
16526     }
16527 });
16528
16529 Roo.apply(Roo.bootstrap.ComboBox,  {
16530     
16531     touchViewTemplate : {
16532         tag: 'div',
16533         cls: 'modal fade roo-combobox-touch-view',
16534         cn: [
16535             {
16536                 tag: 'div',
16537                 cls: 'modal-dialog',
16538                 style : 'position:fixed', // we have to fix position....
16539                 cn: [
16540                     {
16541                         tag: 'div',
16542                         cls: 'modal-content',
16543                         cn: [
16544                             Roo.bootstrap.ComboBox.header,
16545                             Roo.bootstrap.ComboBox.body,
16546                             Roo.bootstrap.ComboBox.footer
16547                         ]
16548                     }
16549                 ]
16550             }
16551         ]
16552     }
16553 });/*
16554  * Based on:
16555  * Ext JS Library 1.1.1
16556  * Copyright(c) 2006-2007, Ext JS, LLC.
16557  *
16558  * Originally Released Under LGPL - original licence link has changed is not relivant.
16559  *
16560  * Fork - LGPL
16561  * <script type="text/javascript">
16562  */
16563
16564 /**
16565  * @class Roo.View
16566  * @extends Roo.util.Observable
16567  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16568  * This class also supports single and multi selection modes. <br>
16569  * Create a data model bound view:
16570  <pre><code>
16571  var store = new Roo.data.Store(...);
16572
16573  var view = new Roo.View({
16574     el : "my-element",
16575     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16576  
16577     singleSelect: true,
16578     selectedClass: "ydataview-selected",
16579     store: store
16580  });
16581
16582  // listen for node click?
16583  view.on("click", function(vw, index, node, e){
16584  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16585  });
16586
16587  // load XML data
16588  dataModel.load("foobar.xml");
16589  </code></pre>
16590  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16591  * <br><br>
16592  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16593  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16594  * 
16595  * Note: old style constructor is still suported (container, template, config)
16596  * 
16597  * @constructor
16598  * Create a new View
16599  * @param {Object} config The config object
16600  * 
16601  */
16602 Roo.View = function(config, depreciated_tpl, depreciated_config){
16603     
16604     this.parent = false;
16605     
16606     if (typeof(depreciated_tpl) == 'undefined') {
16607         // new way.. - universal constructor.
16608         Roo.apply(this, config);
16609         this.el  = Roo.get(this.el);
16610     } else {
16611         // old format..
16612         this.el  = Roo.get(config);
16613         this.tpl = depreciated_tpl;
16614         Roo.apply(this, depreciated_config);
16615     }
16616     this.wrapEl  = this.el.wrap().wrap();
16617     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16618     
16619     
16620     if(typeof(this.tpl) == "string"){
16621         this.tpl = new Roo.Template(this.tpl);
16622     } else {
16623         // support xtype ctors..
16624         this.tpl = new Roo.factory(this.tpl, Roo);
16625     }
16626     
16627     
16628     this.tpl.compile();
16629     
16630     /** @private */
16631     this.addEvents({
16632         /**
16633          * @event beforeclick
16634          * Fires before a click is processed. Returns false to cancel the default action.
16635          * @param {Roo.View} this
16636          * @param {Number} index The index of the target node
16637          * @param {HTMLElement} node The target node
16638          * @param {Roo.EventObject} e The raw event object
16639          */
16640             "beforeclick" : true,
16641         /**
16642          * @event click
16643          * Fires when a template node is clicked.
16644          * @param {Roo.View} this
16645          * @param {Number} index The index of the target node
16646          * @param {HTMLElement} node The target node
16647          * @param {Roo.EventObject} e The raw event object
16648          */
16649             "click" : true,
16650         /**
16651          * @event dblclick
16652          * Fires when a template node is double clicked.
16653          * @param {Roo.View} this
16654          * @param {Number} index The index of the target node
16655          * @param {HTMLElement} node The target node
16656          * @param {Roo.EventObject} e The raw event object
16657          */
16658             "dblclick" : true,
16659         /**
16660          * @event contextmenu
16661          * Fires when a template node is right clicked.
16662          * @param {Roo.View} this
16663          * @param {Number} index The index of the target node
16664          * @param {HTMLElement} node The target node
16665          * @param {Roo.EventObject} e The raw event object
16666          */
16667             "contextmenu" : true,
16668         /**
16669          * @event selectionchange
16670          * Fires when the selected nodes change.
16671          * @param {Roo.View} this
16672          * @param {Array} selections Array of the selected nodes
16673          */
16674             "selectionchange" : true,
16675     
16676         /**
16677          * @event beforeselect
16678          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16679          * @param {Roo.View} this
16680          * @param {HTMLElement} node The node to be selected
16681          * @param {Array} selections Array of currently selected nodes
16682          */
16683             "beforeselect" : true,
16684         /**
16685          * @event preparedata
16686          * Fires on every row to render, to allow you to change the data.
16687          * @param {Roo.View} this
16688          * @param {Object} data to be rendered (change this)
16689          */
16690           "preparedata" : true
16691           
16692           
16693         });
16694
16695
16696
16697     this.el.on({
16698         "click": this.onClick,
16699         "dblclick": this.onDblClick,
16700         "contextmenu": this.onContextMenu,
16701         scope:this
16702     });
16703
16704     this.selections = [];
16705     this.nodes = [];
16706     this.cmp = new Roo.CompositeElementLite([]);
16707     if(this.store){
16708         this.store = Roo.factory(this.store, Roo.data);
16709         this.setStore(this.store, true);
16710     }
16711     
16712     if ( this.footer && this.footer.xtype) {
16713            
16714          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16715         
16716         this.footer.dataSource = this.store;
16717         this.footer.container = fctr;
16718         this.footer = Roo.factory(this.footer, Roo);
16719         fctr.insertFirst(this.el);
16720         
16721         // this is a bit insane - as the paging toolbar seems to detach the el..
16722 //        dom.parentNode.parentNode.parentNode
16723          // they get detached?
16724     }
16725     
16726     
16727     Roo.View.superclass.constructor.call(this);
16728     
16729     
16730 };
16731
16732 Roo.extend(Roo.View, Roo.util.Observable, {
16733     
16734      /**
16735      * @cfg {Roo.data.Store} store Data store to load data from.
16736      */
16737     store : false,
16738     
16739     /**
16740      * @cfg {String|Roo.Element} el The container element.
16741      */
16742     el : '',
16743     
16744     /**
16745      * @cfg {String|Roo.Template} tpl The template used by this View 
16746      */
16747     tpl : false,
16748     /**
16749      * @cfg {String} dataName the named area of the template to use as the data area
16750      *                          Works with domtemplates roo-name="name"
16751      */
16752     dataName: false,
16753     /**
16754      * @cfg {String} selectedClass The css class to add to selected nodes
16755      */
16756     selectedClass : "x-view-selected",
16757      /**
16758      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16759      */
16760     emptyText : "",
16761     
16762     /**
16763      * @cfg {String} text to display on mask (default Loading)
16764      */
16765     mask : false,
16766     /**
16767      * @cfg {Boolean} multiSelect Allow multiple selection
16768      */
16769     multiSelect : false,
16770     /**
16771      * @cfg {Boolean} singleSelect Allow single selection
16772      */
16773     singleSelect:  false,
16774     
16775     /**
16776      * @cfg {Boolean} toggleSelect - selecting 
16777      */
16778     toggleSelect : false,
16779     
16780     /**
16781      * @cfg {Boolean} tickable - selecting 
16782      */
16783     tickable : false,
16784     
16785     /**
16786      * Returns the element this view is bound to.
16787      * @return {Roo.Element}
16788      */
16789     getEl : function(){
16790         return this.wrapEl;
16791     },
16792     
16793     
16794
16795     /**
16796      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16797      */
16798     refresh : function(){
16799         //Roo.log('refresh');
16800         var t = this.tpl;
16801         
16802         // if we are using something like 'domtemplate', then
16803         // the what gets used is:
16804         // t.applySubtemplate(NAME, data, wrapping data..)
16805         // the outer template then get' applied with
16806         //     the store 'extra data'
16807         // and the body get's added to the
16808         //      roo-name="data" node?
16809         //      <span class='roo-tpl-{name}'></span> ?????
16810         
16811         
16812         
16813         this.clearSelections();
16814         this.el.update("");
16815         var html = [];
16816         var records = this.store.getRange();
16817         if(records.length < 1) {
16818             
16819             // is this valid??  = should it render a template??
16820             
16821             this.el.update(this.emptyText);
16822             return;
16823         }
16824         var el = this.el;
16825         if (this.dataName) {
16826             this.el.update(t.apply(this.store.meta)); //????
16827             el = this.el.child('.roo-tpl-' + this.dataName);
16828         }
16829         
16830         for(var i = 0, len = records.length; i < len; i++){
16831             var data = this.prepareData(records[i].data, i, records[i]);
16832             this.fireEvent("preparedata", this, data, i, records[i]);
16833             
16834             var d = Roo.apply({}, data);
16835             
16836             if(this.tickable){
16837                 Roo.apply(d, {'roo-id' : Roo.id()});
16838                 
16839                 var _this = this;
16840             
16841                 Roo.each(this.parent.item, function(item){
16842                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16843                         return;
16844                     }
16845                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16846                 });
16847             }
16848             
16849             html[html.length] = Roo.util.Format.trim(
16850                 this.dataName ?
16851                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16852                     t.apply(d)
16853             );
16854         }
16855         
16856         
16857         
16858         el.update(html.join(""));
16859         this.nodes = el.dom.childNodes;
16860         this.updateIndexes(0);
16861     },
16862     
16863
16864     /**
16865      * Function to override to reformat the data that is sent to
16866      * the template for each node.
16867      * DEPRICATED - use the preparedata event handler.
16868      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16869      * a JSON object for an UpdateManager bound view).
16870      */
16871     prepareData : function(data, index, record)
16872     {
16873         this.fireEvent("preparedata", this, data, index, record);
16874         return data;
16875     },
16876
16877     onUpdate : function(ds, record){
16878         // Roo.log('on update');   
16879         this.clearSelections();
16880         var index = this.store.indexOf(record);
16881         var n = this.nodes[index];
16882         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16883         n.parentNode.removeChild(n);
16884         this.updateIndexes(index, index);
16885     },
16886
16887     
16888     
16889 // --------- FIXME     
16890     onAdd : function(ds, records, index)
16891     {
16892         //Roo.log(['on Add', ds, records, index] );        
16893         this.clearSelections();
16894         if(this.nodes.length == 0){
16895             this.refresh();
16896             return;
16897         }
16898         var n = this.nodes[index];
16899         for(var i = 0, len = records.length; i < len; i++){
16900             var d = this.prepareData(records[i].data, i, records[i]);
16901             if(n){
16902                 this.tpl.insertBefore(n, d);
16903             }else{
16904                 
16905                 this.tpl.append(this.el, d);
16906             }
16907         }
16908         this.updateIndexes(index);
16909     },
16910
16911     onRemove : function(ds, record, index){
16912        // Roo.log('onRemove');
16913         this.clearSelections();
16914         var el = this.dataName  ?
16915             this.el.child('.roo-tpl-' + this.dataName) :
16916             this.el; 
16917         
16918         el.dom.removeChild(this.nodes[index]);
16919         this.updateIndexes(index);
16920     },
16921
16922     /**
16923      * Refresh an individual node.
16924      * @param {Number} index
16925      */
16926     refreshNode : function(index){
16927         this.onUpdate(this.store, this.store.getAt(index));
16928     },
16929
16930     updateIndexes : function(startIndex, endIndex){
16931         var ns = this.nodes;
16932         startIndex = startIndex || 0;
16933         endIndex = endIndex || ns.length - 1;
16934         for(var i = startIndex; i <= endIndex; i++){
16935             ns[i].nodeIndex = i;
16936         }
16937     },
16938
16939     /**
16940      * Changes the data store this view uses and refresh the view.
16941      * @param {Store} store
16942      */
16943     setStore : function(store, initial){
16944         if(!initial && this.store){
16945             this.store.un("datachanged", this.refresh);
16946             this.store.un("add", this.onAdd);
16947             this.store.un("remove", this.onRemove);
16948             this.store.un("update", this.onUpdate);
16949             this.store.un("clear", this.refresh);
16950             this.store.un("beforeload", this.onBeforeLoad);
16951             this.store.un("load", this.onLoad);
16952             this.store.un("loadexception", this.onLoad);
16953         }
16954         if(store){
16955           
16956             store.on("datachanged", this.refresh, this);
16957             store.on("add", this.onAdd, this);
16958             store.on("remove", this.onRemove, this);
16959             store.on("update", this.onUpdate, this);
16960             store.on("clear", this.refresh, this);
16961             store.on("beforeload", this.onBeforeLoad, this);
16962             store.on("load", this.onLoad, this);
16963             store.on("loadexception", this.onLoad, this);
16964         }
16965         
16966         if(store){
16967             this.refresh();
16968         }
16969     },
16970     /**
16971      * onbeforeLoad - masks the loading area.
16972      *
16973      */
16974     onBeforeLoad : function(store,opts)
16975     {
16976          //Roo.log('onBeforeLoad');   
16977         if (!opts.add) {
16978             this.el.update("");
16979         }
16980         this.el.mask(this.mask ? this.mask : "Loading" ); 
16981     },
16982     onLoad : function ()
16983     {
16984         this.el.unmask();
16985     },
16986     
16987
16988     /**
16989      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16990      * @param {HTMLElement} node
16991      * @return {HTMLElement} The template node
16992      */
16993     findItemFromChild : function(node){
16994         var el = this.dataName  ?
16995             this.el.child('.roo-tpl-' + this.dataName,true) :
16996             this.el.dom; 
16997         
16998         if(!node || node.parentNode == el){
16999                     return node;
17000             }
17001             var p = node.parentNode;
17002             while(p && p != el){
17003             if(p.parentNode == el){
17004                 return p;
17005             }
17006             p = p.parentNode;
17007         }
17008             return null;
17009     },
17010
17011     /** @ignore */
17012     onClick : function(e){
17013         var item = this.findItemFromChild(e.getTarget());
17014         if(item){
17015             var index = this.indexOf(item);
17016             if(this.onItemClick(item, index, e) !== false){
17017                 this.fireEvent("click", this, index, item, e);
17018             }
17019         }else{
17020             this.clearSelections();
17021         }
17022     },
17023
17024     /** @ignore */
17025     onContextMenu : function(e){
17026         var item = this.findItemFromChild(e.getTarget());
17027         if(item){
17028             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17029         }
17030     },
17031
17032     /** @ignore */
17033     onDblClick : function(e){
17034         var item = this.findItemFromChild(e.getTarget());
17035         if(item){
17036             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17037         }
17038     },
17039
17040     onItemClick : function(item, index, e)
17041     {
17042         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17043             return false;
17044         }
17045         if (this.toggleSelect) {
17046             var m = this.isSelected(item) ? 'unselect' : 'select';
17047             //Roo.log(m);
17048             var _t = this;
17049             _t[m](item, true, false);
17050             return true;
17051         }
17052         if(this.multiSelect || this.singleSelect){
17053             if(this.multiSelect && e.shiftKey && this.lastSelection){
17054                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17055             }else{
17056                 this.select(item, this.multiSelect && e.ctrlKey);
17057                 this.lastSelection = item;
17058             }
17059             
17060             if(!this.tickable){
17061                 e.preventDefault();
17062             }
17063             
17064         }
17065         return true;
17066     },
17067
17068     /**
17069      * Get the number of selected nodes.
17070      * @return {Number}
17071      */
17072     getSelectionCount : function(){
17073         return this.selections.length;
17074     },
17075
17076     /**
17077      * Get the currently selected nodes.
17078      * @return {Array} An array of HTMLElements
17079      */
17080     getSelectedNodes : function(){
17081         return this.selections;
17082     },
17083
17084     /**
17085      * Get the indexes of the selected nodes.
17086      * @return {Array}
17087      */
17088     getSelectedIndexes : function(){
17089         var indexes = [], s = this.selections;
17090         for(var i = 0, len = s.length; i < len; i++){
17091             indexes.push(s[i].nodeIndex);
17092         }
17093         return indexes;
17094     },
17095
17096     /**
17097      * Clear all selections
17098      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17099      */
17100     clearSelections : function(suppressEvent){
17101         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17102             this.cmp.elements = this.selections;
17103             this.cmp.removeClass(this.selectedClass);
17104             this.selections = [];
17105             if(!suppressEvent){
17106                 this.fireEvent("selectionchange", this, this.selections);
17107             }
17108         }
17109     },
17110
17111     /**
17112      * Returns true if the passed node is selected
17113      * @param {HTMLElement/Number} node The node or node index
17114      * @return {Boolean}
17115      */
17116     isSelected : function(node){
17117         var s = this.selections;
17118         if(s.length < 1){
17119             return false;
17120         }
17121         node = this.getNode(node);
17122         return s.indexOf(node) !== -1;
17123     },
17124
17125     /**
17126      * Selects nodes.
17127      * @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
17128      * @param {Boolean} keepExisting (optional) true to keep existing selections
17129      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17130      */
17131     select : function(nodeInfo, keepExisting, suppressEvent){
17132         if(nodeInfo instanceof Array){
17133             if(!keepExisting){
17134                 this.clearSelections(true);
17135             }
17136             for(var i = 0, len = nodeInfo.length; i < len; i++){
17137                 this.select(nodeInfo[i], true, true);
17138             }
17139             return;
17140         } 
17141         var node = this.getNode(nodeInfo);
17142         if(!node || this.isSelected(node)){
17143             return; // already selected.
17144         }
17145         if(!keepExisting){
17146             this.clearSelections(true);
17147         }
17148         
17149         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17150             Roo.fly(node).addClass(this.selectedClass);
17151             this.selections.push(node);
17152             if(!suppressEvent){
17153                 this.fireEvent("selectionchange", this, this.selections);
17154             }
17155         }
17156         
17157         
17158     },
17159       /**
17160      * Unselects nodes.
17161      * @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
17162      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17163      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17164      */
17165     unselect : function(nodeInfo, keepExisting, suppressEvent)
17166     {
17167         if(nodeInfo instanceof Array){
17168             Roo.each(this.selections, function(s) {
17169                 this.unselect(s, nodeInfo);
17170             }, this);
17171             return;
17172         }
17173         var node = this.getNode(nodeInfo);
17174         if(!node || !this.isSelected(node)){
17175             //Roo.log("not selected");
17176             return; // not selected.
17177         }
17178         // fireevent???
17179         var ns = [];
17180         Roo.each(this.selections, function(s) {
17181             if (s == node ) {
17182                 Roo.fly(node).removeClass(this.selectedClass);
17183
17184                 return;
17185             }
17186             ns.push(s);
17187         },this);
17188         
17189         this.selections= ns;
17190         this.fireEvent("selectionchange", this, this.selections);
17191     },
17192
17193     /**
17194      * Gets a template node.
17195      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17196      * @return {HTMLElement} The node or null if it wasn't found
17197      */
17198     getNode : function(nodeInfo){
17199         if(typeof nodeInfo == "string"){
17200             return document.getElementById(nodeInfo);
17201         }else if(typeof nodeInfo == "number"){
17202             return this.nodes[nodeInfo];
17203         }
17204         return nodeInfo;
17205     },
17206
17207     /**
17208      * Gets a range template nodes.
17209      * @param {Number} startIndex
17210      * @param {Number} endIndex
17211      * @return {Array} An array of nodes
17212      */
17213     getNodes : function(start, end){
17214         var ns = this.nodes;
17215         start = start || 0;
17216         end = typeof end == "undefined" ? ns.length - 1 : end;
17217         var nodes = [];
17218         if(start <= end){
17219             for(var i = start; i <= end; i++){
17220                 nodes.push(ns[i]);
17221             }
17222         } else{
17223             for(var i = start; i >= end; i--){
17224                 nodes.push(ns[i]);
17225             }
17226         }
17227         return nodes;
17228     },
17229
17230     /**
17231      * Finds the index of the passed node
17232      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17233      * @return {Number} The index of the node or -1
17234      */
17235     indexOf : function(node){
17236         node = this.getNode(node);
17237         if(typeof node.nodeIndex == "number"){
17238             return node.nodeIndex;
17239         }
17240         var ns = this.nodes;
17241         for(var i = 0, len = ns.length; i < len; i++){
17242             if(ns[i] == node){
17243                 return i;
17244             }
17245         }
17246         return -1;
17247     }
17248 });
17249 /*
17250  * - LGPL
17251  *
17252  * based on jquery fullcalendar
17253  * 
17254  */
17255
17256 Roo.bootstrap = Roo.bootstrap || {};
17257 /**
17258  * @class Roo.bootstrap.Calendar
17259  * @extends Roo.bootstrap.Component
17260  * Bootstrap Calendar class
17261  * @cfg {Boolean} loadMask (true|false) default false
17262  * @cfg {Object} header generate the user specific header of the calendar, default false
17263
17264  * @constructor
17265  * Create a new Container
17266  * @param {Object} config The config object
17267  */
17268
17269
17270
17271 Roo.bootstrap.Calendar = function(config){
17272     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17273      this.addEvents({
17274         /**
17275              * @event select
17276              * Fires when a date is selected
17277              * @param {DatePicker} this
17278              * @param {Date} date The selected date
17279              */
17280         'select': true,
17281         /**
17282              * @event monthchange
17283              * Fires when the displayed month changes 
17284              * @param {DatePicker} this
17285              * @param {Date} date The selected month
17286              */
17287         'monthchange': true,
17288         /**
17289              * @event evententer
17290              * Fires when mouse over an event
17291              * @param {Calendar} this
17292              * @param {event} Event
17293              */
17294         'evententer': true,
17295         /**
17296              * @event eventleave
17297              * Fires when the mouse leaves an
17298              * @param {Calendar} this
17299              * @param {event}
17300              */
17301         'eventleave': true,
17302         /**
17303              * @event eventclick
17304              * Fires when the mouse click an
17305              * @param {Calendar} this
17306              * @param {event}
17307              */
17308         'eventclick': true
17309         
17310     });
17311
17312 };
17313
17314 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17315     
17316      /**
17317      * @cfg {Number} startDay
17318      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17319      */
17320     startDay : 0,
17321     
17322     loadMask : false,
17323     
17324     header : false,
17325       
17326     getAutoCreate : function(){
17327         
17328         
17329         var fc_button = function(name, corner, style, content ) {
17330             return Roo.apply({},{
17331                 tag : 'span',
17332                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17333                          (corner.length ?
17334                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17335                             ''
17336                         ),
17337                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17338                 unselectable: 'on'
17339             });
17340         };
17341         
17342         var header = {};
17343         
17344         if(!this.header){
17345             header = {
17346                 tag : 'table',
17347                 cls : 'fc-header',
17348                 style : 'width:100%',
17349                 cn : [
17350                     {
17351                         tag: 'tr',
17352                         cn : [
17353                             {
17354                                 tag : 'td',
17355                                 cls : 'fc-header-left',
17356                                 cn : [
17357                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17358                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17359                                     { tag: 'span', cls: 'fc-header-space' },
17360                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17361
17362
17363                                 ]
17364                             },
17365
17366                             {
17367                                 tag : 'td',
17368                                 cls : 'fc-header-center',
17369                                 cn : [
17370                                     {
17371                                         tag: 'span',
17372                                         cls: 'fc-header-title',
17373                                         cn : {
17374                                             tag: 'H2',
17375                                             html : 'month / year'
17376                                         }
17377                                     }
17378
17379                                 ]
17380                             },
17381                             {
17382                                 tag : 'td',
17383                                 cls : 'fc-header-right',
17384                                 cn : [
17385                               /*      fc_button('month', 'left', '', 'month' ),
17386                                     fc_button('week', '', '', 'week' ),
17387                                     fc_button('day', 'right', '', 'day' )
17388                                 */    
17389
17390                                 ]
17391                             }
17392
17393                         ]
17394                     }
17395                 ]
17396             };
17397         }
17398         
17399         header = this.header;
17400         
17401        
17402         var cal_heads = function() {
17403             var ret = [];
17404             // fixme - handle this.
17405             
17406             for (var i =0; i < Date.dayNames.length; i++) {
17407                 var d = Date.dayNames[i];
17408                 ret.push({
17409                     tag: 'th',
17410                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17411                     html : d.substring(0,3)
17412                 });
17413                 
17414             }
17415             ret[0].cls += ' fc-first';
17416             ret[6].cls += ' fc-last';
17417             return ret;
17418         };
17419         var cal_cell = function(n) {
17420             return  {
17421                 tag: 'td',
17422                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17423                 cn : [
17424                     {
17425                         cn : [
17426                             {
17427                                 cls: 'fc-day-number',
17428                                 html: 'D'
17429                             },
17430                             {
17431                                 cls: 'fc-day-content',
17432                              
17433                                 cn : [
17434                                      {
17435                                         style: 'position: relative;' // height: 17px;
17436                                     }
17437                                 ]
17438                             }
17439                             
17440                             
17441                         ]
17442                     }
17443                 ]
17444                 
17445             }
17446         };
17447         var cal_rows = function() {
17448             
17449             var ret = [];
17450             for (var r = 0; r < 6; r++) {
17451                 var row= {
17452                     tag : 'tr',
17453                     cls : 'fc-week',
17454                     cn : []
17455                 };
17456                 
17457                 for (var i =0; i < Date.dayNames.length; i++) {
17458                     var d = Date.dayNames[i];
17459                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17460
17461                 }
17462                 row.cn[0].cls+=' fc-first';
17463                 row.cn[0].cn[0].style = 'min-height:90px';
17464                 row.cn[6].cls+=' fc-last';
17465                 ret.push(row);
17466                 
17467             }
17468             ret[0].cls += ' fc-first';
17469             ret[4].cls += ' fc-prev-last';
17470             ret[5].cls += ' fc-last';
17471             return ret;
17472             
17473         };
17474         
17475         var cal_table = {
17476             tag: 'table',
17477             cls: 'fc-border-separate',
17478             style : 'width:100%',
17479             cellspacing  : 0,
17480             cn : [
17481                 { 
17482                     tag: 'thead',
17483                     cn : [
17484                         { 
17485                             tag: 'tr',
17486                             cls : 'fc-first fc-last',
17487                             cn : cal_heads()
17488                         }
17489                     ]
17490                 },
17491                 { 
17492                     tag: 'tbody',
17493                     cn : cal_rows()
17494                 }
17495                   
17496             ]
17497         };
17498          
17499          var cfg = {
17500             cls : 'fc fc-ltr',
17501             cn : [
17502                 header,
17503                 {
17504                     cls : 'fc-content',
17505                     style : "position: relative;",
17506                     cn : [
17507                         {
17508                             cls : 'fc-view fc-view-month fc-grid',
17509                             style : 'position: relative',
17510                             unselectable : 'on',
17511                             cn : [
17512                                 {
17513                                     cls : 'fc-event-container',
17514                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17515                                 },
17516                                 cal_table
17517                             ]
17518                         }
17519                     ]
17520     
17521                 }
17522            ] 
17523             
17524         };
17525         
17526          
17527         
17528         return cfg;
17529     },
17530     
17531     
17532     initEvents : function()
17533     {
17534         if(!this.store){
17535             throw "can not find store for calendar";
17536         }
17537         
17538         var mark = {
17539             tag: "div",
17540             cls:"x-dlg-mask",
17541             style: "text-align:center",
17542             cn: [
17543                 {
17544                     tag: "div",
17545                     style: "background-color:white;width:50%;margin:250 auto",
17546                     cn: [
17547                         {
17548                             tag: "img",
17549                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17550                         },
17551                         {
17552                             tag: "span",
17553                             html: "Loading"
17554                         }
17555                         
17556                     ]
17557                 }
17558             ]
17559         };
17560         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17561         
17562         var size = this.el.select('.fc-content', true).first().getSize();
17563         this.maskEl.setSize(size.width, size.height);
17564         this.maskEl.enableDisplayMode("block");
17565         if(!this.loadMask){
17566             this.maskEl.hide();
17567         }
17568         
17569         this.store = Roo.factory(this.store, Roo.data);
17570         this.store.on('load', this.onLoad, this);
17571         this.store.on('beforeload', this.onBeforeLoad, this);
17572         
17573         this.resize();
17574         
17575         this.cells = this.el.select('.fc-day',true);
17576         //Roo.log(this.cells);
17577         this.textNodes = this.el.query('.fc-day-number');
17578         this.cells.addClassOnOver('fc-state-hover');
17579         
17580         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17581         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17582         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17583         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17584         
17585         this.on('monthchange', this.onMonthChange, this);
17586         
17587         this.update(new Date().clearTime());
17588     },
17589     
17590     resize : function() {
17591         var sz  = this.el.getSize();
17592         
17593         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17594         this.el.select('.fc-day-content div',true).setHeight(34);
17595     },
17596     
17597     
17598     // private
17599     showPrevMonth : function(e){
17600         this.update(this.activeDate.add("mo", -1));
17601     },
17602     showToday : function(e){
17603         this.update(new Date().clearTime());
17604     },
17605     // private
17606     showNextMonth : function(e){
17607         this.update(this.activeDate.add("mo", 1));
17608     },
17609
17610     // private
17611     showPrevYear : function(){
17612         this.update(this.activeDate.add("y", -1));
17613     },
17614
17615     // private
17616     showNextYear : function(){
17617         this.update(this.activeDate.add("y", 1));
17618     },
17619
17620     
17621    // private
17622     update : function(date)
17623     {
17624         var vd = this.activeDate;
17625         this.activeDate = date;
17626 //        if(vd && this.el){
17627 //            var t = date.getTime();
17628 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17629 //                Roo.log('using add remove');
17630 //                
17631 //                this.fireEvent('monthchange', this, date);
17632 //                
17633 //                this.cells.removeClass("fc-state-highlight");
17634 //                this.cells.each(function(c){
17635 //                   if(c.dateValue == t){
17636 //                       c.addClass("fc-state-highlight");
17637 //                       setTimeout(function(){
17638 //                            try{c.dom.firstChild.focus();}catch(e){}
17639 //                       }, 50);
17640 //                       return false;
17641 //                   }
17642 //                   return true;
17643 //                });
17644 //                return;
17645 //            }
17646 //        }
17647         
17648         var days = date.getDaysInMonth();
17649         
17650         var firstOfMonth = date.getFirstDateOfMonth();
17651         var startingPos = firstOfMonth.getDay()-this.startDay;
17652         
17653         if(startingPos < this.startDay){
17654             startingPos += 7;
17655         }
17656         
17657         var pm = date.add(Date.MONTH, -1);
17658         var prevStart = pm.getDaysInMonth()-startingPos;
17659 //        
17660         this.cells = this.el.select('.fc-day',true);
17661         this.textNodes = this.el.query('.fc-day-number');
17662         this.cells.addClassOnOver('fc-state-hover');
17663         
17664         var cells = this.cells.elements;
17665         var textEls = this.textNodes;
17666         
17667         Roo.each(cells, function(cell){
17668             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17669         });
17670         
17671         days += startingPos;
17672
17673         // convert everything to numbers so it's fast
17674         var day = 86400000;
17675         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17676         //Roo.log(d);
17677         //Roo.log(pm);
17678         //Roo.log(prevStart);
17679         
17680         var today = new Date().clearTime().getTime();
17681         var sel = date.clearTime().getTime();
17682         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17683         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17684         var ddMatch = this.disabledDatesRE;
17685         var ddText = this.disabledDatesText;
17686         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17687         var ddaysText = this.disabledDaysText;
17688         var format = this.format;
17689         
17690         var setCellClass = function(cal, cell){
17691             cell.row = 0;
17692             cell.events = [];
17693             cell.more = [];
17694             //Roo.log('set Cell Class');
17695             cell.title = "";
17696             var t = d.getTime();
17697             
17698             //Roo.log(d);
17699             
17700             cell.dateValue = t;
17701             if(t == today){
17702                 cell.className += " fc-today";
17703                 cell.className += " fc-state-highlight";
17704                 cell.title = cal.todayText;
17705             }
17706             if(t == sel){
17707                 // disable highlight in other month..
17708                 //cell.className += " fc-state-highlight";
17709                 
17710             }
17711             // disabling
17712             if(t < min) {
17713                 cell.className = " fc-state-disabled";
17714                 cell.title = cal.minText;
17715                 return;
17716             }
17717             if(t > max) {
17718                 cell.className = " fc-state-disabled";
17719                 cell.title = cal.maxText;
17720                 return;
17721             }
17722             if(ddays){
17723                 if(ddays.indexOf(d.getDay()) != -1){
17724                     cell.title = ddaysText;
17725                     cell.className = " fc-state-disabled";
17726                 }
17727             }
17728             if(ddMatch && format){
17729                 var fvalue = d.dateFormat(format);
17730                 if(ddMatch.test(fvalue)){
17731                     cell.title = ddText.replace("%0", fvalue);
17732                     cell.className = " fc-state-disabled";
17733                 }
17734             }
17735             
17736             if (!cell.initialClassName) {
17737                 cell.initialClassName = cell.dom.className;
17738             }
17739             
17740             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17741         };
17742
17743         var i = 0;
17744         
17745         for(; i < startingPos; i++) {
17746             textEls[i].innerHTML = (++prevStart);
17747             d.setDate(d.getDate()+1);
17748             
17749             cells[i].className = "fc-past fc-other-month";
17750             setCellClass(this, cells[i]);
17751         }
17752         
17753         var intDay = 0;
17754         
17755         for(; i < days; i++){
17756             intDay = i - startingPos + 1;
17757             textEls[i].innerHTML = (intDay);
17758             d.setDate(d.getDate()+1);
17759             
17760             cells[i].className = ''; // "x-date-active";
17761             setCellClass(this, cells[i]);
17762         }
17763         var extraDays = 0;
17764         
17765         for(; i < 42; i++) {
17766             textEls[i].innerHTML = (++extraDays);
17767             d.setDate(d.getDate()+1);
17768             
17769             cells[i].className = "fc-future fc-other-month";
17770             setCellClass(this, cells[i]);
17771         }
17772         
17773         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17774         
17775         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17776         
17777         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17778         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17779         
17780         if(totalRows != 6){
17781             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17782             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17783         }
17784         
17785         this.fireEvent('monthchange', this, date);
17786         
17787         
17788         /*
17789         if(!this.internalRender){
17790             var main = this.el.dom.firstChild;
17791             var w = main.offsetWidth;
17792             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17793             Roo.fly(main).setWidth(w);
17794             this.internalRender = true;
17795             // opera does not respect the auto grow header center column
17796             // then, after it gets a width opera refuses to recalculate
17797             // without a second pass
17798             if(Roo.isOpera && !this.secondPass){
17799                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17800                 this.secondPass = true;
17801                 this.update.defer(10, this, [date]);
17802             }
17803         }
17804         */
17805         
17806     },
17807     
17808     findCell : function(dt) {
17809         dt = dt.clearTime().getTime();
17810         var ret = false;
17811         this.cells.each(function(c){
17812             //Roo.log("check " +c.dateValue + '?=' + dt);
17813             if(c.dateValue == dt){
17814                 ret = c;
17815                 return false;
17816             }
17817             return true;
17818         });
17819         
17820         return ret;
17821     },
17822     
17823     findCells : function(ev) {
17824         var s = ev.start.clone().clearTime().getTime();
17825        // Roo.log(s);
17826         var e= ev.end.clone().clearTime().getTime();
17827        // Roo.log(e);
17828         var ret = [];
17829         this.cells.each(function(c){
17830              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17831             
17832             if(c.dateValue > e){
17833                 return ;
17834             }
17835             if(c.dateValue < s){
17836                 return ;
17837             }
17838             ret.push(c);
17839         });
17840         
17841         return ret;    
17842     },
17843     
17844 //    findBestRow: function(cells)
17845 //    {
17846 //        var ret = 0;
17847 //        
17848 //        for (var i =0 ; i < cells.length;i++) {
17849 //            ret  = Math.max(cells[i].rows || 0,ret);
17850 //        }
17851 //        return ret;
17852 //        
17853 //    },
17854     
17855     
17856     addItem : function(ev)
17857     {
17858         // look for vertical location slot in
17859         var cells = this.findCells(ev);
17860         
17861 //        ev.row = this.findBestRow(cells);
17862         
17863         // work out the location.
17864         
17865         var crow = false;
17866         var rows = [];
17867         for(var i =0; i < cells.length; i++) {
17868             
17869             cells[i].row = cells[0].row;
17870             
17871             if(i == 0){
17872                 cells[i].row = cells[i].row + 1;
17873             }
17874             
17875             if (!crow) {
17876                 crow = {
17877                     start : cells[i],
17878                     end :  cells[i]
17879                 };
17880                 continue;
17881             }
17882             if (crow.start.getY() == cells[i].getY()) {
17883                 // on same row.
17884                 crow.end = cells[i];
17885                 continue;
17886             }
17887             // different row.
17888             rows.push(crow);
17889             crow = {
17890                 start: cells[i],
17891                 end : cells[i]
17892             };
17893             
17894         }
17895         
17896         rows.push(crow);
17897         ev.els = [];
17898         ev.rows = rows;
17899         ev.cells = cells;
17900         
17901         cells[0].events.push(ev);
17902         
17903         this.calevents.push(ev);
17904     },
17905     
17906     clearEvents: function() {
17907         
17908         if(!this.calevents){
17909             return;
17910         }
17911         
17912         Roo.each(this.cells.elements, function(c){
17913             c.row = 0;
17914             c.events = [];
17915             c.more = [];
17916         });
17917         
17918         Roo.each(this.calevents, function(e) {
17919             Roo.each(e.els, function(el) {
17920                 el.un('mouseenter' ,this.onEventEnter, this);
17921                 el.un('mouseleave' ,this.onEventLeave, this);
17922                 el.remove();
17923             },this);
17924         },this);
17925         
17926         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17927             e.remove();
17928         });
17929         
17930     },
17931     
17932     renderEvents: function()
17933     {   
17934         var _this = this;
17935         
17936         this.cells.each(function(c) {
17937             
17938             if(c.row < 5){
17939                 return;
17940             }
17941             
17942             var ev = c.events;
17943             
17944             var r = 4;
17945             if(c.row != c.events.length){
17946                 r = 4 - (4 - (c.row - c.events.length));
17947             }
17948             
17949             c.events = ev.slice(0, r);
17950             c.more = ev.slice(r);
17951             
17952             if(c.more.length && c.more.length == 1){
17953                 c.events.push(c.more.pop());
17954             }
17955             
17956             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17957             
17958         });
17959             
17960         this.cells.each(function(c) {
17961             
17962             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17963             
17964             
17965             for (var e = 0; e < c.events.length; e++){
17966                 var ev = c.events[e];
17967                 var rows = ev.rows;
17968                 
17969                 for(var i = 0; i < rows.length; i++) {
17970                 
17971                     // how many rows should it span..
17972
17973                     var  cfg = {
17974                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17975                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17976
17977                         unselectable : "on",
17978                         cn : [
17979                             {
17980                                 cls: 'fc-event-inner',
17981                                 cn : [
17982     //                                {
17983     //                                  tag:'span',
17984     //                                  cls: 'fc-event-time',
17985     //                                  html : cells.length > 1 ? '' : ev.time
17986     //                                },
17987                                     {
17988                                       tag:'span',
17989                                       cls: 'fc-event-title',
17990                                       html : String.format('{0}', ev.title)
17991                                     }
17992
17993
17994                                 ]
17995                             },
17996                             {
17997                                 cls: 'ui-resizable-handle ui-resizable-e',
17998                                 html : '&nbsp;&nbsp;&nbsp'
17999                             }
18000
18001                         ]
18002                     };
18003
18004                     if (i == 0) {
18005                         cfg.cls += ' fc-event-start';
18006                     }
18007                     if ((i+1) == rows.length) {
18008                         cfg.cls += ' fc-event-end';
18009                     }
18010
18011                     var ctr = _this.el.select('.fc-event-container',true).first();
18012                     var cg = ctr.createChild(cfg);
18013
18014                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18015                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18016
18017                     var r = (c.more.length) ? 1 : 0;
18018                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18019                     cg.setWidth(ebox.right - sbox.x -2);
18020
18021                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18022                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18023                     cg.on('click', _this.onEventClick, _this, ev);
18024
18025                     ev.els.push(cg);
18026                     
18027                 }
18028                 
18029             }
18030             
18031             
18032             if(c.more.length){
18033                 var  cfg = {
18034                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18035                     style : 'position: absolute',
18036                     unselectable : "on",
18037                     cn : [
18038                         {
18039                             cls: 'fc-event-inner',
18040                             cn : [
18041                                 {
18042                                   tag:'span',
18043                                   cls: 'fc-event-title',
18044                                   html : 'More'
18045                                 }
18046
18047
18048                             ]
18049                         },
18050                         {
18051                             cls: 'ui-resizable-handle ui-resizable-e',
18052                             html : '&nbsp;&nbsp;&nbsp'
18053                         }
18054
18055                     ]
18056                 };
18057
18058                 var ctr = _this.el.select('.fc-event-container',true).first();
18059                 var cg = ctr.createChild(cfg);
18060
18061                 var sbox = c.select('.fc-day-content',true).first().getBox();
18062                 var ebox = c.select('.fc-day-content',true).first().getBox();
18063                 //Roo.log(cg);
18064                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18065                 cg.setWidth(ebox.right - sbox.x -2);
18066
18067                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18068                 
18069             }
18070             
18071         });
18072         
18073         
18074         
18075     },
18076     
18077     onEventEnter: function (e, el,event,d) {
18078         this.fireEvent('evententer', this, el, event);
18079     },
18080     
18081     onEventLeave: function (e, el,event,d) {
18082         this.fireEvent('eventleave', this, el, event);
18083     },
18084     
18085     onEventClick: function (e, el,event,d) {
18086         this.fireEvent('eventclick', this, el, event);
18087     },
18088     
18089     onMonthChange: function () {
18090         this.store.load();
18091     },
18092     
18093     onMoreEventClick: function(e, el, more)
18094     {
18095         var _this = this;
18096         
18097         this.calpopover.placement = 'right';
18098         this.calpopover.setTitle('More');
18099         
18100         this.calpopover.setContent('');
18101         
18102         var ctr = this.calpopover.el.select('.popover-content', true).first();
18103         
18104         Roo.each(more, function(m){
18105             var cfg = {
18106                 cls : 'fc-event-hori fc-event-draggable',
18107                 html : m.title
18108             };
18109             var cg = ctr.createChild(cfg);
18110             
18111             cg.on('click', _this.onEventClick, _this, m);
18112         });
18113         
18114         this.calpopover.show(el);
18115         
18116         
18117     },
18118     
18119     onLoad: function () 
18120     {   
18121         this.calevents = [];
18122         var cal = this;
18123         
18124         if(this.store.getCount() > 0){
18125             this.store.data.each(function(d){
18126                cal.addItem({
18127                     id : d.data.id,
18128                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18129                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18130                     time : d.data.start_time,
18131                     title : d.data.title,
18132                     description : d.data.description,
18133                     venue : d.data.venue
18134                 });
18135             });
18136         }
18137         
18138         this.renderEvents();
18139         
18140         if(this.calevents.length && this.loadMask){
18141             this.maskEl.hide();
18142         }
18143     },
18144     
18145     onBeforeLoad: function()
18146     {
18147         this.clearEvents();
18148         if(this.loadMask){
18149             this.maskEl.show();
18150         }
18151     }
18152 });
18153
18154  
18155  /*
18156  * - LGPL
18157  *
18158  * element
18159  * 
18160  */
18161
18162 /**
18163  * @class Roo.bootstrap.Popover
18164  * @extends Roo.bootstrap.Component
18165  * Bootstrap Popover class
18166  * @cfg {String} html contents of the popover   (or false to use children..)
18167  * @cfg {String} title of popover (or false to hide)
18168  * @cfg {String} placement how it is placed
18169  * @cfg {String} trigger click || hover (or false to trigger manually)
18170  * @cfg {String} over what (parent or false to trigger manually.)
18171  * @cfg {Number} delay - delay before showing
18172  
18173  * @constructor
18174  * Create a new Popover
18175  * @param {Object} config The config object
18176  */
18177
18178 Roo.bootstrap.Popover = function(config){
18179     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18180     
18181     this.addEvents({
18182         // raw events
18183          /**
18184          * @event show
18185          * After the popover show
18186          * 
18187          * @param {Roo.bootstrap.Popover} this
18188          */
18189         "show" : true,
18190         /**
18191          * @event hide
18192          * After the popover hide
18193          * 
18194          * @param {Roo.bootstrap.Popover} this
18195          */
18196         "hide" : true
18197     });
18198 };
18199
18200 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18201     
18202     title: 'Fill in a title',
18203     html: false,
18204     
18205     placement : 'right',
18206     trigger : 'hover', // hover
18207     
18208     delay : 0,
18209     
18210     over: 'parent',
18211     
18212     can_build_overlaid : false,
18213     
18214     getChildContainer : function()
18215     {
18216         return this.el.select('.popover-content',true).first();
18217     },
18218     
18219     getAutoCreate : function(){
18220          
18221         var cfg = {
18222            cls : 'popover roo-dynamic',
18223            style: 'display:block',
18224            cn : [
18225                 {
18226                     cls : 'arrow'
18227                 },
18228                 {
18229                     cls : 'popover-inner',
18230                     cn : [
18231                         {
18232                             tag: 'h3',
18233                             cls: 'popover-title popover-header',
18234                             html : this.title
18235                         },
18236                         {
18237                             cls : 'popover-content popover-body',
18238                             html : this.html
18239                         }
18240                     ]
18241                     
18242                 }
18243            ]
18244         };
18245         
18246         return cfg;
18247     },
18248     setTitle: function(str)
18249     {
18250         this.title = str;
18251         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18252     },
18253     setContent: function(str)
18254     {
18255         this.html = str;
18256         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18257     },
18258     // as it get's added to the bottom of the page.
18259     onRender : function(ct, position)
18260     {
18261         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18262         if(!this.el){
18263             var cfg = Roo.apply({},  this.getAutoCreate());
18264             cfg.id = Roo.id();
18265             
18266             if (this.cls) {
18267                 cfg.cls += ' ' + this.cls;
18268             }
18269             if (this.style) {
18270                 cfg.style = this.style;
18271             }
18272             //Roo.log("adding to ");
18273             this.el = Roo.get(document.body).createChild(cfg, position);
18274 //            Roo.log(this.el);
18275         }
18276         this.initEvents();
18277     },
18278     
18279     initEvents : function()
18280     {
18281         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18282         this.el.enableDisplayMode('block');
18283         this.el.hide();
18284         if (this.over === false) {
18285             return; 
18286         }
18287         if (this.triggers === false) {
18288             return;
18289         }
18290         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18291         var triggers = this.trigger ? this.trigger.split(' ') : [];
18292         Roo.each(triggers, function(trigger) {
18293         
18294             if (trigger == 'click') {
18295                 on_el.on('click', this.toggle, this);
18296             } else if (trigger != 'manual') {
18297                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18298                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18299       
18300                 on_el.on(eventIn  ,this.enter, this);
18301                 on_el.on(eventOut, this.leave, this);
18302             }
18303         }, this);
18304         
18305     },
18306     
18307     
18308     // private
18309     timeout : null,
18310     hoverState : null,
18311     
18312     toggle : function () {
18313         this.hoverState == 'in' ? this.leave() : this.enter();
18314     },
18315     
18316     enter : function () {
18317         
18318         clearTimeout(this.timeout);
18319     
18320         this.hoverState = 'in';
18321     
18322         if (!this.delay || !this.delay.show) {
18323             this.show();
18324             return;
18325         }
18326         var _t = this;
18327         this.timeout = setTimeout(function () {
18328             if (_t.hoverState == 'in') {
18329                 _t.show();
18330             }
18331         }, this.delay.show)
18332     },
18333     
18334     leave : function() {
18335         clearTimeout(this.timeout);
18336     
18337         this.hoverState = 'out';
18338     
18339         if (!this.delay || !this.delay.hide) {
18340             this.hide();
18341             return;
18342         }
18343         var _t = this;
18344         this.timeout = setTimeout(function () {
18345             if (_t.hoverState == 'out') {
18346                 _t.hide();
18347             }
18348         }, this.delay.hide)
18349     },
18350     
18351     show : function (on_el)
18352     {
18353         if (!on_el) {
18354             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18355         }
18356         
18357         // set content.
18358         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18359         if (this.html !== false) {
18360             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18361         }
18362         this.el.removeClass([
18363             'fade','top','bottom', 'left', 'right','in',
18364             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18365         ]);
18366         if (!this.title.length) {
18367             this.el.select('.popover-title',true).hide();
18368         }
18369         
18370         var placement = typeof this.placement == 'function' ?
18371             this.placement.call(this, this.el, on_el) :
18372             this.placement;
18373             
18374         var autoToken = /\s?auto?\s?/i;
18375         var autoPlace = autoToken.test(placement);
18376         if (autoPlace) {
18377             placement = placement.replace(autoToken, '') || 'top';
18378         }
18379         
18380         //this.el.detach()
18381         //this.el.setXY([0,0]);
18382         this.el.show();
18383         this.el.dom.style.display='block';
18384         this.el.addClass(placement);
18385         
18386         //this.el.appendTo(on_el);
18387         
18388         var p = this.getPosition();
18389         var box = this.el.getBox();
18390         
18391         if (autoPlace) {
18392             // fixme..
18393         }
18394         var align = Roo.bootstrap.Popover.alignment[placement];
18395         
18396 //        Roo.log(align);
18397         this.el.alignTo(on_el, align[0],align[1]);
18398         //var arrow = this.el.select('.arrow',true).first();
18399         //arrow.set(align[2], 
18400         
18401         this.el.addClass('in');
18402         
18403         
18404         if (this.el.hasClass('fade')) {
18405             // fade it?
18406         }
18407         
18408         this.hoverState = 'in';
18409         
18410         this.fireEvent('show', this);
18411         
18412     },
18413     hide : function()
18414     {
18415         this.el.setXY([0,0]);
18416         this.el.removeClass('in');
18417         this.el.hide();
18418         this.hoverState = null;
18419         
18420         this.fireEvent('hide', this);
18421     }
18422     
18423 });
18424
18425 Roo.bootstrap.Popover.alignment = {
18426     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18427     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18428     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18429     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18430 };
18431
18432  /*
18433  * - LGPL
18434  *
18435  * Progress
18436  * 
18437  */
18438
18439 /**
18440  * @class Roo.bootstrap.Progress
18441  * @extends Roo.bootstrap.Component
18442  * Bootstrap Progress class
18443  * @cfg {Boolean} striped striped of the progress bar
18444  * @cfg {Boolean} active animated of the progress bar
18445  * 
18446  * 
18447  * @constructor
18448  * Create a new Progress
18449  * @param {Object} config The config object
18450  */
18451
18452 Roo.bootstrap.Progress = function(config){
18453     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18454 };
18455
18456 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18457     
18458     striped : false,
18459     active: false,
18460     
18461     getAutoCreate : function(){
18462         var cfg = {
18463             tag: 'div',
18464             cls: 'progress'
18465         };
18466         
18467         
18468         if(this.striped){
18469             cfg.cls += ' progress-striped';
18470         }
18471       
18472         if(this.active){
18473             cfg.cls += ' active';
18474         }
18475         
18476         
18477         return cfg;
18478     }
18479    
18480 });
18481
18482  
18483
18484  /*
18485  * - LGPL
18486  *
18487  * ProgressBar
18488  * 
18489  */
18490
18491 /**
18492  * @class Roo.bootstrap.ProgressBar
18493  * @extends Roo.bootstrap.Component
18494  * Bootstrap ProgressBar class
18495  * @cfg {Number} aria_valuenow aria-value now
18496  * @cfg {Number} aria_valuemin aria-value min
18497  * @cfg {Number} aria_valuemax aria-value max
18498  * @cfg {String} label label for the progress bar
18499  * @cfg {String} panel (success | info | warning | danger )
18500  * @cfg {String} role role of the progress bar
18501  * @cfg {String} sr_only text
18502  * 
18503  * 
18504  * @constructor
18505  * Create a new ProgressBar
18506  * @param {Object} config The config object
18507  */
18508
18509 Roo.bootstrap.ProgressBar = function(config){
18510     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18511 };
18512
18513 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18514     
18515     aria_valuenow : 0,
18516     aria_valuemin : 0,
18517     aria_valuemax : 100,
18518     label : false,
18519     panel : false,
18520     role : false,
18521     sr_only: false,
18522     
18523     getAutoCreate : function()
18524     {
18525         
18526         var cfg = {
18527             tag: 'div',
18528             cls: 'progress-bar',
18529             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18530         };
18531         
18532         if(this.sr_only){
18533             cfg.cn = {
18534                 tag: 'span',
18535                 cls: 'sr-only',
18536                 html: this.sr_only
18537             }
18538         }
18539         
18540         if(this.role){
18541             cfg.role = this.role;
18542         }
18543         
18544         if(this.aria_valuenow){
18545             cfg['aria-valuenow'] = this.aria_valuenow;
18546         }
18547         
18548         if(this.aria_valuemin){
18549             cfg['aria-valuemin'] = this.aria_valuemin;
18550         }
18551         
18552         if(this.aria_valuemax){
18553             cfg['aria-valuemax'] = this.aria_valuemax;
18554         }
18555         
18556         if(this.label && !this.sr_only){
18557             cfg.html = this.label;
18558         }
18559         
18560         if(this.panel){
18561             cfg.cls += ' progress-bar-' + this.panel;
18562         }
18563         
18564         return cfg;
18565     },
18566     
18567     update : function(aria_valuenow)
18568     {
18569         this.aria_valuenow = aria_valuenow;
18570         
18571         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18572     }
18573    
18574 });
18575
18576  
18577
18578  /*
18579  * - LGPL
18580  *
18581  * column
18582  * 
18583  */
18584
18585 /**
18586  * @class Roo.bootstrap.TabGroup
18587  * @extends Roo.bootstrap.Column
18588  * Bootstrap Column class
18589  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18590  * @cfg {Boolean} carousel true to make the group behave like a carousel
18591  * @cfg {Boolean} bullets show bullets for the panels
18592  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18593  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18594  * @cfg {Boolean} showarrow (true|false) show arrow default true
18595  * 
18596  * @constructor
18597  * Create a new TabGroup
18598  * @param {Object} config The config object
18599  */
18600
18601 Roo.bootstrap.TabGroup = function(config){
18602     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18603     if (!this.navId) {
18604         this.navId = Roo.id();
18605     }
18606     this.tabs = [];
18607     Roo.bootstrap.TabGroup.register(this);
18608     
18609 };
18610
18611 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18612     
18613     carousel : false,
18614     transition : false,
18615     bullets : 0,
18616     timer : 0,
18617     autoslide : false,
18618     slideFn : false,
18619     slideOnTouch : false,
18620     showarrow : true,
18621     
18622     getAutoCreate : function()
18623     {
18624         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18625         
18626         cfg.cls += ' tab-content';
18627         
18628         if (this.carousel) {
18629             cfg.cls += ' carousel slide';
18630             
18631             cfg.cn = [{
18632                cls : 'carousel-inner',
18633                cn : []
18634             }];
18635         
18636             if(this.bullets  && !Roo.isTouch){
18637                 
18638                 var bullets = {
18639                     cls : 'carousel-bullets',
18640                     cn : []
18641                 };
18642                
18643                 if(this.bullets_cls){
18644                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18645                 }
18646                 
18647                 bullets.cn.push({
18648                     cls : 'clear'
18649                 });
18650                 
18651                 cfg.cn[0].cn.push(bullets);
18652             }
18653             
18654             if(this.showarrow){
18655                 cfg.cn[0].cn.push({
18656                     tag : 'div',
18657                     class : 'carousel-arrow',
18658                     cn : [
18659                         {
18660                             tag : 'div',
18661                             class : 'carousel-prev',
18662                             cn : [
18663                                 {
18664                                     tag : 'i',
18665                                     class : 'fa fa-chevron-left'
18666                                 }
18667                             ]
18668                         },
18669                         {
18670                             tag : 'div',
18671                             class : 'carousel-next',
18672                             cn : [
18673                                 {
18674                                     tag : 'i',
18675                                     class : 'fa fa-chevron-right'
18676                                 }
18677                             ]
18678                         }
18679                     ]
18680                 });
18681             }
18682             
18683         }
18684         
18685         return cfg;
18686     },
18687     
18688     initEvents:  function()
18689     {
18690 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18691 //            this.el.on("touchstart", this.onTouchStart, this);
18692 //        }
18693         
18694         if(this.autoslide){
18695             var _this = this;
18696             
18697             this.slideFn = window.setInterval(function() {
18698                 _this.showPanelNext();
18699             }, this.timer);
18700         }
18701         
18702         if(this.showarrow){
18703             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18704             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18705         }
18706         
18707         
18708     },
18709     
18710 //    onTouchStart : function(e, el, o)
18711 //    {
18712 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18713 //            return;
18714 //        }
18715 //        
18716 //        this.showPanelNext();
18717 //    },
18718     
18719     
18720     getChildContainer : function()
18721     {
18722         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18723     },
18724     
18725     /**
18726     * register a Navigation item
18727     * @param {Roo.bootstrap.NavItem} the navitem to add
18728     */
18729     register : function(item)
18730     {
18731         this.tabs.push( item);
18732         item.navId = this.navId; // not really needed..
18733         this.addBullet();
18734     
18735     },
18736     
18737     getActivePanel : function()
18738     {
18739         var r = false;
18740         Roo.each(this.tabs, function(t) {
18741             if (t.active) {
18742                 r = t;
18743                 return false;
18744             }
18745             return null;
18746         });
18747         return r;
18748         
18749     },
18750     getPanelByName : function(n)
18751     {
18752         var r = false;
18753         Roo.each(this.tabs, function(t) {
18754             if (t.tabId == n) {
18755                 r = t;
18756                 return false;
18757             }
18758             return null;
18759         });
18760         return r;
18761     },
18762     indexOfPanel : function(p)
18763     {
18764         var r = false;
18765         Roo.each(this.tabs, function(t,i) {
18766             if (t.tabId == p.tabId) {
18767                 r = i;
18768                 return false;
18769             }
18770             return null;
18771         });
18772         return r;
18773     },
18774     /**
18775      * show a specific panel
18776      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18777      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18778      */
18779     showPanel : function (pan)
18780     {
18781         if(this.transition || typeof(pan) == 'undefined'){
18782             Roo.log("waiting for the transitionend");
18783             return false;
18784         }
18785         
18786         if (typeof(pan) == 'number') {
18787             pan = this.tabs[pan];
18788         }
18789         
18790         if (typeof(pan) == 'string') {
18791             pan = this.getPanelByName(pan);
18792         }
18793         
18794         var cur = this.getActivePanel();
18795         
18796         if(!pan || !cur){
18797             Roo.log('pan or acitve pan is undefined');
18798             return false;
18799         }
18800         
18801         if (pan.tabId == this.getActivePanel().tabId) {
18802             return true;
18803         }
18804         
18805         if (false === cur.fireEvent('beforedeactivate')) {
18806             return false;
18807         }
18808         
18809         if(this.bullets > 0 && !Roo.isTouch){
18810             this.setActiveBullet(this.indexOfPanel(pan));
18811         }
18812         
18813         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18814             
18815             //class="carousel-item carousel-item-next carousel-item-left"
18816             
18817             this.transition = true;
18818             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18819             var lr = dir == 'next' ? 'left' : 'right';
18820             pan.el.addClass(dir); // or prev
18821             pan.el.addClass('carousel-item-' + dir); // or prev
18822             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18823             cur.el.addClass(lr); // or right
18824             pan.el.addClass(lr);
18825             cur.el.addClass('carousel-item-' +lr); // or right
18826             pan.el.addClass('carousel-item-' +lr);
18827             
18828             
18829             var _this = this;
18830             cur.el.on('transitionend', function() {
18831                 Roo.log("trans end?");
18832                 
18833                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18834                 pan.setActive(true);
18835                 
18836                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18837                 cur.setActive(false);
18838                 
18839                 _this.transition = false;
18840                 
18841             }, this, { single:  true } );
18842             
18843             return true;
18844         }
18845         
18846         cur.setActive(false);
18847         pan.setActive(true);
18848         
18849         return true;
18850         
18851     },
18852     showPanelNext : function()
18853     {
18854         var i = this.indexOfPanel(this.getActivePanel());
18855         
18856         if (i >= this.tabs.length - 1 && !this.autoslide) {
18857             return;
18858         }
18859         
18860         if (i >= this.tabs.length - 1 && this.autoslide) {
18861             i = -1;
18862         }
18863         
18864         this.showPanel(this.tabs[i+1]);
18865     },
18866     
18867     showPanelPrev : function()
18868     {
18869         var i = this.indexOfPanel(this.getActivePanel());
18870         
18871         if (i  < 1 && !this.autoslide) {
18872             return;
18873         }
18874         
18875         if (i < 1 && this.autoslide) {
18876             i = this.tabs.length;
18877         }
18878         
18879         this.showPanel(this.tabs[i-1]);
18880     },
18881     
18882     
18883     addBullet: function()
18884     {
18885         if(!this.bullets || Roo.isTouch){
18886             return;
18887         }
18888         var ctr = this.el.select('.carousel-bullets',true).first();
18889         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18890         var bullet = ctr.createChild({
18891             cls : 'bullet bullet-' + i
18892         },ctr.dom.lastChild);
18893         
18894         
18895         var _this = this;
18896         
18897         bullet.on('click', (function(e, el, o, ii, t){
18898
18899             e.preventDefault();
18900
18901             this.showPanel(ii);
18902
18903             if(this.autoslide && this.slideFn){
18904                 clearInterval(this.slideFn);
18905                 this.slideFn = window.setInterval(function() {
18906                     _this.showPanelNext();
18907                 }, this.timer);
18908             }
18909
18910         }).createDelegate(this, [i, bullet], true));
18911                 
18912         
18913     },
18914      
18915     setActiveBullet : function(i)
18916     {
18917         if(Roo.isTouch){
18918             return;
18919         }
18920         
18921         Roo.each(this.el.select('.bullet', true).elements, function(el){
18922             el.removeClass('selected');
18923         });
18924
18925         var bullet = this.el.select('.bullet-' + i, true).first();
18926         
18927         if(!bullet){
18928             return;
18929         }
18930         
18931         bullet.addClass('selected');
18932     }
18933     
18934     
18935   
18936 });
18937
18938  
18939
18940  
18941  
18942 Roo.apply(Roo.bootstrap.TabGroup, {
18943     
18944     groups: {},
18945      /**
18946     * register a Navigation Group
18947     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18948     */
18949     register : function(navgrp)
18950     {
18951         this.groups[navgrp.navId] = navgrp;
18952         
18953     },
18954     /**
18955     * fetch a Navigation Group based on the navigation ID
18956     * if one does not exist , it will get created.
18957     * @param {string} the navgroup to add
18958     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18959     */
18960     get: function(navId) {
18961         if (typeof(this.groups[navId]) == 'undefined') {
18962             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18963         }
18964         return this.groups[navId] ;
18965     }
18966     
18967     
18968     
18969 });
18970
18971  /*
18972  * - LGPL
18973  *
18974  * TabPanel
18975  * 
18976  */
18977
18978 /**
18979  * @class Roo.bootstrap.TabPanel
18980  * @extends Roo.bootstrap.Component
18981  * Bootstrap TabPanel class
18982  * @cfg {Boolean} active panel active
18983  * @cfg {String} html panel content
18984  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18985  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18986  * @cfg {String} href click to link..
18987  * 
18988  * 
18989  * @constructor
18990  * Create a new TabPanel
18991  * @param {Object} config The config object
18992  */
18993
18994 Roo.bootstrap.TabPanel = function(config){
18995     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18996     this.addEvents({
18997         /**
18998              * @event changed
18999              * Fires when the active status changes
19000              * @param {Roo.bootstrap.TabPanel} this
19001              * @param {Boolean} state the new state
19002             
19003          */
19004         'changed': true,
19005         /**
19006              * @event beforedeactivate
19007              * Fires before a tab is de-activated - can be used to do validation on a form.
19008              * @param {Roo.bootstrap.TabPanel} this
19009              * @return {Boolean} false if there is an error
19010             
19011          */
19012         'beforedeactivate': true
19013      });
19014     
19015     this.tabId = this.tabId || Roo.id();
19016   
19017 };
19018
19019 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19020     
19021     active: false,
19022     html: false,
19023     tabId: false,
19024     navId : false,
19025     href : '',
19026     
19027     getAutoCreate : function(){
19028         
19029         
19030         var cfg = {
19031             tag: 'div',
19032             // item is needed for carousel - not sure if it has any effect otherwise
19033             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19034             html: this.html || ''
19035         };
19036         
19037         if(this.active){
19038             cfg.cls += ' active';
19039         }
19040         
19041         if(this.tabId){
19042             cfg.tabId = this.tabId;
19043         }
19044         
19045         
19046         
19047         return cfg;
19048     },
19049     
19050     initEvents:  function()
19051     {
19052         var p = this.parent();
19053         
19054         this.navId = this.navId || p.navId;
19055         
19056         if (typeof(this.navId) != 'undefined') {
19057             // not really needed.. but just in case.. parent should be a NavGroup.
19058             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19059             
19060             tg.register(this);
19061             
19062             var i = tg.tabs.length - 1;
19063             
19064             if(this.active && tg.bullets > 0 && i < tg.bullets){
19065                 tg.setActiveBullet(i);
19066             }
19067         }
19068         
19069         this.el.on('click', this.onClick, this);
19070         
19071         if(Roo.isTouch){
19072             this.el.on("touchstart", this.onTouchStart, this);
19073             this.el.on("touchmove", this.onTouchMove, this);
19074             this.el.on("touchend", this.onTouchEnd, this);
19075         }
19076         
19077     },
19078     
19079     onRender : function(ct, position)
19080     {
19081         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19082     },
19083     
19084     setActive : function(state)
19085     {
19086         Roo.log("panel - set active " + this.tabId + "=" + state);
19087         
19088         this.active = state;
19089         if (!state) {
19090             this.el.removeClass('active');
19091             
19092         } else  if (!this.el.hasClass('active')) {
19093             this.el.addClass('active');
19094         }
19095         
19096         this.fireEvent('changed', this, state);
19097     },
19098     
19099     onClick : function(e)
19100     {
19101         e.preventDefault();
19102         
19103         if(!this.href.length){
19104             return;
19105         }
19106         
19107         window.location.href = this.href;
19108     },
19109     
19110     startX : 0,
19111     startY : 0,
19112     endX : 0,
19113     endY : 0,
19114     swiping : false,
19115     
19116     onTouchStart : function(e)
19117     {
19118         this.swiping = false;
19119         
19120         this.startX = e.browserEvent.touches[0].clientX;
19121         this.startY = e.browserEvent.touches[0].clientY;
19122     },
19123     
19124     onTouchMove : function(e)
19125     {
19126         this.swiping = true;
19127         
19128         this.endX = e.browserEvent.touches[0].clientX;
19129         this.endY = e.browserEvent.touches[0].clientY;
19130     },
19131     
19132     onTouchEnd : function(e)
19133     {
19134         if(!this.swiping){
19135             this.onClick(e);
19136             return;
19137         }
19138         
19139         var tabGroup = this.parent();
19140         
19141         if(this.endX > this.startX){ // swiping right
19142             tabGroup.showPanelPrev();
19143             return;
19144         }
19145         
19146         if(this.startX > this.endX){ // swiping left
19147             tabGroup.showPanelNext();
19148             return;
19149         }
19150     }
19151     
19152     
19153 });
19154  
19155
19156  
19157
19158  /*
19159  * - LGPL
19160  *
19161  * DateField
19162  * 
19163  */
19164
19165 /**
19166  * @class Roo.bootstrap.DateField
19167  * @extends Roo.bootstrap.Input
19168  * Bootstrap DateField class
19169  * @cfg {Number} weekStart default 0
19170  * @cfg {String} viewMode default empty, (months|years)
19171  * @cfg {String} minViewMode default empty, (months|years)
19172  * @cfg {Number} startDate default -Infinity
19173  * @cfg {Number} endDate default Infinity
19174  * @cfg {Boolean} todayHighlight default false
19175  * @cfg {Boolean} todayBtn default false
19176  * @cfg {Boolean} calendarWeeks default false
19177  * @cfg {Object} daysOfWeekDisabled default empty
19178  * @cfg {Boolean} singleMode default false (true | false)
19179  * 
19180  * @cfg {Boolean} keyboardNavigation default true
19181  * @cfg {String} language default en
19182  * 
19183  * @constructor
19184  * Create a new DateField
19185  * @param {Object} config The config object
19186  */
19187
19188 Roo.bootstrap.DateField = function(config){
19189     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19190      this.addEvents({
19191             /**
19192              * @event show
19193              * Fires when this field show.
19194              * @param {Roo.bootstrap.DateField} this
19195              * @param {Mixed} date The date value
19196              */
19197             show : true,
19198             /**
19199              * @event show
19200              * Fires when this field hide.
19201              * @param {Roo.bootstrap.DateField} this
19202              * @param {Mixed} date The date value
19203              */
19204             hide : true,
19205             /**
19206              * @event select
19207              * Fires when select a date.
19208              * @param {Roo.bootstrap.DateField} this
19209              * @param {Mixed} date The date value
19210              */
19211             select : true,
19212             /**
19213              * @event beforeselect
19214              * Fires when before select a date.
19215              * @param {Roo.bootstrap.DateField} this
19216              * @param {Mixed} date The date value
19217              */
19218             beforeselect : true
19219         });
19220 };
19221
19222 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19223     
19224     /**
19225      * @cfg {String} format
19226      * The default date format string which can be overriden for localization support.  The format must be
19227      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19228      */
19229     format : "m/d/y",
19230     /**
19231      * @cfg {String} altFormats
19232      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19233      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19234      */
19235     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19236     
19237     weekStart : 0,
19238     
19239     viewMode : '',
19240     
19241     minViewMode : '',
19242     
19243     todayHighlight : false,
19244     
19245     todayBtn: false,
19246     
19247     language: 'en',
19248     
19249     keyboardNavigation: true,
19250     
19251     calendarWeeks: false,
19252     
19253     startDate: -Infinity,
19254     
19255     endDate: Infinity,
19256     
19257     daysOfWeekDisabled: [],
19258     
19259     _events: [],
19260     
19261     singleMode : false,
19262     
19263     UTCDate: function()
19264     {
19265         return new Date(Date.UTC.apply(Date, arguments));
19266     },
19267     
19268     UTCToday: function()
19269     {
19270         var today = new Date();
19271         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19272     },
19273     
19274     getDate: function() {
19275             var d = this.getUTCDate();
19276             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19277     },
19278     
19279     getUTCDate: function() {
19280             return this.date;
19281     },
19282     
19283     setDate: function(d) {
19284             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19285     },
19286     
19287     setUTCDate: function(d) {
19288             this.date = d;
19289             this.setValue(this.formatDate(this.date));
19290     },
19291         
19292     onRender: function(ct, position)
19293     {
19294         
19295         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19296         
19297         this.language = this.language || 'en';
19298         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19299         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19300         
19301         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19302         this.format = this.format || 'm/d/y';
19303         this.isInline = false;
19304         this.isInput = true;
19305         this.component = this.el.select('.add-on', true).first() || false;
19306         this.component = (this.component && this.component.length === 0) ? false : this.component;
19307         this.hasInput = this.component && this.inputEl().length;
19308         
19309         if (typeof(this.minViewMode === 'string')) {
19310             switch (this.minViewMode) {
19311                 case 'months':
19312                     this.minViewMode = 1;
19313                     break;
19314                 case 'years':
19315                     this.minViewMode = 2;
19316                     break;
19317                 default:
19318                     this.minViewMode = 0;
19319                     break;
19320             }
19321         }
19322         
19323         if (typeof(this.viewMode === 'string')) {
19324             switch (this.viewMode) {
19325                 case 'months':
19326                     this.viewMode = 1;
19327                     break;
19328                 case 'years':
19329                     this.viewMode = 2;
19330                     break;
19331                 default:
19332                     this.viewMode = 0;
19333                     break;
19334             }
19335         }
19336                 
19337         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19338         
19339 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19340         
19341         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19342         
19343         this.picker().on('mousedown', this.onMousedown, this);
19344         this.picker().on('click', this.onClick, this);
19345         
19346         this.picker().addClass('datepicker-dropdown');
19347         
19348         this.startViewMode = this.viewMode;
19349         
19350         if(this.singleMode){
19351             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19352                 v.setVisibilityMode(Roo.Element.DISPLAY);
19353                 v.hide();
19354             });
19355             
19356             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19357                 v.setStyle('width', '189px');
19358             });
19359         }
19360         
19361         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19362             if(!this.calendarWeeks){
19363                 v.remove();
19364                 return;
19365             }
19366             
19367             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19368             v.attr('colspan', function(i, val){
19369                 return parseInt(val) + 1;
19370             });
19371         });
19372                         
19373         
19374         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19375         
19376         this.setStartDate(this.startDate);
19377         this.setEndDate(this.endDate);
19378         
19379         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19380         
19381         this.fillDow();
19382         this.fillMonths();
19383         this.update();
19384         this.showMode();
19385         
19386         if(this.isInline) {
19387             this.showPopup();
19388         }
19389     },
19390     
19391     picker : function()
19392     {
19393         return this.pickerEl;
19394 //        return this.el.select('.datepicker', true).first();
19395     },
19396     
19397     fillDow: function()
19398     {
19399         var dowCnt = this.weekStart;
19400         
19401         var dow = {
19402             tag: 'tr',
19403             cn: [
19404                 
19405             ]
19406         };
19407         
19408         if(this.calendarWeeks){
19409             dow.cn.push({
19410                 tag: 'th',
19411                 cls: 'cw',
19412                 html: '&nbsp;'
19413             })
19414         }
19415         
19416         while (dowCnt < this.weekStart + 7) {
19417             dow.cn.push({
19418                 tag: 'th',
19419                 cls: 'dow',
19420                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19421             });
19422         }
19423         
19424         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19425     },
19426     
19427     fillMonths: function()
19428     {    
19429         var i = 0;
19430         var months = this.picker().select('>.datepicker-months td', true).first();
19431         
19432         months.dom.innerHTML = '';
19433         
19434         while (i < 12) {
19435             var month = {
19436                 tag: 'span',
19437                 cls: 'month',
19438                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19439             };
19440             
19441             months.createChild(month);
19442         }
19443         
19444     },
19445     
19446     update: function()
19447     {
19448         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;
19449         
19450         if (this.date < this.startDate) {
19451             this.viewDate = new Date(this.startDate);
19452         } else if (this.date > this.endDate) {
19453             this.viewDate = new Date(this.endDate);
19454         } else {
19455             this.viewDate = new Date(this.date);
19456         }
19457         
19458         this.fill();
19459     },
19460     
19461     fill: function() 
19462     {
19463         var d = new Date(this.viewDate),
19464                 year = d.getUTCFullYear(),
19465                 month = d.getUTCMonth(),
19466                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19467                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19468                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19469                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19470                 currentDate = this.date && this.date.valueOf(),
19471                 today = this.UTCToday();
19472         
19473         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19474         
19475 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19476         
19477 //        this.picker.select('>tfoot th.today').
19478 //                                              .text(dates[this.language].today)
19479 //                                              .toggle(this.todayBtn !== false);
19480     
19481         this.updateNavArrows();
19482         this.fillMonths();
19483                                                 
19484         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19485         
19486         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19487          
19488         prevMonth.setUTCDate(day);
19489         
19490         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19491         
19492         var nextMonth = new Date(prevMonth);
19493         
19494         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19495         
19496         nextMonth = nextMonth.valueOf();
19497         
19498         var fillMonths = false;
19499         
19500         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19501         
19502         while(prevMonth.valueOf() <= nextMonth) {
19503             var clsName = '';
19504             
19505             if (prevMonth.getUTCDay() === this.weekStart) {
19506                 if(fillMonths){
19507                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19508                 }
19509                     
19510                 fillMonths = {
19511                     tag: 'tr',
19512                     cn: []
19513                 };
19514                 
19515                 if(this.calendarWeeks){
19516                     // ISO 8601: First week contains first thursday.
19517                     // ISO also states week starts on Monday, but we can be more abstract here.
19518                     var
19519                     // Start of current week: based on weekstart/current date
19520                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19521                     // Thursday of this week
19522                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19523                     // First Thursday of year, year from thursday
19524                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19525                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19526                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19527                     
19528                     fillMonths.cn.push({
19529                         tag: 'td',
19530                         cls: 'cw',
19531                         html: calWeek
19532                     });
19533                 }
19534             }
19535             
19536             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19537                 clsName += ' old';
19538             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19539                 clsName += ' new';
19540             }
19541             if (this.todayHighlight &&
19542                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19543                 prevMonth.getUTCMonth() == today.getMonth() &&
19544                 prevMonth.getUTCDate() == today.getDate()) {
19545                 clsName += ' today';
19546             }
19547             
19548             if (currentDate && prevMonth.valueOf() === currentDate) {
19549                 clsName += ' active';
19550             }
19551             
19552             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19553                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19554                     clsName += ' disabled';
19555             }
19556             
19557             fillMonths.cn.push({
19558                 tag: 'td',
19559                 cls: 'day ' + clsName,
19560                 html: prevMonth.getDate()
19561             });
19562             
19563             prevMonth.setDate(prevMonth.getDate()+1);
19564         }
19565           
19566         var currentYear = this.date && this.date.getUTCFullYear();
19567         var currentMonth = this.date && this.date.getUTCMonth();
19568         
19569         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19570         
19571         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19572             v.removeClass('active');
19573             
19574             if(currentYear === year && k === currentMonth){
19575                 v.addClass('active');
19576             }
19577             
19578             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19579                 v.addClass('disabled');
19580             }
19581             
19582         });
19583         
19584         
19585         year = parseInt(year/10, 10) * 10;
19586         
19587         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19588         
19589         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19590         
19591         year -= 1;
19592         for (var i = -1; i < 11; i++) {
19593             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19594                 tag: 'span',
19595                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19596                 html: year
19597             });
19598             
19599             year += 1;
19600         }
19601     },
19602     
19603     showMode: function(dir) 
19604     {
19605         if (dir) {
19606             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19607         }
19608         
19609         Roo.each(this.picker().select('>div',true).elements, function(v){
19610             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19611             v.hide();
19612         });
19613         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19614     },
19615     
19616     place: function()
19617     {
19618         if(this.isInline) {
19619             return;
19620         }
19621         
19622         this.picker().removeClass(['bottom', 'top']);
19623         
19624         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19625             /*
19626              * place to the top of element!
19627              *
19628              */
19629             
19630             this.picker().addClass('top');
19631             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19632             
19633             return;
19634         }
19635         
19636         this.picker().addClass('bottom');
19637         
19638         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19639     },
19640     
19641     parseDate : function(value)
19642     {
19643         if(!value || value instanceof Date){
19644             return value;
19645         }
19646         var v = Date.parseDate(value, this.format);
19647         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19648             v = Date.parseDate(value, 'Y-m-d');
19649         }
19650         if(!v && this.altFormats){
19651             if(!this.altFormatsArray){
19652                 this.altFormatsArray = this.altFormats.split("|");
19653             }
19654             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19655                 v = Date.parseDate(value, this.altFormatsArray[i]);
19656             }
19657         }
19658         return v;
19659     },
19660     
19661     formatDate : function(date, fmt)
19662     {   
19663         return (!date || !(date instanceof Date)) ?
19664         date : date.dateFormat(fmt || this.format);
19665     },
19666     
19667     onFocus : function()
19668     {
19669         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19670         this.showPopup();
19671     },
19672     
19673     onBlur : function()
19674     {
19675         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19676         
19677         var d = this.inputEl().getValue();
19678         
19679         this.setValue(d);
19680                 
19681         this.hidePopup();
19682     },
19683     
19684     showPopup : function()
19685     {
19686         this.picker().show();
19687         this.update();
19688         this.place();
19689         
19690         this.fireEvent('showpopup', this, this.date);
19691     },
19692     
19693     hidePopup : function()
19694     {
19695         if(this.isInline) {
19696             return;
19697         }
19698         this.picker().hide();
19699         this.viewMode = this.startViewMode;
19700         this.showMode();
19701         
19702         this.fireEvent('hidepopup', this, this.date);
19703         
19704     },
19705     
19706     onMousedown: function(e)
19707     {
19708         e.stopPropagation();
19709         e.preventDefault();
19710     },
19711     
19712     keyup: function(e)
19713     {
19714         Roo.bootstrap.DateField.superclass.keyup.call(this);
19715         this.update();
19716     },
19717
19718     setValue: function(v)
19719     {
19720         if(this.fireEvent('beforeselect', this, v) !== false){
19721             var d = new Date(this.parseDate(v) ).clearTime();
19722         
19723             if(isNaN(d.getTime())){
19724                 this.date = this.viewDate = '';
19725                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19726                 return;
19727             }
19728
19729             v = this.formatDate(d);
19730
19731             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19732
19733             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19734
19735             this.update();
19736
19737             this.fireEvent('select', this, this.date);
19738         }
19739     },
19740     
19741     getValue: function()
19742     {
19743         return this.formatDate(this.date);
19744     },
19745     
19746     fireKey: function(e)
19747     {
19748         if (!this.picker().isVisible()){
19749             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19750                 this.showPopup();
19751             }
19752             return;
19753         }
19754         
19755         var dateChanged = false,
19756         dir, day, month,
19757         newDate, newViewDate;
19758         
19759         switch(e.keyCode){
19760             case 27: // escape
19761                 this.hidePopup();
19762                 e.preventDefault();
19763                 break;
19764             case 37: // left
19765             case 39: // right
19766                 if (!this.keyboardNavigation) {
19767                     break;
19768                 }
19769                 dir = e.keyCode == 37 ? -1 : 1;
19770                 
19771                 if (e.ctrlKey){
19772                     newDate = this.moveYear(this.date, dir);
19773                     newViewDate = this.moveYear(this.viewDate, dir);
19774                 } else if (e.shiftKey){
19775                     newDate = this.moveMonth(this.date, dir);
19776                     newViewDate = this.moveMonth(this.viewDate, dir);
19777                 } else {
19778                     newDate = new Date(this.date);
19779                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19780                     newViewDate = new Date(this.viewDate);
19781                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19782                 }
19783                 if (this.dateWithinRange(newDate)){
19784                     this.date = newDate;
19785                     this.viewDate = newViewDate;
19786                     this.setValue(this.formatDate(this.date));
19787 //                    this.update();
19788                     e.preventDefault();
19789                     dateChanged = true;
19790                 }
19791                 break;
19792             case 38: // up
19793             case 40: // down
19794                 if (!this.keyboardNavigation) {
19795                     break;
19796                 }
19797                 dir = e.keyCode == 38 ? -1 : 1;
19798                 if (e.ctrlKey){
19799                     newDate = this.moveYear(this.date, dir);
19800                     newViewDate = this.moveYear(this.viewDate, dir);
19801                 } else if (e.shiftKey){
19802                     newDate = this.moveMonth(this.date, dir);
19803                     newViewDate = this.moveMonth(this.viewDate, dir);
19804                 } else {
19805                     newDate = new Date(this.date);
19806                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19807                     newViewDate = new Date(this.viewDate);
19808                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19809                 }
19810                 if (this.dateWithinRange(newDate)){
19811                     this.date = newDate;
19812                     this.viewDate = newViewDate;
19813                     this.setValue(this.formatDate(this.date));
19814 //                    this.update();
19815                     e.preventDefault();
19816                     dateChanged = true;
19817                 }
19818                 break;
19819             case 13: // enter
19820                 this.setValue(this.formatDate(this.date));
19821                 this.hidePopup();
19822                 e.preventDefault();
19823                 break;
19824             case 9: // tab
19825                 this.setValue(this.formatDate(this.date));
19826                 this.hidePopup();
19827                 break;
19828             case 16: // shift
19829             case 17: // ctrl
19830             case 18: // alt
19831                 break;
19832             default :
19833                 this.hidePopup();
19834                 
19835         }
19836     },
19837     
19838     
19839     onClick: function(e) 
19840     {
19841         e.stopPropagation();
19842         e.preventDefault();
19843         
19844         var target = e.getTarget();
19845         
19846         if(target.nodeName.toLowerCase() === 'i'){
19847             target = Roo.get(target).dom.parentNode;
19848         }
19849         
19850         var nodeName = target.nodeName;
19851         var className = target.className;
19852         var html = target.innerHTML;
19853         //Roo.log(nodeName);
19854         
19855         switch(nodeName.toLowerCase()) {
19856             case 'th':
19857                 switch(className) {
19858                     case 'switch':
19859                         this.showMode(1);
19860                         break;
19861                     case 'prev':
19862                     case 'next':
19863                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19864                         switch(this.viewMode){
19865                                 case 0:
19866                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19867                                         break;
19868                                 case 1:
19869                                 case 2:
19870                                         this.viewDate = this.moveYear(this.viewDate, dir);
19871                                         break;
19872                         }
19873                         this.fill();
19874                         break;
19875                     case 'today':
19876                         var date = new Date();
19877                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19878 //                        this.fill()
19879                         this.setValue(this.formatDate(this.date));
19880                         
19881                         this.hidePopup();
19882                         break;
19883                 }
19884                 break;
19885             case 'span':
19886                 if (className.indexOf('disabled') < 0) {
19887                     this.viewDate.setUTCDate(1);
19888                     if (className.indexOf('month') > -1) {
19889                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19890                     } else {
19891                         var year = parseInt(html, 10) || 0;
19892                         this.viewDate.setUTCFullYear(year);
19893                         
19894                     }
19895                     
19896                     if(this.singleMode){
19897                         this.setValue(this.formatDate(this.viewDate));
19898                         this.hidePopup();
19899                         return;
19900                     }
19901                     
19902                     this.showMode(-1);
19903                     this.fill();
19904                 }
19905                 break;
19906                 
19907             case 'td':
19908                 //Roo.log(className);
19909                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19910                     var day = parseInt(html, 10) || 1;
19911                     var year = this.viewDate.getUTCFullYear(),
19912                         month = this.viewDate.getUTCMonth();
19913
19914                     if (className.indexOf('old') > -1) {
19915                         if(month === 0 ){
19916                             month = 11;
19917                             year -= 1;
19918                         }else{
19919                             month -= 1;
19920                         }
19921                     } else if (className.indexOf('new') > -1) {
19922                         if (month == 11) {
19923                             month = 0;
19924                             year += 1;
19925                         } else {
19926                             month += 1;
19927                         }
19928                     }
19929                     //Roo.log([year,month,day]);
19930                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19931                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19932 //                    this.fill();
19933                     //Roo.log(this.formatDate(this.date));
19934                     this.setValue(this.formatDate(this.date));
19935                     this.hidePopup();
19936                 }
19937                 break;
19938         }
19939     },
19940     
19941     setStartDate: function(startDate)
19942     {
19943         this.startDate = startDate || -Infinity;
19944         if (this.startDate !== -Infinity) {
19945             this.startDate = this.parseDate(this.startDate);
19946         }
19947         this.update();
19948         this.updateNavArrows();
19949     },
19950
19951     setEndDate: function(endDate)
19952     {
19953         this.endDate = endDate || Infinity;
19954         if (this.endDate !== Infinity) {
19955             this.endDate = this.parseDate(this.endDate);
19956         }
19957         this.update();
19958         this.updateNavArrows();
19959     },
19960     
19961     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19962     {
19963         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19964         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19965             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19966         }
19967         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19968             return parseInt(d, 10);
19969         });
19970         this.update();
19971         this.updateNavArrows();
19972     },
19973     
19974     updateNavArrows: function() 
19975     {
19976         if(this.singleMode){
19977             return;
19978         }
19979         
19980         var d = new Date(this.viewDate),
19981         year = d.getUTCFullYear(),
19982         month = d.getUTCMonth();
19983         
19984         Roo.each(this.picker().select('.prev', true).elements, function(v){
19985             v.show();
19986             switch (this.viewMode) {
19987                 case 0:
19988
19989                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19990                         v.hide();
19991                     }
19992                     break;
19993                 case 1:
19994                 case 2:
19995                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19996                         v.hide();
19997                     }
19998                     break;
19999             }
20000         });
20001         
20002         Roo.each(this.picker().select('.next', true).elements, function(v){
20003             v.show();
20004             switch (this.viewMode) {
20005                 case 0:
20006
20007                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20008                         v.hide();
20009                     }
20010                     break;
20011                 case 1:
20012                 case 2:
20013                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20014                         v.hide();
20015                     }
20016                     break;
20017             }
20018         })
20019     },
20020     
20021     moveMonth: function(date, dir)
20022     {
20023         if (!dir) {
20024             return date;
20025         }
20026         var new_date = new Date(date.valueOf()),
20027         day = new_date.getUTCDate(),
20028         month = new_date.getUTCMonth(),
20029         mag = Math.abs(dir),
20030         new_month, test;
20031         dir = dir > 0 ? 1 : -1;
20032         if (mag == 1){
20033             test = dir == -1
20034             // If going back one month, make sure month is not current month
20035             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20036             ? function(){
20037                 return new_date.getUTCMonth() == month;
20038             }
20039             // If going forward one month, make sure month is as expected
20040             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20041             : function(){
20042                 return new_date.getUTCMonth() != new_month;
20043             };
20044             new_month = month + dir;
20045             new_date.setUTCMonth(new_month);
20046             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20047             if (new_month < 0 || new_month > 11) {
20048                 new_month = (new_month + 12) % 12;
20049             }
20050         } else {
20051             // For magnitudes >1, move one month at a time...
20052             for (var i=0; i<mag; i++) {
20053                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20054                 new_date = this.moveMonth(new_date, dir);
20055             }
20056             // ...then reset the day, keeping it in the new month
20057             new_month = new_date.getUTCMonth();
20058             new_date.setUTCDate(day);
20059             test = function(){
20060                 return new_month != new_date.getUTCMonth();
20061             };
20062         }
20063         // Common date-resetting loop -- if date is beyond end of month, make it
20064         // end of month
20065         while (test()){
20066             new_date.setUTCDate(--day);
20067             new_date.setUTCMonth(new_month);
20068         }
20069         return new_date;
20070     },
20071
20072     moveYear: function(date, dir)
20073     {
20074         return this.moveMonth(date, dir*12);
20075     },
20076
20077     dateWithinRange: function(date)
20078     {
20079         return date >= this.startDate && date <= this.endDate;
20080     },
20081
20082     
20083     remove: function() 
20084     {
20085         this.picker().remove();
20086     },
20087     
20088     validateValue : function(value)
20089     {
20090         if(this.getVisibilityEl().hasClass('hidden')){
20091             return true;
20092         }
20093         
20094         if(value.length < 1)  {
20095             if(this.allowBlank){
20096                 return true;
20097             }
20098             return false;
20099         }
20100         
20101         if(value.length < this.minLength){
20102             return false;
20103         }
20104         if(value.length > this.maxLength){
20105             return false;
20106         }
20107         if(this.vtype){
20108             var vt = Roo.form.VTypes;
20109             if(!vt[this.vtype](value, this)){
20110                 return false;
20111             }
20112         }
20113         if(typeof this.validator == "function"){
20114             var msg = this.validator(value);
20115             if(msg !== true){
20116                 return false;
20117             }
20118         }
20119         
20120         if(this.regex && !this.regex.test(value)){
20121             return false;
20122         }
20123         
20124         if(typeof(this.parseDate(value)) == 'undefined'){
20125             return false;
20126         }
20127         
20128         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20129             return false;
20130         }      
20131         
20132         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20133             return false;
20134         } 
20135         
20136         
20137         return true;
20138     },
20139     
20140     reset : function()
20141     {
20142         this.date = this.viewDate = '';
20143         
20144         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20145     }
20146    
20147 });
20148
20149 Roo.apply(Roo.bootstrap.DateField,  {
20150     
20151     head : {
20152         tag: 'thead',
20153         cn: [
20154         {
20155             tag: 'tr',
20156             cn: [
20157             {
20158                 tag: 'th',
20159                 cls: 'prev',
20160                 html: '<i class="fa fa-arrow-left"/>'
20161             },
20162             {
20163                 tag: 'th',
20164                 cls: 'switch',
20165                 colspan: '5'
20166             },
20167             {
20168                 tag: 'th',
20169                 cls: 'next',
20170                 html: '<i class="fa fa-arrow-right"/>'
20171             }
20172
20173             ]
20174         }
20175         ]
20176     },
20177     
20178     content : {
20179         tag: 'tbody',
20180         cn: [
20181         {
20182             tag: 'tr',
20183             cn: [
20184             {
20185                 tag: 'td',
20186                 colspan: '7'
20187             }
20188             ]
20189         }
20190         ]
20191     },
20192     
20193     footer : {
20194         tag: 'tfoot',
20195         cn: [
20196         {
20197             tag: 'tr',
20198             cn: [
20199             {
20200                 tag: 'th',
20201                 colspan: '7',
20202                 cls: 'today'
20203             }
20204                     
20205             ]
20206         }
20207         ]
20208     },
20209     
20210     dates:{
20211         en: {
20212             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20213             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20214             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20215             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20216             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20217             today: "Today"
20218         }
20219     },
20220     
20221     modes: [
20222     {
20223         clsName: 'days',
20224         navFnc: 'Month',
20225         navStep: 1
20226     },
20227     {
20228         clsName: 'months',
20229         navFnc: 'FullYear',
20230         navStep: 1
20231     },
20232     {
20233         clsName: 'years',
20234         navFnc: 'FullYear',
20235         navStep: 10
20236     }]
20237 });
20238
20239 Roo.apply(Roo.bootstrap.DateField,  {
20240   
20241     template : {
20242         tag: 'div',
20243         cls: 'datepicker dropdown-menu roo-dynamic',
20244         cn: [
20245         {
20246             tag: 'div',
20247             cls: 'datepicker-days',
20248             cn: [
20249             {
20250                 tag: 'table',
20251                 cls: 'table-condensed',
20252                 cn:[
20253                 Roo.bootstrap.DateField.head,
20254                 {
20255                     tag: 'tbody'
20256                 },
20257                 Roo.bootstrap.DateField.footer
20258                 ]
20259             }
20260             ]
20261         },
20262         {
20263             tag: 'div',
20264             cls: 'datepicker-months',
20265             cn: [
20266             {
20267                 tag: 'table',
20268                 cls: 'table-condensed',
20269                 cn:[
20270                 Roo.bootstrap.DateField.head,
20271                 Roo.bootstrap.DateField.content,
20272                 Roo.bootstrap.DateField.footer
20273                 ]
20274             }
20275             ]
20276         },
20277         {
20278             tag: 'div',
20279             cls: 'datepicker-years',
20280             cn: [
20281             {
20282                 tag: 'table',
20283                 cls: 'table-condensed',
20284                 cn:[
20285                 Roo.bootstrap.DateField.head,
20286                 Roo.bootstrap.DateField.content,
20287                 Roo.bootstrap.DateField.footer
20288                 ]
20289             }
20290             ]
20291         }
20292         ]
20293     }
20294 });
20295
20296  
20297
20298  /*
20299  * - LGPL
20300  *
20301  * TimeField
20302  * 
20303  */
20304
20305 /**
20306  * @class Roo.bootstrap.TimeField
20307  * @extends Roo.bootstrap.Input
20308  * Bootstrap DateField class
20309  * 
20310  * 
20311  * @constructor
20312  * Create a new TimeField
20313  * @param {Object} config The config object
20314  */
20315
20316 Roo.bootstrap.TimeField = function(config){
20317     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20318     this.addEvents({
20319             /**
20320              * @event show
20321              * Fires when this field show.
20322              * @param {Roo.bootstrap.DateField} thisthis
20323              * @param {Mixed} date The date value
20324              */
20325             show : true,
20326             /**
20327              * @event show
20328              * Fires when this field hide.
20329              * @param {Roo.bootstrap.DateField} this
20330              * @param {Mixed} date The date value
20331              */
20332             hide : true,
20333             /**
20334              * @event select
20335              * Fires when select a date.
20336              * @param {Roo.bootstrap.DateField} this
20337              * @param {Mixed} date The date value
20338              */
20339             select : true
20340         });
20341 };
20342
20343 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20344     
20345     /**
20346      * @cfg {String} format
20347      * The default time format string which can be overriden for localization support.  The format must be
20348      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20349      */
20350     format : "H:i",
20351        
20352     onRender: function(ct, position)
20353     {
20354         
20355         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20356                 
20357         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20358         
20359         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20360         
20361         this.pop = this.picker().select('>.datepicker-time',true).first();
20362         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20363         
20364         this.picker().on('mousedown', this.onMousedown, this);
20365         this.picker().on('click', this.onClick, this);
20366         
20367         this.picker().addClass('datepicker-dropdown');
20368     
20369         this.fillTime();
20370         this.update();
20371             
20372         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20373         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20374         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20375         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20376         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20377         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20378
20379     },
20380     
20381     fireKey: function(e){
20382         if (!this.picker().isVisible()){
20383             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20384                 this.show();
20385             }
20386             return;
20387         }
20388
20389         e.preventDefault();
20390         
20391         switch(e.keyCode){
20392             case 27: // escape
20393                 this.hide();
20394                 break;
20395             case 37: // left
20396             case 39: // right
20397                 this.onTogglePeriod();
20398                 break;
20399             case 38: // up
20400                 this.onIncrementMinutes();
20401                 break;
20402             case 40: // down
20403                 this.onDecrementMinutes();
20404                 break;
20405             case 13: // enter
20406             case 9: // tab
20407                 this.setTime();
20408                 break;
20409         }
20410     },
20411     
20412     onClick: function(e) {
20413         e.stopPropagation();
20414         e.preventDefault();
20415     },
20416     
20417     picker : function()
20418     {
20419         return this.el.select('.datepicker', true).first();
20420     },
20421     
20422     fillTime: function()
20423     {    
20424         var time = this.pop.select('tbody', true).first();
20425         
20426         time.dom.innerHTML = '';
20427         
20428         time.createChild({
20429             tag: 'tr',
20430             cn: [
20431                 {
20432                     tag: 'td',
20433                     cn: [
20434                         {
20435                             tag: 'a',
20436                             href: '#',
20437                             cls: 'btn',
20438                             cn: [
20439                                 {
20440                                     tag: 'span',
20441                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20442                                 }
20443                             ]
20444                         } 
20445                     ]
20446                 },
20447                 {
20448                     tag: 'td',
20449                     cls: 'separator'
20450                 },
20451                 {
20452                     tag: 'td',
20453                     cn: [
20454                         {
20455                             tag: 'a',
20456                             href: '#',
20457                             cls: 'btn',
20458                             cn: [
20459                                 {
20460                                     tag: 'span',
20461                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20462                                 }
20463                             ]
20464                         }
20465                     ]
20466                 },
20467                 {
20468                     tag: 'td',
20469                     cls: 'separator'
20470                 }
20471             ]
20472         });
20473         
20474         time.createChild({
20475             tag: 'tr',
20476             cn: [
20477                 {
20478                     tag: 'td',
20479                     cn: [
20480                         {
20481                             tag: 'span',
20482                             cls: 'timepicker-hour',
20483                             html: '00'
20484                         }  
20485                     ]
20486                 },
20487                 {
20488                     tag: 'td',
20489                     cls: 'separator',
20490                     html: ':'
20491                 },
20492                 {
20493                     tag: 'td',
20494                     cn: [
20495                         {
20496                             tag: 'span',
20497                             cls: 'timepicker-minute',
20498                             html: '00'
20499                         }  
20500                     ]
20501                 },
20502                 {
20503                     tag: 'td',
20504                     cls: 'separator'
20505                 },
20506                 {
20507                     tag: 'td',
20508                     cn: [
20509                         {
20510                             tag: 'button',
20511                             type: 'button',
20512                             cls: 'btn btn-primary period',
20513                             html: 'AM'
20514                             
20515                         }
20516                     ]
20517                 }
20518             ]
20519         });
20520         
20521         time.createChild({
20522             tag: 'tr',
20523             cn: [
20524                 {
20525                     tag: 'td',
20526                     cn: [
20527                         {
20528                             tag: 'a',
20529                             href: '#',
20530                             cls: 'btn',
20531                             cn: [
20532                                 {
20533                                     tag: 'span',
20534                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20535                                 }
20536                             ]
20537                         }
20538                     ]
20539                 },
20540                 {
20541                     tag: 'td',
20542                     cls: 'separator'
20543                 },
20544                 {
20545                     tag: 'td',
20546                     cn: [
20547                         {
20548                             tag: 'a',
20549                             href: '#',
20550                             cls: 'btn',
20551                             cn: [
20552                                 {
20553                                     tag: 'span',
20554                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20555                                 }
20556                             ]
20557                         }
20558                     ]
20559                 },
20560                 {
20561                     tag: 'td',
20562                     cls: 'separator'
20563                 }
20564             ]
20565         });
20566         
20567     },
20568     
20569     update: function()
20570     {
20571         
20572         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20573         
20574         this.fill();
20575     },
20576     
20577     fill: function() 
20578     {
20579         var hours = this.time.getHours();
20580         var minutes = this.time.getMinutes();
20581         var period = 'AM';
20582         
20583         if(hours > 11){
20584             period = 'PM';
20585         }
20586         
20587         if(hours == 0){
20588             hours = 12;
20589         }
20590         
20591         
20592         if(hours > 12){
20593             hours = hours - 12;
20594         }
20595         
20596         if(hours < 10){
20597             hours = '0' + hours;
20598         }
20599         
20600         if(minutes < 10){
20601             minutes = '0' + minutes;
20602         }
20603         
20604         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20605         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20606         this.pop.select('button', true).first().dom.innerHTML = period;
20607         
20608     },
20609     
20610     place: function()
20611     {   
20612         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20613         
20614         var cls = ['bottom'];
20615         
20616         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20617             cls.pop();
20618             cls.push('top');
20619         }
20620         
20621         cls.push('right');
20622         
20623         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20624             cls.pop();
20625             cls.push('left');
20626         }
20627         
20628         this.picker().addClass(cls.join('-'));
20629         
20630         var _this = this;
20631         
20632         Roo.each(cls, function(c){
20633             if(c == 'bottom'){
20634                 _this.picker().setTop(_this.inputEl().getHeight());
20635                 return;
20636             }
20637             if(c == 'top'){
20638                 _this.picker().setTop(0 - _this.picker().getHeight());
20639                 return;
20640             }
20641             
20642             if(c == 'left'){
20643                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20644                 return;
20645             }
20646             if(c == 'right'){
20647                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20648                 return;
20649             }
20650         });
20651         
20652     },
20653   
20654     onFocus : function()
20655     {
20656         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20657         this.show();
20658     },
20659     
20660     onBlur : function()
20661     {
20662         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20663         this.hide();
20664     },
20665     
20666     show : function()
20667     {
20668         this.picker().show();
20669         this.pop.show();
20670         this.update();
20671         this.place();
20672         
20673         this.fireEvent('show', this, this.date);
20674     },
20675     
20676     hide : function()
20677     {
20678         this.picker().hide();
20679         this.pop.hide();
20680         
20681         this.fireEvent('hide', this, this.date);
20682     },
20683     
20684     setTime : function()
20685     {
20686         this.hide();
20687         this.setValue(this.time.format(this.format));
20688         
20689         this.fireEvent('select', this, this.date);
20690         
20691         
20692     },
20693     
20694     onMousedown: function(e){
20695         e.stopPropagation();
20696         e.preventDefault();
20697     },
20698     
20699     onIncrementHours: function()
20700     {
20701         Roo.log('onIncrementHours');
20702         this.time = this.time.add(Date.HOUR, 1);
20703         this.update();
20704         
20705     },
20706     
20707     onDecrementHours: function()
20708     {
20709         Roo.log('onDecrementHours');
20710         this.time = this.time.add(Date.HOUR, -1);
20711         this.update();
20712     },
20713     
20714     onIncrementMinutes: function()
20715     {
20716         Roo.log('onIncrementMinutes');
20717         this.time = this.time.add(Date.MINUTE, 1);
20718         this.update();
20719     },
20720     
20721     onDecrementMinutes: function()
20722     {
20723         Roo.log('onDecrementMinutes');
20724         this.time = this.time.add(Date.MINUTE, -1);
20725         this.update();
20726     },
20727     
20728     onTogglePeriod: function()
20729     {
20730         Roo.log('onTogglePeriod');
20731         this.time = this.time.add(Date.HOUR, 12);
20732         this.update();
20733     }
20734     
20735    
20736 });
20737
20738 Roo.apply(Roo.bootstrap.TimeField,  {
20739     
20740     content : {
20741         tag: 'tbody',
20742         cn: [
20743             {
20744                 tag: 'tr',
20745                 cn: [
20746                 {
20747                     tag: 'td',
20748                     colspan: '7'
20749                 }
20750                 ]
20751             }
20752         ]
20753     },
20754     
20755     footer : {
20756         tag: 'tfoot',
20757         cn: [
20758             {
20759                 tag: 'tr',
20760                 cn: [
20761                 {
20762                     tag: 'th',
20763                     colspan: '7',
20764                     cls: '',
20765                     cn: [
20766                         {
20767                             tag: 'button',
20768                             cls: 'btn btn-info ok',
20769                             html: 'OK'
20770                         }
20771                     ]
20772                 }
20773
20774                 ]
20775             }
20776         ]
20777     }
20778 });
20779
20780 Roo.apply(Roo.bootstrap.TimeField,  {
20781   
20782     template : {
20783         tag: 'div',
20784         cls: 'datepicker dropdown-menu',
20785         cn: [
20786             {
20787                 tag: 'div',
20788                 cls: 'datepicker-time',
20789                 cn: [
20790                 {
20791                     tag: 'table',
20792                     cls: 'table-condensed',
20793                     cn:[
20794                     Roo.bootstrap.TimeField.content,
20795                     Roo.bootstrap.TimeField.footer
20796                     ]
20797                 }
20798                 ]
20799             }
20800         ]
20801     }
20802 });
20803
20804  
20805
20806  /*
20807  * - LGPL
20808  *
20809  * MonthField
20810  * 
20811  */
20812
20813 /**
20814  * @class Roo.bootstrap.MonthField
20815  * @extends Roo.bootstrap.Input
20816  * Bootstrap MonthField class
20817  * 
20818  * @cfg {String} language default en
20819  * 
20820  * @constructor
20821  * Create a new MonthField
20822  * @param {Object} config The config object
20823  */
20824
20825 Roo.bootstrap.MonthField = function(config){
20826     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20827     
20828     this.addEvents({
20829         /**
20830          * @event show
20831          * Fires when this field show.
20832          * @param {Roo.bootstrap.MonthField} this
20833          * @param {Mixed} date The date value
20834          */
20835         show : true,
20836         /**
20837          * @event show
20838          * Fires when this field hide.
20839          * @param {Roo.bootstrap.MonthField} this
20840          * @param {Mixed} date The date value
20841          */
20842         hide : true,
20843         /**
20844          * @event select
20845          * Fires when select a date.
20846          * @param {Roo.bootstrap.MonthField} this
20847          * @param {String} oldvalue The old value
20848          * @param {String} newvalue The new value
20849          */
20850         select : true
20851     });
20852 };
20853
20854 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20855     
20856     onRender: function(ct, position)
20857     {
20858         
20859         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20860         
20861         this.language = this.language || 'en';
20862         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20863         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20864         
20865         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20866         this.isInline = false;
20867         this.isInput = true;
20868         this.component = this.el.select('.add-on', true).first() || false;
20869         this.component = (this.component && this.component.length === 0) ? false : this.component;
20870         this.hasInput = this.component && this.inputEL().length;
20871         
20872         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20873         
20874         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20875         
20876         this.picker().on('mousedown', this.onMousedown, this);
20877         this.picker().on('click', this.onClick, this);
20878         
20879         this.picker().addClass('datepicker-dropdown');
20880         
20881         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20882             v.setStyle('width', '189px');
20883         });
20884         
20885         this.fillMonths();
20886         
20887         this.update();
20888         
20889         if(this.isInline) {
20890             this.show();
20891         }
20892         
20893     },
20894     
20895     setValue: function(v, suppressEvent)
20896     {   
20897         var o = this.getValue();
20898         
20899         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20900         
20901         this.update();
20902
20903         if(suppressEvent !== true){
20904             this.fireEvent('select', this, o, v);
20905         }
20906         
20907     },
20908     
20909     getValue: function()
20910     {
20911         return this.value;
20912     },
20913     
20914     onClick: function(e) 
20915     {
20916         e.stopPropagation();
20917         e.preventDefault();
20918         
20919         var target = e.getTarget();
20920         
20921         if(target.nodeName.toLowerCase() === 'i'){
20922             target = Roo.get(target).dom.parentNode;
20923         }
20924         
20925         var nodeName = target.nodeName;
20926         var className = target.className;
20927         var html = target.innerHTML;
20928         
20929         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20930             return;
20931         }
20932         
20933         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20934         
20935         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20936         
20937         this.hide();
20938                         
20939     },
20940     
20941     picker : function()
20942     {
20943         return this.pickerEl;
20944     },
20945     
20946     fillMonths: function()
20947     {    
20948         var i = 0;
20949         var months = this.picker().select('>.datepicker-months td', true).first();
20950         
20951         months.dom.innerHTML = '';
20952         
20953         while (i < 12) {
20954             var month = {
20955                 tag: 'span',
20956                 cls: 'month',
20957                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20958             };
20959             
20960             months.createChild(month);
20961         }
20962         
20963     },
20964     
20965     update: function()
20966     {
20967         var _this = this;
20968         
20969         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20970             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20971         }
20972         
20973         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20974             e.removeClass('active');
20975             
20976             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20977                 e.addClass('active');
20978             }
20979         })
20980     },
20981     
20982     place: function()
20983     {
20984         if(this.isInline) {
20985             return;
20986         }
20987         
20988         this.picker().removeClass(['bottom', 'top']);
20989         
20990         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20991             /*
20992              * place to the top of element!
20993              *
20994              */
20995             
20996             this.picker().addClass('top');
20997             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20998             
20999             return;
21000         }
21001         
21002         this.picker().addClass('bottom');
21003         
21004         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21005     },
21006     
21007     onFocus : function()
21008     {
21009         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21010         this.show();
21011     },
21012     
21013     onBlur : function()
21014     {
21015         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21016         
21017         var d = this.inputEl().getValue();
21018         
21019         this.setValue(d);
21020                 
21021         this.hide();
21022     },
21023     
21024     show : function()
21025     {
21026         this.picker().show();
21027         this.picker().select('>.datepicker-months', true).first().show();
21028         this.update();
21029         this.place();
21030         
21031         this.fireEvent('show', this, this.date);
21032     },
21033     
21034     hide : function()
21035     {
21036         if(this.isInline) {
21037             return;
21038         }
21039         this.picker().hide();
21040         this.fireEvent('hide', this, this.date);
21041         
21042     },
21043     
21044     onMousedown: function(e)
21045     {
21046         e.stopPropagation();
21047         e.preventDefault();
21048     },
21049     
21050     keyup: function(e)
21051     {
21052         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21053         this.update();
21054     },
21055
21056     fireKey: function(e)
21057     {
21058         if (!this.picker().isVisible()){
21059             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21060                 this.show();
21061             }
21062             return;
21063         }
21064         
21065         var dir;
21066         
21067         switch(e.keyCode){
21068             case 27: // escape
21069                 this.hide();
21070                 e.preventDefault();
21071                 break;
21072             case 37: // left
21073             case 39: // right
21074                 dir = e.keyCode == 37 ? -1 : 1;
21075                 
21076                 this.vIndex = this.vIndex + dir;
21077                 
21078                 if(this.vIndex < 0){
21079                     this.vIndex = 0;
21080                 }
21081                 
21082                 if(this.vIndex > 11){
21083                     this.vIndex = 11;
21084                 }
21085                 
21086                 if(isNaN(this.vIndex)){
21087                     this.vIndex = 0;
21088                 }
21089                 
21090                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21091                 
21092                 break;
21093             case 38: // up
21094             case 40: // down
21095                 
21096                 dir = e.keyCode == 38 ? -1 : 1;
21097                 
21098                 this.vIndex = this.vIndex + dir * 4;
21099                 
21100                 if(this.vIndex < 0){
21101                     this.vIndex = 0;
21102                 }
21103                 
21104                 if(this.vIndex > 11){
21105                     this.vIndex = 11;
21106                 }
21107                 
21108                 if(isNaN(this.vIndex)){
21109                     this.vIndex = 0;
21110                 }
21111                 
21112                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21113                 break;
21114                 
21115             case 13: // enter
21116                 
21117                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21118                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21119                 }
21120                 
21121                 this.hide();
21122                 e.preventDefault();
21123                 break;
21124             case 9: // tab
21125                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21126                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21127                 }
21128                 this.hide();
21129                 break;
21130             case 16: // shift
21131             case 17: // ctrl
21132             case 18: // alt
21133                 break;
21134             default :
21135                 this.hide();
21136                 
21137         }
21138     },
21139     
21140     remove: function() 
21141     {
21142         this.picker().remove();
21143     }
21144    
21145 });
21146
21147 Roo.apply(Roo.bootstrap.MonthField,  {
21148     
21149     content : {
21150         tag: 'tbody',
21151         cn: [
21152         {
21153             tag: 'tr',
21154             cn: [
21155             {
21156                 tag: 'td',
21157                 colspan: '7'
21158             }
21159             ]
21160         }
21161         ]
21162     },
21163     
21164     dates:{
21165         en: {
21166             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21167             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21168         }
21169     }
21170 });
21171
21172 Roo.apply(Roo.bootstrap.MonthField,  {
21173   
21174     template : {
21175         tag: 'div',
21176         cls: 'datepicker dropdown-menu roo-dynamic',
21177         cn: [
21178             {
21179                 tag: 'div',
21180                 cls: 'datepicker-months',
21181                 cn: [
21182                 {
21183                     tag: 'table',
21184                     cls: 'table-condensed',
21185                     cn:[
21186                         Roo.bootstrap.DateField.content
21187                     ]
21188                 }
21189                 ]
21190             }
21191         ]
21192     }
21193 });
21194
21195  
21196
21197  
21198  /*
21199  * - LGPL
21200  *
21201  * CheckBox
21202  * 
21203  */
21204
21205 /**
21206  * @class Roo.bootstrap.CheckBox
21207  * @extends Roo.bootstrap.Input
21208  * Bootstrap CheckBox class
21209  * 
21210  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21211  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21212  * @cfg {String} boxLabel The text that appears beside the checkbox
21213  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21214  * @cfg {Boolean} checked initnal the element
21215  * @cfg {Boolean} inline inline the element (default false)
21216  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21217  * @cfg {String} tooltip label tooltip
21218  * 
21219  * @constructor
21220  * Create a new CheckBox
21221  * @param {Object} config The config object
21222  */
21223
21224 Roo.bootstrap.CheckBox = function(config){
21225     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21226    
21227     this.addEvents({
21228         /**
21229         * @event check
21230         * Fires when the element is checked or unchecked.
21231         * @param {Roo.bootstrap.CheckBox} this This input
21232         * @param {Boolean} checked The new checked value
21233         */
21234        check : true,
21235        /**
21236         * @event click
21237         * Fires when the element is click.
21238         * @param {Roo.bootstrap.CheckBox} this This input
21239         */
21240        click : true
21241     });
21242     
21243 };
21244
21245 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21246   
21247     inputType: 'checkbox',
21248     inputValue: 1,
21249     valueOff: 0,
21250     boxLabel: false,
21251     checked: false,
21252     weight : false,
21253     inline: false,
21254     tooltip : '',
21255     
21256     // checkbox success does not make any sense really.. 
21257     invalidClass : "",
21258     validClass : "",
21259     
21260     
21261     getAutoCreate : function()
21262     {
21263         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21264         
21265         var id = Roo.id();
21266         
21267         var cfg = {};
21268         
21269         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21270         
21271         if(this.inline){
21272             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21273         }
21274         
21275         var input =  {
21276             tag: 'input',
21277             id : id,
21278             type : this.inputType,
21279             value : this.inputValue,
21280             cls : 'roo-' + this.inputType, //'form-box',
21281             placeholder : this.placeholder || ''
21282             
21283         };
21284         
21285         if(this.inputType != 'radio'){
21286             var hidden =  {
21287                 tag: 'input',
21288                 type : 'hidden',
21289                 cls : 'roo-hidden-value',
21290                 value : this.checked ? this.inputValue : this.valueOff
21291             };
21292         }
21293         
21294             
21295         if (this.weight) { // Validity check?
21296             cfg.cls += " " + this.inputType + "-" + this.weight;
21297         }
21298         
21299         if (this.disabled) {
21300             input.disabled=true;
21301         }
21302         
21303         if(this.checked){
21304             input.checked = this.checked;
21305         }
21306         
21307         if (this.name) {
21308             
21309             input.name = this.name;
21310             
21311             if(this.inputType != 'radio'){
21312                 hidden.name = this.name;
21313                 input.name = '_hidden_' + this.name;
21314             }
21315         }
21316         
21317         if (this.size) {
21318             input.cls += ' input-' + this.size;
21319         }
21320         
21321         var settings=this;
21322         
21323         ['xs','sm','md','lg'].map(function(size){
21324             if (settings[size]) {
21325                 cfg.cls += ' col-' + size + '-' + settings[size];
21326             }
21327         });
21328         
21329         var inputblock = input;
21330          
21331         if (this.before || this.after) {
21332             
21333             inputblock = {
21334                 cls : 'input-group',
21335                 cn :  [] 
21336             };
21337             
21338             if (this.before) {
21339                 inputblock.cn.push({
21340                     tag :'span',
21341                     cls : 'input-group-addon',
21342                     html : this.before
21343                 });
21344             }
21345             
21346             inputblock.cn.push(input);
21347             
21348             if(this.inputType != 'radio'){
21349                 inputblock.cn.push(hidden);
21350             }
21351             
21352             if (this.after) {
21353                 inputblock.cn.push({
21354                     tag :'span',
21355                     cls : 'input-group-addon',
21356                     html : this.after
21357                 });
21358             }
21359             
21360         }
21361         var boxLabelCfg = false;
21362         
21363         if(this.boxLabel){
21364            
21365             boxLabelCfg = {
21366                 tag: 'label',
21367                 //'for': id, // box label is handled by onclick - so no for...
21368                 cls: 'box-label',
21369                 html: this.boxLabel
21370             };
21371             if(this.tooltip){
21372                 boxLabelCfg.tooltip = this.tooltip;
21373             }
21374              
21375         }
21376         
21377         
21378         if (align ==='left' && this.fieldLabel.length) {
21379 //                Roo.log("left and has label");
21380             cfg.cn = [
21381                 {
21382                     tag: 'label',
21383                     'for' :  id,
21384                     cls : 'control-label',
21385                     html : this.fieldLabel
21386                 },
21387                 {
21388                     cls : "", 
21389                     cn: [
21390                         inputblock
21391                     ]
21392                 }
21393             ];
21394             
21395             if (boxLabelCfg) {
21396                 cfg.cn[1].cn.push(boxLabelCfg);
21397             }
21398             
21399             if(this.labelWidth > 12){
21400                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21401             }
21402             
21403             if(this.labelWidth < 13 && this.labelmd == 0){
21404                 this.labelmd = this.labelWidth;
21405             }
21406             
21407             if(this.labellg > 0){
21408                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21409                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21410             }
21411             
21412             if(this.labelmd > 0){
21413                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21414                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21415             }
21416             
21417             if(this.labelsm > 0){
21418                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21419                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21420             }
21421             
21422             if(this.labelxs > 0){
21423                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21424                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21425             }
21426             
21427         } else if ( this.fieldLabel.length) {
21428 //                Roo.log(" label");
21429                 cfg.cn = [
21430                    
21431                     {
21432                         tag: this.boxLabel ? 'span' : 'label',
21433                         'for': id,
21434                         cls: 'control-label box-input-label',
21435                         //cls : 'input-group-addon',
21436                         html : this.fieldLabel
21437                     },
21438                     
21439                     inputblock
21440                     
21441                 ];
21442                 if (boxLabelCfg) {
21443                     cfg.cn.push(boxLabelCfg);
21444                 }
21445
21446         } else {
21447             
21448 //                Roo.log(" no label && no align");
21449                 cfg.cn = [  inputblock ] ;
21450                 if (boxLabelCfg) {
21451                     cfg.cn.push(boxLabelCfg);
21452                 }
21453
21454                 
21455         }
21456         
21457        
21458         
21459         if(this.inputType != 'radio'){
21460             cfg.cn.push(hidden);
21461         }
21462         
21463         return cfg;
21464         
21465     },
21466     
21467     /**
21468      * return the real input element.
21469      */
21470     inputEl: function ()
21471     {
21472         return this.el.select('input.roo-' + this.inputType,true).first();
21473     },
21474     hiddenEl: function ()
21475     {
21476         return this.el.select('input.roo-hidden-value',true).first();
21477     },
21478     
21479     labelEl: function()
21480     {
21481         return this.el.select('label.control-label',true).first();
21482     },
21483     /* depricated... */
21484     
21485     label: function()
21486     {
21487         return this.labelEl();
21488     },
21489     
21490     boxLabelEl: function()
21491     {
21492         return this.el.select('label.box-label',true).first();
21493     },
21494     
21495     initEvents : function()
21496     {
21497 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21498         
21499         this.inputEl().on('click', this.onClick,  this);
21500         
21501         if (this.boxLabel) { 
21502             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21503         }
21504         
21505         this.startValue = this.getValue();
21506         
21507         if(this.groupId){
21508             Roo.bootstrap.CheckBox.register(this);
21509         }
21510     },
21511     
21512     onClick : function(e)
21513     {   
21514         if(this.fireEvent('click', this, e) !== false){
21515             this.setChecked(!this.checked);
21516         }
21517         
21518     },
21519     
21520     setChecked : function(state,suppressEvent)
21521     {
21522         this.startValue = this.getValue();
21523
21524         if(this.inputType == 'radio'){
21525             
21526             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21527                 e.dom.checked = false;
21528             });
21529             
21530             this.inputEl().dom.checked = true;
21531             
21532             this.inputEl().dom.value = this.inputValue;
21533             
21534             if(suppressEvent !== true){
21535                 this.fireEvent('check', this, true);
21536             }
21537             
21538             this.validate();
21539             
21540             return;
21541         }
21542         
21543         this.checked = state;
21544         
21545         this.inputEl().dom.checked = state;
21546         
21547         
21548         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21549         
21550         if(suppressEvent !== true){
21551             this.fireEvent('check', this, state);
21552         }
21553         
21554         this.validate();
21555     },
21556     
21557     getValue : function()
21558     {
21559         if(this.inputType == 'radio'){
21560             return this.getGroupValue();
21561         }
21562         
21563         return this.hiddenEl().dom.value;
21564         
21565     },
21566     
21567     getGroupValue : function()
21568     {
21569         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21570             return '';
21571         }
21572         
21573         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21574     },
21575     
21576     setValue : function(v,suppressEvent)
21577     {
21578         if(this.inputType == 'radio'){
21579             this.setGroupValue(v, suppressEvent);
21580             return;
21581         }
21582         
21583         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21584         
21585         this.validate();
21586     },
21587     
21588     setGroupValue : function(v, suppressEvent)
21589     {
21590         this.startValue = this.getValue();
21591         
21592         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21593             e.dom.checked = false;
21594             
21595             if(e.dom.value == v){
21596                 e.dom.checked = true;
21597             }
21598         });
21599         
21600         if(suppressEvent !== true){
21601             this.fireEvent('check', this, true);
21602         }
21603
21604         this.validate();
21605         
21606         return;
21607     },
21608     
21609     validate : function()
21610     {
21611         if(this.getVisibilityEl().hasClass('hidden')){
21612             return true;
21613         }
21614         
21615         if(
21616                 this.disabled || 
21617                 (this.inputType == 'radio' && this.validateRadio()) ||
21618                 (this.inputType == 'checkbox' && this.validateCheckbox())
21619         ){
21620             this.markValid();
21621             return true;
21622         }
21623         
21624         this.markInvalid();
21625         return false;
21626     },
21627     
21628     validateRadio : function()
21629     {
21630         if(this.getVisibilityEl().hasClass('hidden')){
21631             return true;
21632         }
21633         
21634         if(this.allowBlank){
21635             return true;
21636         }
21637         
21638         var valid = false;
21639         
21640         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21641             if(!e.dom.checked){
21642                 return;
21643             }
21644             
21645             valid = true;
21646             
21647             return false;
21648         });
21649         
21650         return valid;
21651     },
21652     
21653     validateCheckbox : function()
21654     {
21655         if(!this.groupId){
21656             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21657             //return (this.getValue() == this.inputValue) ? true : false;
21658         }
21659         
21660         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21661         
21662         if(!group){
21663             return false;
21664         }
21665         
21666         var r = false;
21667         
21668         for(var i in group){
21669             if(group[i].el.isVisible(true)){
21670                 r = false;
21671                 break;
21672             }
21673             
21674             r = true;
21675         }
21676         
21677         for(var i in group){
21678             if(r){
21679                 break;
21680             }
21681             
21682             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21683         }
21684         
21685         return r;
21686     },
21687     
21688     /**
21689      * Mark this field as valid
21690      */
21691     markValid : function()
21692     {
21693         var _this = this;
21694         
21695         this.fireEvent('valid', this);
21696         
21697         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21698         
21699         if(this.groupId){
21700             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21701         }
21702         
21703         if(label){
21704             label.markValid();
21705         }
21706
21707         if(this.inputType == 'radio'){
21708             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21709                 var fg = e.findParent('.form-group', false, true);
21710                 if (Roo.bootstrap.version == 3) {
21711                     fg.removeClass([_this.invalidClass, _this.validClass]);
21712                     fg.addClass(_this.validClass);
21713                 } else {
21714                     fg.removeClass(['is-valid', 'is-invalid']);
21715                     fg.addClass('is-valid');
21716                 }
21717             });
21718             
21719             return;
21720         }
21721
21722         if(!this.groupId){
21723             var fg = this.el.findParent('.form-group', false, true);
21724             if (Roo.bootstrap.version == 3) {
21725                 fg.removeClass([this.invalidClass, this.validClass]);
21726                 fg.addClass(this.validClass);
21727             } else {
21728                 fg.removeClass(['is-valid', 'is-invalid']);
21729                 fg.addClass('is-valid');
21730             }
21731             return;
21732         }
21733         
21734         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21735         
21736         if(!group){
21737             return;
21738         }
21739         
21740         for(var i in group){
21741             var fg = group[i].el.findParent('.form-group', false, true);
21742             if (Roo.bootstrap.version == 3) {
21743                 fg.removeClass([this.invalidClass, this.validClass]);
21744                 fg.addClass(this.validClass);
21745             } else {
21746                 fg.removeClass(['is-valid', 'is-invalid']);
21747                 fg.addClass('is-valid');
21748             }
21749         }
21750     },
21751     
21752      /**
21753      * Mark this field as invalid
21754      * @param {String} msg The validation message
21755      */
21756     markInvalid : function(msg)
21757     {
21758         if(this.allowBlank){
21759             return;
21760         }
21761         
21762         var _this = this;
21763         
21764         this.fireEvent('invalid', this, msg);
21765         
21766         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21767         
21768         if(this.groupId){
21769             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21770         }
21771         
21772         if(label){
21773             label.markInvalid();
21774         }
21775             
21776         if(this.inputType == 'radio'){
21777             
21778             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21779                 var fg = e.findParent('.form-group', false, true);
21780                 if (Roo.bootstrap.version == 3) {
21781                     fg.removeClass([_this.invalidClass, _this.validClass]);
21782                     fg.addClass(_this.invalidClass);
21783                 } else {
21784                     fg.removeClass(['is-invalid', 'is-valid']);
21785                     fg.addClass('is-invalid');
21786                 }
21787             });
21788             
21789             return;
21790         }
21791         
21792         if(!this.groupId){
21793             var fg = this.el.findParent('.form-group', false, true);
21794             if (Roo.bootstrap.version == 3) {
21795                 fg.removeClass([_this.invalidClass, _this.validClass]);
21796                 fg.addClass(_this.invalidClass);
21797             } else {
21798                 fg.removeClass(['is-invalid', 'is-valid']);
21799                 fg.addClass('is-invalid');
21800             }
21801             return;
21802         }
21803         
21804         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21805         
21806         if(!group){
21807             return;
21808         }
21809         
21810         for(var i in group){
21811             var fg = group[i].el.findParent('.form-group', false, true);
21812             if (Roo.bootstrap.version == 3) {
21813                 fg.removeClass([_this.invalidClass, _this.validClass]);
21814                 fg.addClass(_this.invalidClass);
21815             } else {
21816                 fg.removeClass(['is-invalid', 'is-valid']);
21817                 fg.addClass('is-invalid');
21818             }
21819         }
21820         
21821     },
21822     
21823     clearInvalid : function()
21824     {
21825         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21826         
21827         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21828         
21829         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21830         
21831         if (label && label.iconEl) {
21832             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21833             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21834         }
21835     },
21836     
21837     disable : function()
21838     {
21839         if(this.inputType != 'radio'){
21840             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21841             return;
21842         }
21843         
21844         var _this = this;
21845         
21846         if(this.rendered){
21847             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21848                 _this.getActionEl().addClass(this.disabledClass);
21849                 e.dom.disabled = true;
21850             });
21851         }
21852         
21853         this.disabled = true;
21854         this.fireEvent("disable", this);
21855         return this;
21856     },
21857
21858     enable : function()
21859     {
21860         if(this.inputType != 'radio'){
21861             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21862             return;
21863         }
21864         
21865         var _this = this;
21866         
21867         if(this.rendered){
21868             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21869                 _this.getActionEl().removeClass(this.disabledClass);
21870                 e.dom.disabled = false;
21871             });
21872         }
21873         
21874         this.disabled = false;
21875         this.fireEvent("enable", this);
21876         return this;
21877     },
21878     
21879     setBoxLabel : function(v)
21880     {
21881         this.boxLabel = v;
21882         
21883         if(this.rendered){
21884             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21885         }
21886     }
21887
21888 });
21889
21890 Roo.apply(Roo.bootstrap.CheckBox, {
21891     
21892     groups: {},
21893     
21894      /**
21895     * register a CheckBox Group
21896     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21897     */
21898     register : function(checkbox)
21899     {
21900         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21901             this.groups[checkbox.groupId] = {};
21902         }
21903         
21904         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21905             return;
21906         }
21907         
21908         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21909         
21910     },
21911     /**
21912     * fetch a CheckBox Group based on the group ID
21913     * @param {string} the group ID
21914     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21915     */
21916     get: function(groupId) {
21917         if (typeof(this.groups[groupId]) == 'undefined') {
21918             return false;
21919         }
21920         
21921         return this.groups[groupId] ;
21922     }
21923     
21924     
21925 });
21926 /*
21927  * - LGPL
21928  *
21929  * RadioItem
21930  * 
21931  */
21932
21933 /**
21934  * @class Roo.bootstrap.Radio
21935  * @extends Roo.bootstrap.Component
21936  * Bootstrap Radio class
21937  * @cfg {String} boxLabel - the label associated
21938  * @cfg {String} value - the value of radio
21939  * 
21940  * @constructor
21941  * Create a new Radio
21942  * @param {Object} config The config object
21943  */
21944 Roo.bootstrap.Radio = function(config){
21945     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21946     
21947 };
21948
21949 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21950     
21951     boxLabel : '',
21952     
21953     value : '',
21954     
21955     getAutoCreate : function()
21956     {
21957         var cfg = {
21958             tag : 'div',
21959             cls : 'form-group radio',
21960             cn : [
21961                 {
21962                     tag : 'label',
21963                     cls : 'box-label',
21964                     html : this.boxLabel
21965                 }
21966             ]
21967         };
21968         
21969         return cfg;
21970     },
21971     
21972     initEvents : function() 
21973     {
21974         this.parent().register(this);
21975         
21976         this.el.on('click', this.onClick, this);
21977         
21978     },
21979     
21980     onClick : function(e)
21981     {
21982         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21983             this.setChecked(true);
21984         }
21985     },
21986     
21987     setChecked : function(state, suppressEvent)
21988     {
21989         this.parent().setValue(this.value, suppressEvent);
21990         
21991     },
21992     
21993     setBoxLabel : function(v)
21994     {
21995         this.boxLabel = v;
21996         
21997         if(this.rendered){
21998             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21999         }
22000     }
22001     
22002 });
22003  
22004
22005  /*
22006  * - LGPL
22007  *
22008  * Input
22009  * 
22010  */
22011
22012 /**
22013  * @class Roo.bootstrap.SecurePass
22014  * @extends Roo.bootstrap.Input
22015  * Bootstrap SecurePass class
22016  *
22017  * 
22018  * @constructor
22019  * Create a new SecurePass
22020  * @param {Object} config The config object
22021  */
22022  
22023 Roo.bootstrap.SecurePass = function (config) {
22024     // these go here, so the translation tool can replace them..
22025     this.errors = {
22026         PwdEmpty: "Please type a password, and then retype it to confirm.",
22027         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22028         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22029         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22030         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22031         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22032         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22033         TooWeak: "Your password is Too Weak."
22034     },
22035     this.meterLabel = "Password strength:";
22036     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22037     this.meterClass = [
22038         "roo-password-meter-tooweak", 
22039         "roo-password-meter-weak", 
22040         "roo-password-meter-medium", 
22041         "roo-password-meter-strong", 
22042         "roo-password-meter-grey"
22043     ];
22044     
22045     this.errors = {};
22046     
22047     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22048 }
22049
22050 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22051     /**
22052      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22053      * {
22054      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22055      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22056      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22057      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22058      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22059      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22060      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22061      * })
22062      */
22063     // private
22064     
22065     meterWidth: 300,
22066     errorMsg :'',    
22067     errors: false,
22068     imageRoot: '/',
22069     /**
22070      * @cfg {String/Object} Label for the strength meter (defaults to
22071      * 'Password strength:')
22072      */
22073     // private
22074     meterLabel: '',
22075     /**
22076      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22077      * ['Weak', 'Medium', 'Strong'])
22078      */
22079     // private    
22080     pwdStrengths: false,    
22081     // private
22082     strength: 0,
22083     // private
22084     _lastPwd: null,
22085     // private
22086     kCapitalLetter: 0,
22087     kSmallLetter: 1,
22088     kDigit: 2,
22089     kPunctuation: 3,
22090     
22091     insecure: false,
22092     // private
22093     initEvents: function ()
22094     {
22095         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22096
22097         if (this.el.is('input[type=password]') && Roo.isSafari) {
22098             this.el.on('keydown', this.SafariOnKeyDown, this);
22099         }
22100
22101         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22102     },
22103     // private
22104     onRender: function (ct, position)
22105     {
22106         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22107         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22108         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22109
22110         this.trigger.createChild({
22111                    cn: [
22112                     {
22113                     //id: 'PwdMeter',
22114                     tag: 'div',
22115                     cls: 'roo-password-meter-grey col-xs-12',
22116                     style: {
22117                         //width: 0,
22118                         //width: this.meterWidth + 'px'                                                
22119                         }
22120                     },
22121                     {                            
22122                          cls: 'roo-password-meter-text'                          
22123                     }
22124                 ]            
22125         });
22126
22127          
22128         if (this.hideTrigger) {
22129             this.trigger.setDisplayed(false);
22130         }
22131         this.setSize(this.width || '', this.height || '');
22132     },
22133     // private
22134     onDestroy: function ()
22135     {
22136         if (this.trigger) {
22137             this.trigger.removeAllListeners();
22138             this.trigger.remove();
22139         }
22140         if (this.wrap) {
22141             this.wrap.remove();
22142         }
22143         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22144     },
22145     // private
22146     checkStrength: function ()
22147     {
22148         var pwd = this.inputEl().getValue();
22149         if (pwd == this._lastPwd) {
22150             return;
22151         }
22152
22153         var strength;
22154         if (this.ClientSideStrongPassword(pwd)) {
22155             strength = 3;
22156         } else if (this.ClientSideMediumPassword(pwd)) {
22157             strength = 2;
22158         } else if (this.ClientSideWeakPassword(pwd)) {
22159             strength = 1;
22160         } else {
22161             strength = 0;
22162         }
22163         
22164         Roo.log('strength1: ' + strength);
22165         
22166         //var pm = this.trigger.child('div/div/div').dom;
22167         var pm = this.trigger.child('div/div');
22168         pm.removeClass(this.meterClass);
22169         pm.addClass(this.meterClass[strength]);
22170                 
22171         
22172         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22173                 
22174         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22175         
22176         this._lastPwd = pwd;
22177     },
22178     reset: function ()
22179     {
22180         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22181         
22182         this._lastPwd = '';
22183         
22184         var pm = this.trigger.child('div/div');
22185         pm.removeClass(this.meterClass);
22186         pm.addClass('roo-password-meter-grey');        
22187         
22188         
22189         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22190         
22191         pt.innerHTML = '';
22192         this.inputEl().dom.type='password';
22193     },
22194     // private
22195     validateValue: function (value)
22196     {
22197         
22198         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22199             return false;
22200         }
22201         if (value.length == 0) {
22202             if (this.allowBlank) {
22203                 this.clearInvalid();
22204                 return true;
22205             }
22206
22207             this.markInvalid(this.errors.PwdEmpty);
22208             this.errorMsg = this.errors.PwdEmpty;
22209             return false;
22210         }
22211         
22212         if(this.insecure){
22213             return true;
22214         }
22215         
22216         if ('[\x21-\x7e]*'.match(value)) {
22217             this.markInvalid(this.errors.PwdBadChar);
22218             this.errorMsg = this.errors.PwdBadChar;
22219             return false;
22220         }
22221         if (value.length < 6) {
22222             this.markInvalid(this.errors.PwdShort);
22223             this.errorMsg = this.errors.PwdShort;
22224             return false;
22225         }
22226         if (value.length > 16) {
22227             this.markInvalid(this.errors.PwdLong);
22228             this.errorMsg = this.errors.PwdLong;
22229             return false;
22230         }
22231         var strength;
22232         if (this.ClientSideStrongPassword(value)) {
22233             strength = 3;
22234         } else if (this.ClientSideMediumPassword(value)) {
22235             strength = 2;
22236         } else if (this.ClientSideWeakPassword(value)) {
22237             strength = 1;
22238         } else {
22239             strength = 0;
22240         }
22241
22242         
22243         if (strength < 2) {
22244             //this.markInvalid(this.errors.TooWeak);
22245             this.errorMsg = this.errors.TooWeak;
22246             //return false;
22247         }
22248         
22249         
22250         console.log('strength2: ' + strength);
22251         
22252         //var pm = this.trigger.child('div/div/div').dom;
22253         
22254         var pm = this.trigger.child('div/div');
22255         pm.removeClass(this.meterClass);
22256         pm.addClass(this.meterClass[strength]);
22257                 
22258         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22259                 
22260         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22261         
22262         this.errorMsg = ''; 
22263         return true;
22264     },
22265     // private
22266     CharacterSetChecks: function (type)
22267     {
22268         this.type = type;
22269         this.fResult = false;
22270     },
22271     // private
22272     isctype: function (character, type)
22273     {
22274         switch (type) {  
22275             case this.kCapitalLetter:
22276                 if (character >= 'A' && character <= 'Z') {
22277                     return true;
22278                 }
22279                 break;
22280             
22281             case this.kSmallLetter:
22282                 if (character >= 'a' && character <= 'z') {
22283                     return true;
22284                 }
22285                 break;
22286             
22287             case this.kDigit:
22288                 if (character >= '0' && character <= '9') {
22289                     return true;
22290                 }
22291                 break;
22292             
22293             case this.kPunctuation:
22294                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22295                     return true;
22296                 }
22297                 break;
22298             
22299             default:
22300                 return false;
22301         }
22302
22303     },
22304     // private
22305     IsLongEnough: function (pwd, size)
22306     {
22307         return !(pwd == null || isNaN(size) || pwd.length < size);
22308     },
22309     // private
22310     SpansEnoughCharacterSets: function (word, nb)
22311     {
22312         if (!this.IsLongEnough(word, nb))
22313         {
22314             return false;
22315         }
22316
22317         var characterSetChecks = new Array(
22318             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22319             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22320         );
22321         
22322         for (var index = 0; index < word.length; ++index) {
22323             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22324                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22325                     characterSetChecks[nCharSet].fResult = true;
22326                     break;
22327                 }
22328             }
22329         }
22330
22331         var nCharSets = 0;
22332         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22333             if (characterSetChecks[nCharSet].fResult) {
22334                 ++nCharSets;
22335             }
22336         }
22337
22338         if (nCharSets < nb) {
22339             return false;
22340         }
22341         return true;
22342     },
22343     // private
22344     ClientSideStrongPassword: function (pwd)
22345     {
22346         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22347     },
22348     // private
22349     ClientSideMediumPassword: function (pwd)
22350     {
22351         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22352     },
22353     // private
22354     ClientSideWeakPassword: function (pwd)
22355     {
22356         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22357     }
22358           
22359 })//<script type="text/javascript">
22360
22361 /*
22362  * Based  Ext JS Library 1.1.1
22363  * Copyright(c) 2006-2007, Ext JS, LLC.
22364  * LGPL
22365  *
22366  */
22367  
22368 /**
22369  * @class Roo.HtmlEditorCore
22370  * @extends Roo.Component
22371  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22372  *
22373  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22374  */
22375
22376 Roo.HtmlEditorCore = function(config){
22377     
22378     
22379     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22380     
22381     
22382     this.addEvents({
22383         /**
22384          * @event initialize
22385          * Fires when the editor is fully initialized (including the iframe)
22386          * @param {Roo.HtmlEditorCore} this
22387          */
22388         initialize: true,
22389         /**
22390          * @event activate
22391          * Fires when the editor is first receives the focus. Any insertion must wait
22392          * until after this event.
22393          * @param {Roo.HtmlEditorCore} this
22394          */
22395         activate: true,
22396          /**
22397          * @event beforesync
22398          * Fires before the textarea is updated with content from the editor iframe. Return false
22399          * to cancel the sync.
22400          * @param {Roo.HtmlEditorCore} this
22401          * @param {String} html
22402          */
22403         beforesync: true,
22404          /**
22405          * @event beforepush
22406          * Fires before the iframe editor is updated with content from the textarea. Return false
22407          * to cancel the push.
22408          * @param {Roo.HtmlEditorCore} this
22409          * @param {String} html
22410          */
22411         beforepush: true,
22412          /**
22413          * @event sync
22414          * Fires when the textarea is updated with content from the editor iframe.
22415          * @param {Roo.HtmlEditorCore} this
22416          * @param {String} html
22417          */
22418         sync: true,
22419          /**
22420          * @event push
22421          * Fires when the iframe editor is updated with content from the textarea.
22422          * @param {Roo.HtmlEditorCore} this
22423          * @param {String} html
22424          */
22425         push: true,
22426         
22427         /**
22428          * @event editorevent
22429          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22430          * @param {Roo.HtmlEditorCore} this
22431          */
22432         editorevent: true
22433         
22434     });
22435     
22436     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22437     
22438     // defaults : white / black...
22439     this.applyBlacklists();
22440     
22441     
22442     
22443 };
22444
22445
22446 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22447
22448
22449      /**
22450      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22451      */
22452     
22453     owner : false,
22454     
22455      /**
22456      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22457      *                        Roo.resizable.
22458      */
22459     resizable : false,
22460      /**
22461      * @cfg {Number} height (in pixels)
22462      */   
22463     height: 300,
22464    /**
22465      * @cfg {Number} width (in pixels)
22466      */   
22467     width: 500,
22468     
22469     /**
22470      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22471      * 
22472      */
22473     stylesheets: false,
22474     
22475     // id of frame..
22476     frameId: false,
22477     
22478     // private properties
22479     validationEvent : false,
22480     deferHeight: true,
22481     initialized : false,
22482     activated : false,
22483     sourceEditMode : false,
22484     onFocus : Roo.emptyFn,
22485     iframePad:3,
22486     hideMode:'offsets',
22487     
22488     clearUp: true,
22489     
22490     // blacklist + whitelisted elements..
22491     black: false,
22492     white: false,
22493      
22494     bodyCls : '',
22495
22496     /**
22497      * Protected method that will not generally be called directly. It
22498      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22499      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22500      */
22501     getDocMarkup : function(){
22502         // body styles..
22503         var st = '';
22504         
22505         // inherit styels from page...?? 
22506         if (this.stylesheets === false) {
22507             
22508             Roo.get(document.head).select('style').each(function(node) {
22509                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22510             });
22511             
22512             Roo.get(document.head).select('link').each(function(node) { 
22513                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22514             });
22515             
22516         } else if (!this.stylesheets.length) {
22517                 // simple..
22518                 st = '<style type="text/css">' +
22519                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22520                    '</style>';
22521         } else { 
22522             st = '<style type="text/css">' +
22523                     this.stylesheets +
22524                 '</style>';
22525         }
22526         
22527         st +=  '<style type="text/css">' +
22528             'IMG { cursor: pointer } ' +
22529         '</style>';
22530
22531         var cls = 'roo-htmleditor-body';
22532         
22533         if(this.bodyCls.length){
22534             cls += ' ' + this.bodyCls;
22535         }
22536         
22537         return '<html><head>' + st  +
22538             //<style type="text/css">' +
22539             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22540             //'</style>' +
22541             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
22542     },
22543
22544     // private
22545     onRender : function(ct, position)
22546     {
22547         var _t = this;
22548         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22549         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22550         
22551         
22552         this.el.dom.style.border = '0 none';
22553         this.el.dom.setAttribute('tabIndex', -1);
22554         this.el.addClass('x-hidden hide');
22555         
22556         
22557         
22558         if(Roo.isIE){ // fix IE 1px bogus margin
22559             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22560         }
22561        
22562         
22563         this.frameId = Roo.id();
22564         
22565          
22566         
22567         var iframe = this.owner.wrap.createChild({
22568             tag: 'iframe',
22569             cls: 'form-control', // bootstrap..
22570             id: this.frameId,
22571             name: this.frameId,
22572             frameBorder : 'no',
22573             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22574         }, this.el
22575         );
22576         
22577         
22578         this.iframe = iframe.dom;
22579
22580          this.assignDocWin();
22581         
22582         this.doc.designMode = 'on';
22583        
22584         this.doc.open();
22585         this.doc.write(this.getDocMarkup());
22586         this.doc.close();
22587
22588         
22589         var task = { // must defer to wait for browser to be ready
22590             run : function(){
22591                 //console.log("run task?" + this.doc.readyState);
22592                 this.assignDocWin();
22593                 if(this.doc.body || this.doc.readyState == 'complete'){
22594                     try {
22595                         this.doc.designMode="on";
22596                     } catch (e) {
22597                         return;
22598                     }
22599                     Roo.TaskMgr.stop(task);
22600                     this.initEditor.defer(10, this);
22601                 }
22602             },
22603             interval : 10,
22604             duration: 10000,
22605             scope: this
22606         };
22607         Roo.TaskMgr.start(task);
22608
22609     },
22610
22611     // private
22612     onResize : function(w, h)
22613     {
22614          Roo.log('resize: ' +w + ',' + h );
22615         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22616         if(!this.iframe){
22617             return;
22618         }
22619         if(typeof w == 'number'){
22620             
22621             this.iframe.style.width = w + 'px';
22622         }
22623         if(typeof h == 'number'){
22624             
22625             this.iframe.style.height = h + 'px';
22626             if(this.doc){
22627                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22628             }
22629         }
22630         
22631     },
22632
22633     /**
22634      * Toggles the editor between standard and source edit mode.
22635      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22636      */
22637     toggleSourceEdit : function(sourceEditMode){
22638         
22639         this.sourceEditMode = sourceEditMode === true;
22640         
22641         if(this.sourceEditMode){
22642  
22643             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22644             
22645         }else{
22646             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22647             //this.iframe.className = '';
22648             this.deferFocus();
22649         }
22650         //this.setSize(this.owner.wrap.getSize());
22651         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22652     },
22653
22654     
22655   
22656
22657     /**
22658      * Protected method that will not generally be called directly. If you need/want
22659      * custom HTML cleanup, this is the method you should override.
22660      * @param {String} html The HTML to be cleaned
22661      * return {String} The cleaned HTML
22662      */
22663     cleanHtml : function(html){
22664         html = String(html);
22665         if(html.length > 5){
22666             if(Roo.isSafari){ // strip safari nonsense
22667                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22668             }
22669         }
22670         if(html == '&nbsp;'){
22671             html = '';
22672         }
22673         return html;
22674     },
22675
22676     /**
22677      * HTML Editor -> Textarea
22678      * Protected method that will not generally be called directly. Syncs the contents
22679      * of the editor iframe with the textarea.
22680      */
22681     syncValue : function(){
22682         if(this.initialized){
22683             var bd = (this.doc.body || this.doc.documentElement);
22684             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22685             var html = bd.innerHTML;
22686             if(Roo.isSafari){
22687                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22688                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22689                 if(m && m[1]){
22690                     html = '<div style="'+m[0]+'">' + html + '</div>';
22691                 }
22692             }
22693             html = this.cleanHtml(html);
22694             // fix up the special chars.. normaly like back quotes in word...
22695             // however we do not want to do this with chinese..
22696             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22697                 
22698                 var cc = match.charCodeAt();
22699
22700                 // Get the character value, handling surrogate pairs
22701                 if (match.length == 2) {
22702                     // It's a surrogate pair, calculate the Unicode code point
22703                     var high = match.charCodeAt(0) - 0xD800;
22704                     var low  = match.charCodeAt(1) - 0xDC00;
22705                     cc = (high * 0x400) + low + 0x10000;
22706                 }  else if (
22707                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22708                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22709                     (cc >= 0xf900 && cc < 0xfb00 )
22710                 ) {
22711                         return match;
22712                 }  
22713          
22714                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22715                 return "&#" + cc + ";";
22716                 
22717                 
22718             });
22719             
22720             
22721              
22722             if(this.owner.fireEvent('beforesync', this, html) !== false){
22723                 this.el.dom.value = html;
22724                 this.owner.fireEvent('sync', this, html);
22725             }
22726         }
22727     },
22728
22729     /**
22730      * Protected method that will not generally be called directly. Pushes the value of the textarea
22731      * into the iframe editor.
22732      */
22733     pushValue : function(){
22734         if(this.initialized){
22735             var v = this.el.dom.value.trim();
22736             
22737 //            if(v.length < 1){
22738 //                v = '&#160;';
22739 //            }
22740             
22741             if(this.owner.fireEvent('beforepush', this, v) !== false){
22742                 var d = (this.doc.body || this.doc.documentElement);
22743                 d.innerHTML = v;
22744                 this.cleanUpPaste();
22745                 this.el.dom.value = d.innerHTML;
22746                 this.owner.fireEvent('push', this, v);
22747             }
22748         }
22749     },
22750
22751     // private
22752     deferFocus : function(){
22753         this.focus.defer(10, this);
22754     },
22755
22756     // doc'ed in Field
22757     focus : function(){
22758         if(this.win && !this.sourceEditMode){
22759             this.win.focus();
22760         }else{
22761             this.el.focus();
22762         }
22763     },
22764     
22765     assignDocWin: function()
22766     {
22767         var iframe = this.iframe;
22768         
22769          if(Roo.isIE){
22770             this.doc = iframe.contentWindow.document;
22771             this.win = iframe.contentWindow;
22772         } else {
22773 //            if (!Roo.get(this.frameId)) {
22774 //                return;
22775 //            }
22776 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22777 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22778             
22779             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22780                 return;
22781             }
22782             
22783             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22784             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22785         }
22786     },
22787     
22788     // private
22789     initEditor : function(){
22790         //console.log("INIT EDITOR");
22791         this.assignDocWin();
22792         
22793         
22794         
22795         this.doc.designMode="on";
22796         this.doc.open();
22797         this.doc.write(this.getDocMarkup());
22798         this.doc.close();
22799         
22800         var dbody = (this.doc.body || this.doc.documentElement);
22801         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22802         // this copies styles from the containing element into thsi one..
22803         // not sure why we need all of this..
22804         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22805         
22806         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22807         //ss['background-attachment'] = 'fixed'; // w3c
22808         dbody.bgProperties = 'fixed'; // ie
22809         //Roo.DomHelper.applyStyles(dbody, ss);
22810         Roo.EventManager.on(this.doc, {
22811             //'mousedown': this.onEditorEvent,
22812             'mouseup': this.onEditorEvent,
22813             'dblclick': this.onEditorEvent,
22814             'click': this.onEditorEvent,
22815             'keyup': this.onEditorEvent,
22816             buffer:100,
22817             scope: this
22818         });
22819         if(Roo.isGecko){
22820             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22821         }
22822         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22823             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22824         }
22825         this.initialized = true;
22826
22827         this.owner.fireEvent('initialize', this);
22828         this.pushValue();
22829     },
22830
22831     // private
22832     onDestroy : function(){
22833         
22834         
22835         
22836         if(this.rendered){
22837             
22838             //for (var i =0; i < this.toolbars.length;i++) {
22839             //    // fixme - ask toolbars for heights?
22840             //    this.toolbars[i].onDestroy();
22841            // }
22842             
22843             //this.wrap.dom.innerHTML = '';
22844             //this.wrap.remove();
22845         }
22846     },
22847
22848     // private
22849     onFirstFocus : function(){
22850         
22851         this.assignDocWin();
22852         
22853         
22854         this.activated = true;
22855          
22856     
22857         if(Roo.isGecko){ // prevent silly gecko errors
22858             this.win.focus();
22859             var s = this.win.getSelection();
22860             if(!s.focusNode || s.focusNode.nodeType != 3){
22861                 var r = s.getRangeAt(0);
22862                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22863                 r.collapse(true);
22864                 this.deferFocus();
22865             }
22866             try{
22867                 this.execCmd('useCSS', true);
22868                 this.execCmd('styleWithCSS', false);
22869             }catch(e){}
22870         }
22871         this.owner.fireEvent('activate', this);
22872     },
22873
22874     // private
22875     adjustFont: function(btn){
22876         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22877         //if(Roo.isSafari){ // safari
22878         //    adjust *= 2;
22879        // }
22880         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22881         if(Roo.isSafari){ // safari
22882             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22883             v =  (v < 10) ? 10 : v;
22884             v =  (v > 48) ? 48 : v;
22885             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22886             
22887         }
22888         
22889         
22890         v = Math.max(1, v+adjust);
22891         
22892         this.execCmd('FontSize', v  );
22893     },
22894
22895     onEditorEvent : function(e)
22896     {
22897         this.owner.fireEvent('editorevent', this, e);
22898       //  this.updateToolbar();
22899         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22900     },
22901
22902     insertTag : function(tg)
22903     {
22904         // could be a bit smarter... -> wrap the current selected tRoo..
22905         if (tg.toLowerCase() == 'span' ||
22906             tg.toLowerCase() == 'code' ||
22907             tg.toLowerCase() == 'sup' ||
22908             tg.toLowerCase() == 'sub' 
22909             ) {
22910             
22911             range = this.createRange(this.getSelection());
22912             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22913             wrappingNode.appendChild(range.extractContents());
22914             range.insertNode(wrappingNode);
22915
22916             return;
22917             
22918             
22919             
22920         }
22921         this.execCmd("formatblock",   tg);
22922         
22923     },
22924     
22925     insertText : function(txt)
22926     {
22927         
22928         
22929         var range = this.createRange();
22930         range.deleteContents();
22931                //alert(Sender.getAttribute('label'));
22932                
22933         range.insertNode(this.doc.createTextNode(txt));
22934     } ,
22935     
22936      
22937
22938     /**
22939      * Executes a Midas editor command on the editor document and performs necessary focus and
22940      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22941      * @param {String} cmd The Midas command
22942      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22943      */
22944     relayCmd : function(cmd, value){
22945         this.win.focus();
22946         this.execCmd(cmd, value);
22947         this.owner.fireEvent('editorevent', this);
22948         //this.updateToolbar();
22949         this.owner.deferFocus();
22950     },
22951
22952     /**
22953      * Executes a Midas editor command directly on the editor document.
22954      * For visual commands, you should use {@link #relayCmd} instead.
22955      * <b>This should only be called after the editor is initialized.</b>
22956      * @param {String} cmd The Midas command
22957      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22958      */
22959     execCmd : function(cmd, value){
22960         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22961         this.syncValue();
22962     },
22963  
22964  
22965    
22966     /**
22967      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22968      * to insert tRoo.
22969      * @param {String} text | dom node.. 
22970      */
22971     insertAtCursor : function(text)
22972     {
22973         
22974         if(!this.activated){
22975             return;
22976         }
22977         /*
22978         if(Roo.isIE){
22979             this.win.focus();
22980             var r = this.doc.selection.createRange();
22981             if(r){
22982                 r.collapse(true);
22983                 r.pasteHTML(text);
22984                 this.syncValue();
22985                 this.deferFocus();
22986             
22987             }
22988             return;
22989         }
22990         */
22991         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22992             this.win.focus();
22993             
22994             
22995             // from jquery ui (MIT licenced)
22996             var range, node;
22997             var win = this.win;
22998             
22999             if (win.getSelection && win.getSelection().getRangeAt) {
23000                 range = win.getSelection().getRangeAt(0);
23001                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23002                 range.insertNode(node);
23003             } else if (win.document.selection && win.document.selection.createRange) {
23004                 // no firefox support
23005                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23006                 win.document.selection.createRange().pasteHTML(txt);
23007             } else {
23008                 // no firefox support
23009                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23010                 this.execCmd('InsertHTML', txt);
23011             } 
23012             
23013             this.syncValue();
23014             
23015             this.deferFocus();
23016         }
23017     },
23018  // private
23019     mozKeyPress : function(e){
23020         if(e.ctrlKey){
23021             var c = e.getCharCode(), cmd;
23022           
23023             if(c > 0){
23024                 c = String.fromCharCode(c).toLowerCase();
23025                 switch(c){
23026                     case 'b':
23027                         cmd = 'bold';
23028                         break;
23029                     case 'i':
23030                         cmd = 'italic';
23031                         break;
23032                     
23033                     case 'u':
23034                         cmd = 'underline';
23035                         break;
23036                     
23037                     case 'v':
23038                         this.cleanUpPaste.defer(100, this);
23039                         return;
23040                         
23041                 }
23042                 if(cmd){
23043                     this.win.focus();
23044                     this.execCmd(cmd);
23045                     this.deferFocus();
23046                     e.preventDefault();
23047                 }
23048                 
23049             }
23050         }
23051     },
23052
23053     // private
23054     fixKeys : function(){ // load time branching for fastest keydown performance
23055         if(Roo.isIE){
23056             return function(e){
23057                 var k = e.getKey(), r;
23058                 if(k == e.TAB){
23059                     e.stopEvent();
23060                     r = this.doc.selection.createRange();
23061                     if(r){
23062                         r.collapse(true);
23063                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23064                         this.deferFocus();
23065                     }
23066                     return;
23067                 }
23068                 
23069                 if(k == e.ENTER){
23070                     r = this.doc.selection.createRange();
23071                     if(r){
23072                         var target = r.parentElement();
23073                         if(!target || target.tagName.toLowerCase() != 'li'){
23074                             e.stopEvent();
23075                             r.pasteHTML('<br />');
23076                             r.collapse(false);
23077                             r.select();
23078                         }
23079                     }
23080                 }
23081                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23082                     this.cleanUpPaste.defer(100, this);
23083                     return;
23084                 }
23085                 
23086                 
23087             };
23088         }else if(Roo.isOpera){
23089             return function(e){
23090                 var k = e.getKey();
23091                 if(k == e.TAB){
23092                     e.stopEvent();
23093                     this.win.focus();
23094                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23095                     this.deferFocus();
23096                 }
23097                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23098                     this.cleanUpPaste.defer(100, this);
23099                     return;
23100                 }
23101                 
23102             };
23103         }else if(Roo.isSafari){
23104             return function(e){
23105                 var k = e.getKey();
23106                 
23107                 if(k == e.TAB){
23108                     e.stopEvent();
23109                     this.execCmd('InsertText','\t');
23110                     this.deferFocus();
23111                     return;
23112                 }
23113                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23114                     this.cleanUpPaste.defer(100, this);
23115                     return;
23116                 }
23117                 
23118              };
23119         }
23120     }(),
23121     
23122     getAllAncestors: function()
23123     {
23124         var p = this.getSelectedNode();
23125         var a = [];
23126         if (!p) {
23127             a.push(p); // push blank onto stack..
23128             p = this.getParentElement();
23129         }
23130         
23131         
23132         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23133             a.push(p);
23134             p = p.parentNode;
23135         }
23136         a.push(this.doc.body);
23137         return a;
23138     },
23139     lastSel : false,
23140     lastSelNode : false,
23141     
23142     
23143     getSelection : function() 
23144     {
23145         this.assignDocWin();
23146         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23147     },
23148     
23149     getSelectedNode: function() 
23150     {
23151         // this may only work on Gecko!!!
23152         
23153         // should we cache this!!!!
23154         
23155         
23156         
23157          
23158         var range = this.createRange(this.getSelection()).cloneRange();
23159         
23160         if (Roo.isIE) {
23161             var parent = range.parentElement();
23162             while (true) {
23163                 var testRange = range.duplicate();
23164                 testRange.moveToElementText(parent);
23165                 if (testRange.inRange(range)) {
23166                     break;
23167                 }
23168                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23169                     break;
23170                 }
23171                 parent = parent.parentElement;
23172             }
23173             return parent;
23174         }
23175         
23176         // is ancestor a text element.
23177         var ac =  range.commonAncestorContainer;
23178         if (ac.nodeType == 3) {
23179             ac = ac.parentNode;
23180         }
23181         
23182         var ar = ac.childNodes;
23183          
23184         var nodes = [];
23185         var other_nodes = [];
23186         var has_other_nodes = false;
23187         for (var i=0;i<ar.length;i++) {
23188             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23189                 continue;
23190             }
23191             // fullly contained node.
23192             
23193             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23194                 nodes.push(ar[i]);
23195                 continue;
23196             }
23197             
23198             // probably selected..
23199             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23200                 other_nodes.push(ar[i]);
23201                 continue;
23202             }
23203             // outer..
23204             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23205                 continue;
23206             }
23207             
23208             
23209             has_other_nodes = true;
23210         }
23211         if (!nodes.length && other_nodes.length) {
23212             nodes= other_nodes;
23213         }
23214         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23215             return false;
23216         }
23217         
23218         return nodes[0];
23219     },
23220     createRange: function(sel)
23221     {
23222         // this has strange effects when using with 
23223         // top toolbar - not sure if it's a great idea.
23224         //this.editor.contentWindow.focus();
23225         if (typeof sel != "undefined") {
23226             try {
23227                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23228             } catch(e) {
23229                 return this.doc.createRange();
23230             }
23231         } else {
23232             return this.doc.createRange();
23233         }
23234     },
23235     getParentElement: function()
23236     {
23237         
23238         this.assignDocWin();
23239         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23240         
23241         var range = this.createRange(sel);
23242          
23243         try {
23244             var p = range.commonAncestorContainer;
23245             while (p.nodeType == 3) { // text node
23246                 p = p.parentNode;
23247             }
23248             return p;
23249         } catch (e) {
23250             return null;
23251         }
23252     
23253     },
23254     /***
23255      *
23256      * Range intersection.. the hard stuff...
23257      *  '-1' = before
23258      *  '0' = hits..
23259      *  '1' = after.
23260      *         [ -- selected range --- ]
23261      *   [fail]                        [fail]
23262      *
23263      *    basically..
23264      *      if end is before start or  hits it. fail.
23265      *      if start is after end or hits it fail.
23266      *
23267      *   if either hits (but other is outside. - then it's not 
23268      *   
23269      *    
23270      **/
23271     
23272     
23273     // @see http://www.thismuchiknow.co.uk/?p=64.
23274     rangeIntersectsNode : function(range, node)
23275     {
23276         var nodeRange = node.ownerDocument.createRange();
23277         try {
23278             nodeRange.selectNode(node);
23279         } catch (e) {
23280             nodeRange.selectNodeContents(node);
23281         }
23282     
23283         var rangeStartRange = range.cloneRange();
23284         rangeStartRange.collapse(true);
23285     
23286         var rangeEndRange = range.cloneRange();
23287         rangeEndRange.collapse(false);
23288     
23289         var nodeStartRange = nodeRange.cloneRange();
23290         nodeStartRange.collapse(true);
23291     
23292         var nodeEndRange = nodeRange.cloneRange();
23293         nodeEndRange.collapse(false);
23294     
23295         return rangeStartRange.compareBoundaryPoints(
23296                  Range.START_TO_START, nodeEndRange) == -1 &&
23297                rangeEndRange.compareBoundaryPoints(
23298                  Range.START_TO_START, nodeStartRange) == 1;
23299         
23300          
23301     },
23302     rangeCompareNode : function(range, node)
23303     {
23304         var nodeRange = node.ownerDocument.createRange();
23305         try {
23306             nodeRange.selectNode(node);
23307         } catch (e) {
23308             nodeRange.selectNodeContents(node);
23309         }
23310         
23311         
23312         range.collapse(true);
23313     
23314         nodeRange.collapse(true);
23315      
23316         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23317         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23318          
23319         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23320         
23321         var nodeIsBefore   =  ss == 1;
23322         var nodeIsAfter    = ee == -1;
23323         
23324         if (nodeIsBefore && nodeIsAfter) {
23325             return 0; // outer
23326         }
23327         if (!nodeIsBefore && nodeIsAfter) {
23328             return 1; //right trailed.
23329         }
23330         
23331         if (nodeIsBefore && !nodeIsAfter) {
23332             return 2;  // left trailed.
23333         }
23334         // fully contined.
23335         return 3;
23336     },
23337
23338     // private? - in a new class?
23339     cleanUpPaste :  function()
23340     {
23341         // cleans up the whole document..
23342         Roo.log('cleanuppaste');
23343         
23344         this.cleanUpChildren(this.doc.body);
23345         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23346         if (clean != this.doc.body.innerHTML) {
23347             this.doc.body.innerHTML = clean;
23348         }
23349         
23350     },
23351     
23352     cleanWordChars : function(input) {// change the chars to hex code
23353         var he = Roo.HtmlEditorCore;
23354         
23355         var output = input;
23356         Roo.each(he.swapCodes, function(sw) { 
23357             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23358             
23359             output = output.replace(swapper, sw[1]);
23360         });
23361         
23362         return output;
23363     },
23364     
23365     
23366     cleanUpChildren : function (n)
23367     {
23368         if (!n.childNodes.length) {
23369             return;
23370         }
23371         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23372            this.cleanUpChild(n.childNodes[i]);
23373         }
23374     },
23375     
23376     
23377         
23378     
23379     cleanUpChild : function (node)
23380     {
23381         var ed = this;
23382         //console.log(node);
23383         if (node.nodeName == "#text") {
23384             // clean up silly Windows -- stuff?
23385             return; 
23386         }
23387         if (node.nodeName == "#comment") {
23388             node.parentNode.removeChild(node);
23389             // clean up silly Windows -- stuff?
23390             return; 
23391         }
23392         var lcname = node.tagName.toLowerCase();
23393         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23394         // whitelist of tags..
23395         
23396         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23397             // remove node.
23398             node.parentNode.removeChild(node);
23399             return;
23400             
23401         }
23402         
23403         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23404         
23405         // spans with no attributes - just remove them..
23406         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23407             remove_keep_children = true;
23408         }
23409         
23410         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23411         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23412         
23413         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23414         //    remove_keep_children = true;
23415         //}
23416         
23417         if (remove_keep_children) {
23418             this.cleanUpChildren(node);
23419             // inserts everything just before this node...
23420             while (node.childNodes.length) {
23421                 var cn = node.childNodes[0];
23422                 node.removeChild(cn);
23423                 node.parentNode.insertBefore(cn, node);
23424             }
23425             node.parentNode.removeChild(node);
23426             return;
23427         }
23428         
23429         if (!node.attributes || !node.attributes.length) {
23430             
23431           
23432             
23433             
23434             this.cleanUpChildren(node);
23435             return;
23436         }
23437         
23438         function cleanAttr(n,v)
23439         {
23440             
23441             if (v.match(/^\./) || v.match(/^\//)) {
23442                 return;
23443             }
23444             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23445                 return;
23446             }
23447             if (v.match(/^#/)) {
23448                 return;
23449             }
23450 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23451             node.removeAttribute(n);
23452             
23453         }
23454         
23455         var cwhite = this.cwhite;
23456         var cblack = this.cblack;
23457             
23458         function cleanStyle(n,v)
23459         {
23460             if (v.match(/expression/)) { //XSS?? should we even bother..
23461                 node.removeAttribute(n);
23462                 return;
23463             }
23464             
23465             var parts = v.split(/;/);
23466             var clean = [];
23467             
23468             Roo.each(parts, function(p) {
23469                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23470                 if (!p.length) {
23471                     return true;
23472                 }
23473                 var l = p.split(':').shift().replace(/\s+/g,'');
23474                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23475                 
23476                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23477 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23478                     //node.removeAttribute(n);
23479                     return true;
23480                 }
23481                 //Roo.log()
23482                 // only allow 'c whitelisted system attributes'
23483                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23484 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23485                     //node.removeAttribute(n);
23486                     return true;
23487                 }
23488                 
23489                 
23490                  
23491                 
23492                 clean.push(p);
23493                 return true;
23494             });
23495             if (clean.length) { 
23496                 node.setAttribute(n, clean.join(';'));
23497             } else {
23498                 node.removeAttribute(n);
23499             }
23500             
23501         }
23502         
23503         
23504         for (var i = node.attributes.length-1; i > -1 ; i--) {
23505             var a = node.attributes[i];
23506             //console.log(a);
23507             
23508             if (a.name.toLowerCase().substr(0,2)=='on')  {
23509                 node.removeAttribute(a.name);
23510                 continue;
23511             }
23512             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23513                 node.removeAttribute(a.name);
23514                 continue;
23515             }
23516             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23517                 cleanAttr(a.name,a.value); // fixme..
23518                 continue;
23519             }
23520             if (a.name == 'style') {
23521                 cleanStyle(a.name,a.value);
23522                 continue;
23523             }
23524             /// clean up MS crap..
23525             // tecnically this should be a list of valid class'es..
23526             
23527             
23528             if (a.name == 'class') {
23529                 if (a.value.match(/^Mso/)) {
23530                     node.removeAttribute('class');
23531                 }
23532                 
23533                 if (a.value.match(/^body$/)) {
23534                     node.removeAttribute('class');
23535                 }
23536                 continue;
23537             }
23538             
23539             // style cleanup!?
23540             // class cleanup?
23541             
23542         }
23543         
23544         
23545         this.cleanUpChildren(node);
23546         
23547         
23548     },
23549     
23550     /**
23551      * Clean up MS wordisms...
23552      */
23553     cleanWord : function(node)
23554     {
23555         if (!node) {
23556             this.cleanWord(this.doc.body);
23557             return;
23558         }
23559         
23560         if(
23561                 node.nodeName == 'SPAN' &&
23562                 !node.hasAttributes() &&
23563                 node.childNodes.length == 1 &&
23564                 node.firstChild.nodeName == "#text"  
23565         ) {
23566             var textNode = node.firstChild;
23567             node.removeChild(textNode);
23568             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23569                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23570             }
23571             node.parentNode.insertBefore(textNode, node);
23572             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23573                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23574             }
23575             node.parentNode.removeChild(node);
23576         }
23577         
23578         if (node.nodeName == "#text") {
23579             // clean up silly Windows -- stuff?
23580             return; 
23581         }
23582         if (node.nodeName == "#comment") {
23583             node.parentNode.removeChild(node);
23584             // clean up silly Windows -- stuff?
23585             return; 
23586         }
23587         
23588         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23589             node.parentNode.removeChild(node);
23590             return;
23591         }
23592         //Roo.log(node.tagName);
23593         // remove - but keep children..
23594         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23595             //Roo.log('-- removed');
23596             while (node.childNodes.length) {
23597                 var cn = node.childNodes[0];
23598                 node.removeChild(cn);
23599                 node.parentNode.insertBefore(cn, node);
23600                 // move node to parent - and clean it..
23601                 this.cleanWord(cn);
23602             }
23603             node.parentNode.removeChild(node);
23604             /// no need to iterate chidlren = it's got none..
23605             //this.iterateChildren(node, this.cleanWord);
23606             return;
23607         }
23608         // clean styles
23609         if (node.className.length) {
23610             
23611             var cn = node.className.split(/\W+/);
23612             var cna = [];
23613             Roo.each(cn, function(cls) {
23614                 if (cls.match(/Mso[a-zA-Z]+/)) {
23615                     return;
23616                 }
23617                 cna.push(cls);
23618             });
23619             node.className = cna.length ? cna.join(' ') : '';
23620             if (!cna.length) {
23621                 node.removeAttribute("class");
23622             }
23623         }
23624         
23625         if (node.hasAttribute("lang")) {
23626             node.removeAttribute("lang");
23627         }
23628         
23629         if (node.hasAttribute("style")) {
23630             
23631             var styles = node.getAttribute("style").split(";");
23632             var nstyle = [];
23633             Roo.each(styles, function(s) {
23634                 if (!s.match(/:/)) {
23635                     return;
23636                 }
23637                 var kv = s.split(":");
23638                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23639                     return;
23640                 }
23641                 // what ever is left... we allow.
23642                 nstyle.push(s);
23643             });
23644             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23645             if (!nstyle.length) {
23646                 node.removeAttribute('style');
23647             }
23648         }
23649         this.iterateChildren(node, this.cleanWord);
23650         
23651         
23652         
23653     },
23654     /**
23655      * iterateChildren of a Node, calling fn each time, using this as the scole..
23656      * @param {DomNode} node node to iterate children of.
23657      * @param {Function} fn method of this class to call on each item.
23658      */
23659     iterateChildren : function(node, fn)
23660     {
23661         if (!node.childNodes.length) {
23662                 return;
23663         }
23664         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23665            fn.call(this, node.childNodes[i])
23666         }
23667     },
23668     
23669     
23670     /**
23671      * cleanTableWidths.
23672      *
23673      * Quite often pasting from word etc.. results in tables with column and widths.
23674      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23675      *
23676      */
23677     cleanTableWidths : function(node)
23678     {
23679          
23680          
23681         if (!node) {
23682             this.cleanTableWidths(this.doc.body);
23683             return;
23684         }
23685         
23686         // ignore list...
23687         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23688             return; 
23689         }
23690         Roo.log(node.tagName);
23691         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23692             this.iterateChildren(node, this.cleanTableWidths);
23693             return;
23694         }
23695         if (node.hasAttribute('width')) {
23696             node.removeAttribute('width');
23697         }
23698         
23699          
23700         if (node.hasAttribute("style")) {
23701             // pretty basic...
23702             
23703             var styles = node.getAttribute("style").split(";");
23704             var nstyle = [];
23705             Roo.each(styles, function(s) {
23706                 if (!s.match(/:/)) {
23707                     return;
23708                 }
23709                 var kv = s.split(":");
23710                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23711                     return;
23712                 }
23713                 // what ever is left... we allow.
23714                 nstyle.push(s);
23715             });
23716             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23717             if (!nstyle.length) {
23718                 node.removeAttribute('style');
23719             }
23720         }
23721         
23722         this.iterateChildren(node, this.cleanTableWidths);
23723         
23724         
23725     },
23726     
23727     
23728     
23729     
23730     domToHTML : function(currentElement, depth, nopadtext) {
23731         
23732         depth = depth || 0;
23733         nopadtext = nopadtext || false;
23734     
23735         if (!currentElement) {
23736             return this.domToHTML(this.doc.body);
23737         }
23738         
23739         //Roo.log(currentElement);
23740         var j;
23741         var allText = false;
23742         var nodeName = currentElement.nodeName;
23743         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23744         
23745         if  (nodeName == '#text') {
23746             
23747             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23748         }
23749         
23750         
23751         var ret = '';
23752         if (nodeName != 'BODY') {
23753              
23754             var i = 0;
23755             // Prints the node tagName, such as <A>, <IMG>, etc
23756             if (tagName) {
23757                 var attr = [];
23758                 for(i = 0; i < currentElement.attributes.length;i++) {
23759                     // quoting?
23760                     var aname = currentElement.attributes.item(i).name;
23761                     if (!currentElement.attributes.item(i).value.length) {
23762                         continue;
23763                     }
23764                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23765                 }
23766                 
23767                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23768             } 
23769             else {
23770                 
23771                 // eack
23772             }
23773         } else {
23774             tagName = false;
23775         }
23776         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23777             return ret;
23778         }
23779         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23780             nopadtext = true;
23781         }
23782         
23783         
23784         // Traverse the tree
23785         i = 0;
23786         var currentElementChild = currentElement.childNodes.item(i);
23787         var allText = true;
23788         var innerHTML  = '';
23789         lastnode = '';
23790         while (currentElementChild) {
23791             // Formatting code (indent the tree so it looks nice on the screen)
23792             var nopad = nopadtext;
23793             if (lastnode == 'SPAN') {
23794                 nopad  = true;
23795             }
23796             // text
23797             if  (currentElementChild.nodeName == '#text') {
23798                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23799                 toadd = nopadtext ? toadd : toadd.trim();
23800                 if (!nopad && toadd.length > 80) {
23801                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23802                 }
23803                 innerHTML  += toadd;
23804                 
23805                 i++;
23806                 currentElementChild = currentElement.childNodes.item(i);
23807                 lastNode = '';
23808                 continue;
23809             }
23810             allText = false;
23811             
23812             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23813                 
23814             // Recursively traverse the tree structure of the child node
23815             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23816             lastnode = currentElementChild.nodeName;
23817             i++;
23818             currentElementChild=currentElement.childNodes.item(i);
23819         }
23820         
23821         ret += innerHTML;
23822         
23823         if (!allText) {
23824                 // The remaining code is mostly for formatting the tree
23825             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23826         }
23827         
23828         
23829         if (tagName) {
23830             ret+= "</"+tagName+">";
23831         }
23832         return ret;
23833         
23834     },
23835         
23836     applyBlacklists : function()
23837     {
23838         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23839         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23840         
23841         this.white = [];
23842         this.black = [];
23843         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23844             if (b.indexOf(tag) > -1) {
23845                 return;
23846             }
23847             this.white.push(tag);
23848             
23849         }, this);
23850         
23851         Roo.each(w, function(tag) {
23852             if (b.indexOf(tag) > -1) {
23853                 return;
23854             }
23855             if (this.white.indexOf(tag) > -1) {
23856                 return;
23857             }
23858             this.white.push(tag);
23859             
23860         }, this);
23861         
23862         
23863         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23864             if (w.indexOf(tag) > -1) {
23865                 return;
23866             }
23867             this.black.push(tag);
23868             
23869         }, this);
23870         
23871         Roo.each(b, function(tag) {
23872             if (w.indexOf(tag) > -1) {
23873                 return;
23874             }
23875             if (this.black.indexOf(tag) > -1) {
23876                 return;
23877             }
23878             this.black.push(tag);
23879             
23880         }, this);
23881         
23882         
23883         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23884         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23885         
23886         this.cwhite = [];
23887         this.cblack = [];
23888         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23889             if (b.indexOf(tag) > -1) {
23890                 return;
23891             }
23892             this.cwhite.push(tag);
23893             
23894         }, this);
23895         
23896         Roo.each(w, function(tag) {
23897             if (b.indexOf(tag) > -1) {
23898                 return;
23899             }
23900             if (this.cwhite.indexOf(tag) > -1) {
23901                 return;
23902             }
23903             this.cwhite.push(tag);
23904             
23905         }, this);
23906         
23907         
23908         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23909             if (w.indexOf(tag) > -1) {
23910                 return;
23911             }
23912             this.cblack.push(tag);
23913             
23914         }, this);
23915         
23916         Roo.each(b, function(tag) {
23917             if (w.indexOf(tag) > -1) {
23918                 return;
23919             }
23920             if (this.cblack.indexOf(tag) > -1) {
23921                 return;
23922             }
23923             this.cblack.push(tag);
23924             
23925         }, this);
23926     },
23927     
23928     setStylesheets : function(stylesheets)
23929     {
23930         if(typeof(stylesheets) == 'string'){
23931             Roo.get(this.iframe.contentDocument.head).createChild({
23932                 tag : 'link',
23933                 rel : 'stylesheet',
23934                 type : 'text/css',
23935                 href : stylesheets
23936             });
23937             
23938             return;
23939         }
23940         var _this = this;
23941      
23942         Roo.each(stylesheets, function(s) {
23943             if(!s.length){
23944                 return;
23945             }
23946             
23947             Roo.get(_this.iframe.contentDocument.head).createChild({
23948                 tag : 'link',
23949                 rel : 'stylesheet',
23950                 type : 'text/css',
23951                 href : s
23952             });
23953         });
23954
23955         
23956     },
23957     
23958     removeStylesheets : function()
23959     {
23960         var _this = this;
23961         
23962         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23963             s.remove();
23964         });
23965     },
23966     
23967     setStyle : function(style)
23968     {
23969         Roo.get(this.iframe.contentDocument.head).createChild({
23970             tag : 'style',
23971             type : 'text/css',
23972             html : style
23973         });
23974
23975         return;
23976     }
23977     
23978     // hide stuff that is not compatible
23979     /**
23980      * @event blur
23981      * @hide
23982      */
23983     /**
23984      * @event change
23985      * @hide
23986      */
23987     /**
23988      * @event focus
23989      * @hide
23990      */
23991     /**
23992      * @event specialkey
23993      * @hide
23994      */
23995     /**
23996      * @cfg {String} fieldClass @hide
23997      */
23998     /**
23999      * @cfg {String} focusClass @hide
24000      */
24001     /**
24002      * @cfg {String} autoCreate @hide
24003      */
24004     /**
24005      * @cfg {String} inputType @hide
24006      */
24007     /**
24008      * @cfg {String} invalidClass @hide
24009      */
24010     /**
24011      * @cfg {String} invalidText @hide
24012      */
24013     /**
24014      * @cfg {String} msgFx @hide
24015      */
24016     /**
24017      * @cfg {String} validateOnBlur @hide
24018      */
24019 });
24020
24021 Roo.HtmlEditorCore.white = [
24022         'area', 'br', 'img', 'input', 'hr', 'wbr',
24023         
24024        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24025        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24026        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24027        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24028        'table',   'ul',         'xmp', 
24029        
24030        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24031       'thead',   'tr', 
24032      
24033       'dir', 'menu', 'ol', 'ul', 'dl',
24034        
24035       'embed',  'object'
24036 ];
24037
24038
24039 Roo.HtmlEditorCore.black = [
24040     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24041         'applet', // 
24042         'base',   'basefont', 'bgsound', 'blink',  'body', 
24043         'frame',  'frameset', 'head',    'html',   'ilayer', 
24044         'iframe', 'layer',  'link',     'meta',    'object',   
24045         'script', 'style' ,'title',  'xml' // clean later..
24046 ];
24047 Roo.HtmlEditorCore.clean = [
24048     'script', 'style', 'title', 'xml'
24049 ];
24050 Roo.HtmlEditorCore.remove = [
24051     'font'
24052 ];
24053 // attributes..
24054
24055 Roo.HtmlEditorCore.ablack = [
24056     'on'
24057 ];
24058     
24059 Roo.HtmlEditorCore.aclean = [ 
24060     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24061 ];
24062
24063 // protocols..
24064 Roo.HtmlEditorCore.pwhite= [
24065         'http',  'https',  'mailto'
24066 ];
24067
24068 // white listed style attributes.
24069 Roo.HtmlEditorCore.cwhite= [
24070       //  'text-align', /// default is to allow most things..
24071       
24072          
24073 //        'font-size'//??
24074 ];
24075
24076 // black listed style attributes.
24077 Roo.HtmlEditorCore.cblack= [
24078       //  'font-size' -- this can be set by the project 
24079 ];
24080
24081
24082 Roo.HtmlEditorCore.swapCodes   =[ 
24083     [    8211, "--" ], 
24084     [    8212, "--" ], 
24085     [    8216,  "'" ],  
24086     [    8217, "'" ],  
24087     [    8220, '"' ],  
24088     [    8221, '"' ],  
24089     [    8226, "*" ],  
24090     [    8230, "..." ]
24091 ]; 
24092
24093     /*
24094  * - LGPL
24095  *
24096  * HtmlEditor
24097  * 
24098  */
24099
24100 /**
24101  * @class Roo.bootstrap.HtmlEditor
24102  * @extends Roo.bootstrap.TextArea
24103  * Bootstrap HtmlEditor class
24104
24105  * @constructor
24106  * Create a new HtmlEditor
24107  * @param {Object} config The config object
24108  */
24109
24110 Roo.bootstrap.HtmlEditor = function(config){
24111     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24112     if (!this.toolbars) {
24113         this.toolbars = [];
24114     }
24115     
24116     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24117     this.addEvents({
24118             /**
24119              * @event initialize
24120              * Fires when the editor is fully initialized (including the iframe)
24121              * @param {HtmlEditor} this
24122              */
24123             initialize: true,
24124             /**
24125              * @event activate
24126              * Fires when the editor is first receives the focus. Any insertion must wait
24127              * until after this event.
24128              * @param {HtmlEditor} this
24129              */
24130             activate: true,
24131              /**
24132              * @event beforesync
24133              * Fires before the textarea is updated with content from the editor iframe. Return false
24134              * to cancel the sync.
24135              * @param {HtmlEditor} this
24136              * @param {String} html
24137              */
24138             beforesync: true,
24139              /**
24140              * @event beforepush
24141              * Fires before the iframe editor is updated with content from the textarea. Return false
24142              * to cancel the push.
24143              * @param {HtmlEditor} this
24144              * @param {String} html
24145              */
24146             beforepush: true,
24147              /**
24148              * @event sync
24149              * Fires when the textarea is updated with content from the editor iframe.
24150              * @param {HtmlEditor} this
24151              * @param {String} html
24152              */
24153             sync: true,
24154              /**
24155              * @event push
24156              * Fires when the iframe editor is updated with content from the textarea.
24157              * @param {HtmlEditor} this
24158              * @param {String} html
24159              */
24160             push: true,
24161              /**
24162              * @event editmodechange
24163              * Fires when the editor switches edit modes
24164              * @param {HtmlEditor} this
24165              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24166              */
24167             editmodechange: true,
24168             /**
24169              * @event editorevent
24170              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24171              * @param {HtmlEditor} this
24172              */
24173             editorevent: true,
24174             /**
24175              * @event firstfocus
24176              * Fires when on first focus - needed by toolbars..
24177              * @param {HtmlEditor} this
24178              */
24179             firstfocus: true,
24180             /**
24181              * @event autosave
24182              * Auto save the htmlEditor value as a file into Events
24183              * @param {HtmlEditor} this
24184              */
24185             autosave: true,
24186             /**
24187              * @event savedpreview
24188              * preview the saved version of htmlEditor
24189              * @param {HtmlEditor} this
24190              */
24191             savedpreview: true
24192         });
24193 };
24194
24195
24196 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24197     
24198     
24199       /**
24200      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24201      */
24202     toolbars : false,
24203     
24204      /**
24205     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24206     */
24207     btns : [],
24208    
24209      /**
24210      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24211      *                        Roo.resizable.
24212      */
24213     resizable : false,
24214      /**
24215      * @cfg {Number} height (in pixels)
24216      */   
24217     height: 300,
24218    /**
24219      * @cfg {Number} width (in pixels)
24220      */   
24221     width: false,
24222     
24223     /**
24224      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24225      * 
24226      */
24227     stylesheets: false,
24228     
24229     // id of frame..
24230     frameId: false,
24231     
24232     // private properties
24233     validationEvent : false,
24234     deferHeight: true,
24235     initialized : false,
24236     activated : false,
24237     
24238     onFocus : Roo.emptyFn,
24239     iframePad:3,
24240     hideMode:'offsets',
24241     
24242     tbContainer : false,
24243     
24244     bodyCls : '',
24245     
24246     toolbarContainer :function() {
24247         return this.wrap.select('.x-html-editor-tb',true).first();
24248     },
24249
24250     /**
24251      * Protected method that will not generally be called directly. It
24252      * is called when the editor creates its toolbar. Override this method if you need to
24253      * add custom toolbar buttons.
24254      * @param {HtmlEditor} editor
24255      */
24256     createToolbar : function(){
24257         Roo.log('renewing');
24258         Roo.log("create toolbars");
24259         
24260         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24261         this.toolbars[0].render(this.toolbarContainer());
24262         
24263         return;
24264         
24265 //        if (!editor.toolbars || !editor.toolbars.length) {
24266 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24267 //        }
24268 //        
24269 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24270 //            editor.toolbars[i] = Roo.factory(
24271 //                    typeof(editor.toolbars[i]) == 'string' ?
24272 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24273 //                Roo.bootstrap.HtmlEditor);
24274 //            editor.toolbars[i].init(editor);
24275 //        }
24276     },
24277
24278      
24279     // private
24280     onRender : function(ct, position)
24281     {
24282        // Roo.log("Call onRender: " + this.xtype);
24283         var _t = this;
24284         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24285       
24286         this.wrap = this.inputEl().wrap({
24287             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24288         });
24289         
24290         this.editorcore.onRender(ct, position);
24291          
24292         if (this.resizable) {
24293             this.resizeEl = new Roo.Resizable(this.wrap, {
24294                 pinned : true,
24295                 wrap: true,
24296                 dynamic : true,
24297                 minHeight : this.height,
24298                 height: this.height,
24299                 handles : this.resizable,
24300                 width: this.width,
24301                 listeners : {
24302                     resize : function(r, w, h) {
24303                         _t.onResize(w,h); // -something
24304                     }
24305                 }
24306             });
24307             
24308         }
24309         this.createToolbar(this);
24310        
24311         
24312         if(!this.width && this.resizable){
24313             this.setSize(this.wrap.getSize());
24314         }
24315         if (this.resizeEl) {
24316             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24317             // should trigger onReize..
24318         }
24319         
24320     },
24321
24322     // private
24323     onResize : function(w, h)
24324     {
24325         Roo.log('resize: ' +w + ',' + h );
24326         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24327         var ew = false;
24328         var eh = false;
24329         
24330         if(this.inputEl() ){
24331             if(typeof w == 'number'){
24332                 var aw = w - this.wrap.getFrameWidth('lr');
24333                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24334                 ew = aw;
24335             }
24336             if(typeof h == 'number'){
24337                  var tbh = -11;  // fixme it needs to tool bar size!
24338                 for (var i =0; i < this.toolbars.length;i++) {
24339                     // fixme - ask toolbars for heights?
24340                     tbh += this.toolbars[i].el.getHeight();
24341                     //if (this.toolbars[i].footer) {
24342                     //    tbh += this.toolbars[i].footer.el.getHeight();
24343                     //}
24344                 }
24345               
24346                 
24347                 
24348                 
24349                 
24350                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24351                 ah -= 5; // knock a few pixes off for look..
24352                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24353                 var eh = ah;
24354             }
24355         }
24356         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24357         this.editorcore.onResize(ew,eh);
24358         
24359     },
24360
24361     /**
24362      * Toggles the editor between standard and source edit mode.
24363      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24364      */
24365     toggleSourceEdit : function(sourceEditMode)
24366     {
24367         this.editorcore.toggleSourceEdit(sourceEditMode);
24368         
24369         if(this.editorcore.sourceEditMode){
24370             Roo.log('editor - showing textarea');
24371             
24372 //            Roo.log('in');
24373 //            Roo.log(this.syncValue());
24374             this.syncValue();
24375             this.inputEl().removeClass(['hide', 'x-hidden']);
24376             this.inputEl().dom.removeAttribute('tabIndex');
24377             this.inputEl().focus();
24378         }else{
24379             Roo.log('editor - hiding textarea');
24380 //            Roo.log('out')
24381 //            Roo.log(this.pushValue()); 
24382             this.pushValue();
24383             
24384             this.inputEl().addClass(['hide', 'x-hidden']);
24385             this.inputEl().dom.setAttribute('tabIndex', -1);
24386             //this.deferFocus();
24387         }
24388          
24389         if(this.resizable){
24390             this.setSize(this.wrap.getSize());
24391         }
24392         
24393         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24394     },
24395  
24396     // private (for BoxComponent)
24397     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24398
24399     // private (for BoxComponent)
24400     getResizeEl : function(){
24401         return this.wrap;
24402     },
24403
24404     // private (for BoxComponent)
24405     getPositionEl : function(){
24406         return this.wrap;
24407     },
24408
24409     // private
24410     initEvents : function(){
24411         this.originalValue = this.getValue();
24412     },
24413
24414 //    /**
24415 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24416 //     * @method
24417 //     */
24418 //    markInvalid : Roo.emptyFn,
24419 //    /**
24420 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24421 //     * @method
24422 //     */
24423 //    clearInvalid : Roo.emptyFn,
24424
24425     setValue : function(v){
24426         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24427         this.editorcore.pushValue();
24428     },
24429
24430      
24431     // private
24432     deferFocus : function(){
24433         this.focus.defer(10, this);
24434     },
24435
24436     // doc'ed in Field
24437     focus : function(){
24438         this.editorcore.focus();
24439         
24440     },
24441       
24442
24443     // private
24444     onDestroy : function(){
24445         
24446         
24447         
24448         if(this.rendered){
24449             
24450             for (var i =0; i < this.toolbars.length;i++) {
24451                 // fixme - ask toolbars for heights?
24452                 this.toolbars[i].onDestroy();
24453             }
24454             
24455             this.wrap.dom.innerHTML = '';
24456             this.wrap.remove();
24457         }
24458     },
24459
24460     // private
24461     onFirstFocus : function(){
24462         //Roo.log("onFirstFocus");
24463         this.editorcore.onFirstFocus();
24464          for (var i =0; i < this.toolbars.length;i++) {
24465             this.toolbars[i].onFirstFocus();
24466         }
24467         
24468     },
24469     
24470     // private
24471     syncValue : function()
24472     {   
24473         this.editorcore.syncValue();
24474     },
24475     
24476     pushValue : function()
24477     {   
24478         this.editorcore.pushValue();
24479     }
24480      
24481     
24482     // hide stuff that is not compatible
24483     /**
24484      * @event blur
24485      * @hide
24486      */
24487     /**
24488      * @event change
24489      * @hide
24490      */
24491     /**
24492      * @event focus
24493      * @hide
24494      */
24495     /**
24496      * @event specialkey
24497      * @hide
24498      */
24499     /**
24500      * @cfg {String} fieldClass @hide
24501      */
24502     /**
24503      * @cfg {String} focusClass @hide
24504      */
24505     /**
24506      * @cfg {String} autoCreate @hide
24507      */
24508     /**
24509      * @cfg {String} inputType @hide
24510      */
24511      
24512     /**
24513      * @cfg {String} invalidText @hide
24514      */
24515     /**
24516      * @cfg {String} msgFx @hide
24517      */
24518     /**
24519      * @cfg {String} validateOnBlur @hide
24520      */
24521 });
24522  
24523     
24524    
24525    
24526    
24527       
24528 Roo.namespace('Roo.bootstrap.htmleditor');
24529 /**
24530  * @class Roo.bootstrap.HtmlEditorToolbar1
24531  * Basic Toolbar
24532  * 
24533  * @example
24534  * Usage:
24535  *
24536  new Roo.bootstrap.HtmlEditor({
24537     ....
24538     toolbars : [
24539         new Roo.bootstrap.HtmlEditorToolbar1({
24540             disable : { fonts: 1 , format: 1, ..., ... , ...],
24541             btns : [ .... ]
24542         })
24543     }
24544      
24545  * 
24546  * @cfg {Object} disable List of elements to disable..
24547  * @cfg {Array} btns List of additional buttons.
24548  * 
24549  * 
24550  * NEEDS Extra CSS? 
24551  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24552  */
24553  
24554 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24555 {
24556     
24557     Roo.apply(this, config);
24558     
24559     // default disabled, based on 'good practice'..
24560     this.disable = this.disable || {};
24561     Roo.applyIf(this.disable, {
24562         fontSize : true,
24563         colors : true,
24564         specialElements : true
24565     });
24566     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24567     
24568     this.editor = config.editor;
24569     this.editorcore = config.editor.editorcore;
24570     
24571     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24572     
24573     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24574     // dont call parent... till later.
24575 }
24576 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24577      
24578     bar : true,
24579     
24580     editor : false,
24581     editorcore : false,
24582     
24583     
24584     formats : [
24585         "p" ,  
24586         "h1","h2","h3","h4","h5","h6", 
24587         "pre", "code", 
24588         "abbr", "acronym", "address", "cite", "samp", "var",
24589         'div','span'
24590     ],
24591     
24592     onRender : function(ct, position)
24593     {
24594        // Roo.log("Call onRender: " + this.xtype);
24595         
24596        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24597        Roo.log(this.el);
24598        this.el.dom.style.marginBottom = '0';
24599        var _this = this;
24600        var editorcore = this.editorcore;
24601        var editor= this.editor;
24602        
24603        var children = [];
24604        var btn = function(id,cmd , toggle, handler, html){
24605        
24606             var  event = toggle ? 'toggle' : 'click';
24607        
24608             var a = {
24609                 size : 'sm',
24610                 xtype: 'Button',
24611                 xns: Roo.bootstrap,
24612                 //glyphicon : id,
24613                 fa: id,
24614                 cmd : id || cmd,
24615                 enableToggle:toggle !== false,
24616                 html : html || '',
24617                 pressed : toggle ? false : null,
24618                 listeners : {}
24619             };
24620             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24621                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24622             };
24623             children.push(a);
24624             return a;
24625        }
24626        
24627     //    var cb_box = function...
24628         
24629         var style = {
24630                 xtype: 'Button',
24631                 size : 'sm',
24632                 xns: Roo.bootstrap,
24633                 fa : 'font',
24634                 //html : 'submit'
24635                 menu : {
24636                     xtype: 'Menu',
24637                     xns: Roo.bootstrap,
24638                     items:  []
24639                 }
24640         };
24641         Roo.each(this.formats, function(f) {
24642             style.menu.items.push({
24643                 xtype :'MenuItem',
24644                 xns: Roo.bootstrap,
24645                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24646                 tagname : f,
24647                 listeners : {
24648                     click : function()
24649                     {
24650                         editorcore.insertTag(this.tagname);
24651                         editor.focus();
24652                     }
24653                 }
24654                 
24655             });
24656         });
24657         children.push(style);   
24658         
24659         btn('bold',false,true);
24660         btn('italic',false,true);
24661         btn('align-left', 'justifyleft',true);
24662         btn('align-center', 'justifycenter',true);
24663         btn('align-right' , 'justifyright',true);
24664         btn('link', false, false, function(btn) {
24665             //Roo.log("create link?");
24666             var url = prompt(this.createLinkText, this.defaultLinkValue);
24667             if(url && url != 'http:/'+'/'){
24668                 this.editorcore.relayCmd('createlink', url);
24669             }
24670         }),
24671         btn('list','insertunorderedlist',true);
24672         btn('pencil', false,true, function(btn){
24673                 Roo.log(this);
24674                 this.toggleSourceEdit(btn.pressed);
24675         });
24676         
24677         if (this.editor.btns.length > 0) {
24678             for (var i = 0; i<this.editor.btns.length; i++) {
24679                 children.push(this.editor.btns[i]);
24680             }
24681         }
24682         
24683         /*
24684         var cog = {
24685                 xtype: 'Button',
24686                 size : 'sm',
24687                 xns: Roo.bootstrap,
24688                 glyphicon : 'cog',
24689                 //html : 'submit'
24690                 menu : {
24691                     xtype: 'Menu',
24692                     xns: Roo.bootstrap,
24693                     items:  []
24694                 }
24695         };
24696         
24697         cog.menu.items.push({
24698             xtype :'MenuItem',
24699             xns: Roo.bootstrap,
24700             html : Clean styles,
24701             tagname : f,
24702             listeners : {
24703                 click : function()
24704                 {
24705                     editorcore.insertTag(this.tagname);
24706                     editor.focus();
24707                 }
24708             }
24709             
24710         });
24711        */
24712         
24713          
24714        this.xtype = 'NavSimplebar';
24715         
24716         for(var i=0;i< children.length;i++) {
24717             
24718             this.buttons.add(this.addxtypeChild(children[i]));
24719             
24720         }
24721         
24722         editor.on('editorevent', this.updateToolbar, this);
24723     },
24724     onBtnClick : function(id)
24725     {
24726        this.editorcore.relayCmd(id);
24727        this.editorcore.focus();
24728     },
24729     
24730     /**
24731      * Protected method that will not generally be called directly. It triggers
24732      * a toolbar update by reading the markup state of the current selection in the editor.
24733      */
24734     updateToolbar: function(){
24735
24736         if(!this.editorcore.activated){
24737             this.editor.onFirstFocus(); // is this neeed?
24738             return;
24739         }
24740
24741         var btns = this.buttons; 
24742         var doc = this.editorcore.doc;
24743         btns.get('bold').setActive(doc.queryCommandState('bold'));
24744         btns.get('italic').setActive(doc.queryCommandState('italic'));
24745         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24746         
24747         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24748         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24749         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24750         
24751         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24752         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24753          /*
24754         
24755         var ans = this.editorcore.getAllAncestors();
24756         if (this.formatCombo) {
24757             
24758             
24759             var store = this.formatCombo.store;
24760             this.formatCombo.setValue("");
24761             for (var i =0; i < ans.length;i++) {
24762                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24763                     // select it..
24764                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24765                     break;
24766                 }
24767             }
24768         }
24769         
24770         
24771         
24772         // hides menus... - so this cant be on a menu...
24773         Roo.bootstrap.MenuMgr.hideAll();
24774         */
24775         Roo.bootstrap.MenuMgr.hideAll();
24776         //this.editorsyncValue();
24777     },
24778     onFirstFocus: function() {
24779         this.buttons.each(function(item){
24780            item.enable();
24781         });
24782     },
24783     toggleSourceEdit : function(sourceEditMode){
24784         
24785           
24786         if(sourceEditMode){
24787             Roo.log("disabling buttons");
24788            this.buttons.each( function(item){
24789                 if(item.cmd != 'pencil'){
24790                     item.disable();
24791                 }
24792             });
24793           
24794         }else{
24795             Roo.log("enabling buttons");
24796             if(this.editorcore.initialized){
24797                 this.buttons.each( function(item){
24798                     item.enable();
24799                 });
24800             }
24801             
24802         }
24803         Roo.log("calling toggole on editor");
24804         // tell the editor that it's been pressed..
24805         this.editor.toggleSourceEdit(sourceEditMode);
24806        
24807     }
24808 });
24809
24810
24811
24812
24813
24814 /**
24815  * @class Roo.bootstrap.Table.AbstractSelectionModel
24816  * @extends Roo.util.Observable
24817  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24818  * implemented by descendant classes.  This class should not be directly instantiated.
24819  * @constructor
24820  */
24821 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24822     this.locked = false;
24823     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24824 };
24825
24826
24827 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24828     /** @ignore Called by the grid automatically. Do not call directly. */
24829     init : function(grid){
24830         this.grid = grid;
24831         this.initEvents();
24832     },
24833
24834     /**
24835      * Locks the selections.
24836      */
24837     lock : function(){
24838         this.locked = true;
24839     },
24840
24841     /**
24842      * Unlocks the selections.
24843      */
24844     unlock : function(){
24845         this.locked = false;
24846     },
24847
24848     /**
24849      * Returns true if the selections are locked.
24850      * @return {Boolean}
24851      */
24852     isLocked : function(){
24853         return this.locked;
24854     },
24855     
24856     
24857     initEvents : function ()
24858     {
24859         
24860     }
24861 });
24862 /**
24863  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24864  * @class Roo.bootstrap.Table.RowSelectionModel
24865  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24866  * It supports multiple selections and keyboard selection/navigation. 
24867  * @constructor
24868  * @param {Object} config
24869  */
24870
24871 Roo.bootstrap.Table.RowSelectionModel = function(config){
24872     Roo.apply(this, config);
24873     this.selections = new Roo.util.MixedCollection(false, function(o){
24874         return o.id;
24875     });
24876
24877     this.last = false;
24878     this.lastActive = false;
24879
24880     this.addEvents({
24881         /**
24882              * @event selectionchange
24883              * Fires when the selection changes
24884              * @param {SelectionModel} this
24885              */
24886             "selectionchange" : true,
24887         /**
24888              * @event afterselectionchange
24889              * Fires after the selection changes (eg. by key press or clicking)
24890              * @param {SelectionModel} this
24891              */
24892             "afterselectionchange" : true,
24893         /**
24894              * @event beforerowselect
24895              * Fires when a row is selected being selected, return false to cancel.
24896              * @param {SelectionModel} this
24897              * @param {Number} rowIndex The selected index
24898              * @param {Boolean} keepExisting False if other selections will be cleared
24899              */
24900             "beforerowselect" : true,
24901         /**
24902              * @event rowselect
24903              * Fires when a row is selected.
24904              * @param {SelectionModel} this
24905              * @param {Number} rowIndex The selected index
24906              * @param {Roo.data.Record} r The record
24907              */
24908             "rowselect" : true,
24909         /**
24910              * @event rowdeselect
24911              * Fires when a row is deselected.
24912              * @param {SelectionModel} this
24913              * @param {Number} rowIndex The selected index
24914              */
24915         "rowdeselect" : true
24916     });
24917     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24918     this.locked = false;
24919  };
24920
24921 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24922     /**
24923      * @cfg {Boolean} singleSelect
24924      * True to allow selection of only one row at a time (defaults to false)
24925      */
24926     singleSelect : false,
24927
24928     // private
24929     initEvents : function()
24930     {
24931
24932         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24933         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24934         //}else{ // allow click to work like normal
24935          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24936         //}
24937         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24938         this.grid.on("rowclick", this.handleMouseDown, this);
24939         
24940         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24941             "up" : function(e){
24942                 if(!e.shiftKey){
24943                     this.selectPrevious(e.shiftKey);
24944                 }else if(this.last !== false && this.lastActive !== false){
24945                     var last = this.last;
24946                     this.selectRange(this.last,  this.lastActive-1);
24947                     this.grid.getView().focusRow(this.lastActive);
24948                     if(last !== false){
24949                         this.last = last;
24950                     }
24951                 }else{
24952                     this.selectFirstRow();
24953                 }
24954                 this.fireEvent("afterselectionchange", this);
24955             },
24956             "down" : function(e){
24957                 if(!e.shiftKey){
24958                     this.selectNext(e.shiftKey);
24959                 }else if(this.last !== false && this.lastActive !== false){
24960                     var last = this.last;
24961                     this.selectRange(this.last,  this.lastActive+1);
24962                     this.grid.getView().focusRow(this.lastActive);
24963                     if(last !== false){
24964                         this.last = last;
24965                     }
24966                 }else{
24967                     this.selectFirstRow();
24968                 }
24969                 this.fireEvent("afterselectionchange", this);
24970             },
24971             scope: this
24972         });
24973         this.grid.store.on('load', function(){
24974             this.selections.clear();
24975         },this);
24976         /*
24977         var view = this.grid.view;
24978         view.on("refresh", this.onRefresh, this);
24979         view.on("rowupdated", this.onRowUpdated, this);
24980         view.on("rowremoved", this.onRemove, this);
24981         */
24982     },
24983
24984     // private
24985     onRefresh : function()
24986     {
24987         var ds = this.grid.store, i, v = this.grid.view;
24988         var s = this.selections;
24989         s.each(function(r){
24990             if((i = ds.indexOfId(r.id)) != -1){
24991                 v.onRowSelect(i);
24992             }else{
24993                 s.remove(r);
24994             }
24995         });
24996     },
24997
24998     // private
24999     onRemove : function(v, index, r){
25000         this.selections.remove(r);
25001     },
25002
25003     // private
25004     onRowUpdated : function(v, index, r){
25005         if(this.isSelected(r)){
25006             v.onRowSelect(index);
25007         }
25008     },
25009
25010     /**
25011      * Select records.
25012      * @param {Array} records The records to select
25013      * @param {Boolean} keepExisting (optional) True to keep existing selections
25014      */
25015     selectRecords : function(records, keepExisting)
25016     {
25017         if(!keepExisting){
25018             this.clearSelections();
25019         }
25020             var ds = this.grid.store;
25021         for(var i = 0, len = records.length; i < len; i++){
25022             this.selectRow(ds.indexOf(records[i]), true);
25023         }
25024     },
25025
25026     /**
25027      * Gets the number of selected rows.
25028      * @return {Number}
25029      */
25030     getCount : function(){
25031         return this.selections.length;
25032     },
25033
25034     /**
25035      * Selects the first row in the grid.
25036      */
25037     selectFirstRow : function(){
25038         this.selectRow(0);
25039     },
25040
25041     /**
25042      * Select the last row.
25043      * @param {Boolean} keepExisting (optional) True to keep existing selections
25044      */
25045     selectLastRow : function(keepExisting){
25046         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25047         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25048     },
25049
25050     /**
25051      * Selects the row immediately following the last selected row.
25052      * @param {Boolean} keepExisting (optional) True to keep existing selections
25053      */
25054     selectNext : function(keepExisting)
25055     {
25056             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25057             this.selectRow(this.last+1, keepExisting);
25058             this.grid.getView().focusRow(this.last);
25059         }
25060     },
25061
25062     /**
25063      * Selects the row that precedes the last selected row.
25064      * @param {Boolean} keepExisting (optional) True to keep existing selections
25065      */
25066     selectPrevious : function(keepExisting){
25067         if(this.last){
25068             this.selectRow(this.last-1, keepExisting);
25069             this.grid.getView().focusRow(this.last);
25070         }
25071     },
25072
25073     /**
25074      * Returns the selected records
25075      * @return {Array} Array of selected records
25076      */
25077     getSelections : function(){
25078         return [].concat(this.selections.items);
25079     },
25080
25081     /**
25082      * Returns the first selected record.
25083      * @return {Record}
25084      */
25085     getSelected : function(){
25086         return this.selections.itemAt(0);
25087     },
25088
25089
25090     /**
25091      * Clears all selections.
25092      */
25093     clearSelections : function(fast)
25094     {
25095         if(this.locked) {
25096             return;
25097         }
25098         if(fast !== true){
25099                 var ds = this.grid.store;
25100             var s = this.selections;
25101             s.each(function(r){
25102                 this.deselectRow(ds.indexOfId(r.id));
25103             }, this);
25104             s.clear();
25105         }else{
25106             this.selections.clear();
25107         }
25108         this.last = false;
25109     },
25110
25111
25112     /**
25113      * Selects all rows.
25114      */
25115     selectAll : function(){
25116         if(this.locked) {
25117             return;
25118         }
25119         this.selections.clear();
25120         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25121             this.selectRow(i, true);
25122         }
25123     },
25124
25125     /**
25126      * Returns True if there is a selection.
25127      * @return {Boolean}
25128      */
25129     hasSelection : function(){
25130         return this.selections.length > 0;
25131     },
25132
25133     /**
25134      * Returns True if the specified row is selected.
25135      * @param {Number/Record} record The record or index of the record to check
25136      * @return {Boolean}
25137      */
25138     isSelected : function(index){
25139             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25140         return (r && this.selections.key(r.id) ? true : false);
25141     },
25142
25143     /**
25144      * Returns True if the specified record id is selected.
25145      * @param {String} id The id of record to check
25146      * @return {Boolean}
25147      */
25148     isIdSelected : function(id){
25149         return (this.selections.key(id) ? true : false);
25150     },
25151
25152
25153     // private
25154     handleMouseDBClick : function(e, t){
25155         
25156     },
25157     // private
25158     handleMouseDown : function(e, t)
25159     {
25160             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25161         if(this.isLocked() || rowIndex < 0 ){
25162             return;
25163         };
25164         if(e.shiftKey && this.last !== false){
25165             var last = this.last;
25166             this.selectRange(last, rowIndex, e.ctrlKey);
25167             this.last = last; // reset the last
25168             t.focus();
25169     
25170         }else{
25171             var isSelected = this.isSelected(rowIndex);
25172             //Roo.log("select row:" + rowIndex);
25173             if(isSelected){
25174                 this.deselectRow(rowIndex);
25175             } else {
25176                         this.selectRow(rowIndex, true);
25177             }
25178     
25179             /*
25180                 if(e.button !== 0 && isSelected){
25181                 alert('rowIndex 2: ' + rowIndex);
25182                     view.focusRow(rowIndex);
25183                 }else if(e.ctrlKey && isSelected){
25184                     this.deselectRow(rowIndex);
25185                 }else if(!isSelected){
25186                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25187                     view.focusRow(rowIndex);
25188                 }
25189             */
25190         }
25191         this.fireEvent("afterselectionchange", this);
25192     },
25193     // private
25194     handleDragableRowClick :  function(grid, rowIndex, e) 
25195     {
25196         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25197             this.selectRow(rowIndex, false);
25198             grid.view.focusRow(rowIndex);
25199              this.fireEvent("afterselectionchange", this);
25200         }
25201     },
25202     
25203     /**
25204      * Selects multiple rows.
25205      * @param {Array} rows Array of the indexes of the row to select
25206      * @param {Boolean} keepExisting (optional) True to keep existing selections
25207      */
25208     selectRows : function(rows, keepExisting){
25209         if(!keepExisting){
25210             this.clearSelections();
25211         }
25212         for(var i = 0, len = rows.length; i < len; i++){
25213             this.selectRow(rows[i], true);
25214         }
25215     },
25216
25217     /**
25218      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25219      * @param {Number} startRow The index of the first row in the range
25220      * @param {Number} endRow The index of the last row in the range
25221      * @param {Boolean} keepExisting (optional) True to retain existing selections
25222      */
25223     selectRange : function(startRow, endRow, keepExisting){
25224         if(this.locked) {
25225             return;
25226         }
25227         if(!keepExisting){
25228             this.clearSelections();
25229         }
25230         if(startRow <= endRow){
25231             for(var i = startRow; i <= endRow; i++){
25232                 this.selectRow(i, true);
25233             }
25234         }else{
25235             for(var i = startRow; i >= endRow; i--){
25236                 this.selectRow(i, true);
25237             }
25238         }
25239     },
25240
25241     /**
25242      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25243      * @param {Number} startRow The index of the first row in the range
25244      * @param {Number} endRow The index of the last row in the range
25245      */
25246     deselectRange : function(startRow, endRow, preventViewNotify){
25247         if(this.locked) {
25248             return;
25249         }
25250         for(var i = startRow; i <= endRow; i++){
25251             this.deselectRow(i, preventViewNotify);
25252         }
25253     },
25254
25255     /**
25256      * Selects a row.
25257      * @param {Number} row The index of the row to select
25258      * @param {Boolean} keepExisting (optional) True to keep existing selections
25259      */
25260     selectRow : function(index, keepExisting, preventViewNotify)
25261     {
25262             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25263             return;
25264         }
25265         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25266             if(!keepExisting || this.singleSelect){
25267                 this.clearSelections();
25268             }
25269             
25270             var r = this.grid.store.getAt(index);
25271             //console.log('selectRow - record id :' + r.id);
25272             
25273             this.selections.add(r);
25274             this.last = this.lastActive = index;
25275             if(!preventViewNotify){
25276                 var proxy = new Roo.Element(
25277                                 this.grid.getRowDom(index)
25278                 );
25279                 proxy.addClass('bg-info info');
25280             }
25281             this.fireEvent("rowselect", this, index, r);
25282             this.fireEvent("selectionchange", this);
25283         }
25284     },
25285
25286     /**
25287      * Deselects a row.
25288      * @param {Number} row The index of the row to deselect
25289      */
25290     deselectRow : function(index, preventViewNotify)
25291     {
25292         if(this.locked) {
25293             return;
25294         }
25295         if(this.last == index){
25296             this.last = false;
25297         }
25298         if(this.lastActive == index){
25299             this.lastActive = false;
25300         }
25301         
25302         var r = this.grid.store.getAt(index);
25303         if (!r) {
25304             return;
25305         }
25306         
25307         this.selections.remove(r);
25308         //.console.log('deselectRow - record id :' + r.id);
25309         if(!preventViewNotify){
25310         
25311             var proxy = new Roo.Element(
25312                 this.grid.getRowDom(index)
25313             );
25314             proxy.removeClass('bg-info info');
25315         }
25316         this.fireEvent("rowdeselect", this, index);
25317         this.fireEvent("selectionchange", this);
25318     },
25319
25320     // private
25321     restoreLast : function(){
25322         if(this._last){
25323             this.last = this._last;
25324         }
25325     },
25326
25327     // private
25328     acceptsNav : function(row, col, cm){
25329         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25330     },
25331
25332     // private
25333     onEditorKey : function(field, e){
25334         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25335         if(k == e.TAB){
25336             e.stopEvent();
25337             ed.completeEdit();
25338             if(e.shiftKey){
25339                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25340             }else{
25341                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25342             }
25343         }else if(k == e.ENTER && !e.ctrlKey){
25344             e.stopEvent();
25345             ed.completeEdit();
25346             if(e.shiftKey){
25347                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25348             }else{
25349                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25350             }
25351         }else if(k == e.ESC){
25352             ed.cancelEdit();
25353         }
25354         if(newCell){
25355             g.startEditing(newCell[0], newCell[1]);
25356         }
25357     }
25358 });
25359 /*
25360  * Based on:
25361  * Ext JS Library 1.1.1
25362  * Copyright(c) 2006-2007, Ext JS, LLC.
25363  *
25364  * Originally Released Under LGPL - original licence link has changed is not relivant.
25365  *
25366  * Fork - LGPL
25367  * <script type="text/javascript">
25368  */
25369  
25370 /**
25371  * @class Roo.bootstrap.PagingToolbar
25372  * @extends Roo.bootstrap.NavSimplebar
25373  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25374  * @constructor
25375  * Create a new PagingToolbar
25376  * @param {Object} config The config object
25377  * @param {Roo.data.Store} store
25378  */
25379 Roo.bootstrap.PagingToolbar = function(config)
25380 {
25381     // old args format still supported... - xtype is prefered..
25382         // created from xtype...
25383     
25384     this.ds = config.dataSource;
25385     
25386     if (config.store && !this.ds) {
25387         this.store= Roo.factory(config.store, Roo.data);
25388         this.ds = this.store;
25389         this.ds.xmodule = this.xmodule || false;
25390     }
25391     
25392     this.toolbarItems = [];
25393     if (config.items) {
25394         this.toolbarItems = config.items;
25395     }
25396     
25397     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25398     
25399     this.cursor = 0;
25400     
25401     if (this.ds) { 
25402         this.bind(this.ds);
25403     }
25404     
25405     if (Roo.bootstrap.version == 4) {
25406         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25407     } else {
25408         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25409     }
25410     
25411 };
25412
25413 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25414     /**
25415      * @cfg {Roo.data.Store} dataSource
25416      * The underlying data store providing the paged data
25417      */
25418     /**
25419      * @cfg {String/HTMLElement/Element} container
25420      * container The id or element that will contain the toolbar
25421      */
25422     /**
25423      * @cfg {Boolean} displayInfo
25424      * True to display the displayMsg (defaults to false)
25425      */
25426     /**
25427      * @cfg {Number} pageSize
25428      * The number of records to display per page (defaults to 20)
25429      */
25430     pageSize: 20,
25431     /**
25432      * @cfg {String} displayMsg
25433      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25434      */
25435     displayMsg : 'Displaying {0} - {1} of {2}',
25436     /**
25437      * @cfg {String} emptyMsg
25438      * The message to display when no records are found (defaults to "No data to display")
25439      */
25440     emptyMsg : 'No data to display',
25441     /**
25442      * Customizable piece of the default paging text (defaults to "Page")
25443      * @type String
25444      */
25445     beforePageText : "Page",
25446     /**
25447      * Customizable piece of the default paging text (defaults to "of %0")
25448      * @type String
25449      */
25450     afterPageText : "of {0}",
25451     /**
25452      * Customizable piece of the default paging text (defaults to "First Page")
25453      * @type String
25454      */
25455     firstText : "First Page",
25456     /**
25457      * Customizable piece of the default paging text (defaults to "Previous Page")
25458      * @type String
25459      */
25460     prevText : "Previous Page",
25461     /**
25462      * Customizable piece of the default paging text (defaults to "Next Page")
25463      * @type String
25464      */
25465     nextText : "Next Page",
25466     /**
25467      * Customizable piece of the default paging text (defaults to "Last Page")
25468      * @type String
25469      */
25470     lastText : "Last Page",
25471     /**
25472      * Customizable piece of the default paging text (defaults to "Refresh")
25473      * @type String
25474      */
25475     refreshText : "Refresh",
25476
25477     buttons : false,
25478     // private
25479     onRender : function(ct, position) 
25480     {
25481         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25482         this.navgroup.parentId = this.id;
25483         this.navgroup.onRender(this.el, null);
25484         // add the buttons to the navgroup
25485         
25486         if(this.displayInfo){
25487             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25488             this.displayEl = this.el.select('.x-paging-info', true).first();
25489 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25490 //            this.displayEl = navel.el.select('span',true).first();
25491         }
25492         
25493         var _this = this;
25494         
25495         if(this.buttons){
25496             Roo.each(_this.buttons, function(e){ // this might need to use render????
25497                Roo.factory(e).render(_this.el);
25498             });
25499         }
25500             
25501         Roo.each(_this.toolbarItems, function(e) {
25502             _this.navgroup.addItem(e);
25503         });
25504         
25505         
25506         this.first = this.navgroup.addItem({
25507             tooltip: this.firstText,
25508             cls: "prev btn-outline-secondary",
25509             html : ' <i class="fa fa-step-backward"></i>',
25510             disabled: true,
25511             preventDefault: true,
25512             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25513         });
25514         
25515         this.prev =  this.navgroup.addItem({
25516             tooltip: this.prevText,
25517             cls: "prev btn-outline-secondary",
25518             html : ' <i class="fa fa-backward"></i>',
25519             disabled: true,
25520             preventDefault: true,
25521             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25522         });
25523     //this.addSeparator();
25524         
25525         
25526         var field = this.navgroup.addItem( {
25527             tagtype : 'span',
25528             cls : 'x-paging-position  btn-outline-secondary',
25529              disabled: true,
25530             html : this.beforePageText  +
25531                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25532                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25533          } ); //?? escaped?
25534         
25535         this.field = field.el.select('input', true).first();
25536         this.field.on("keydown", this.onPagingKeydown, this);
25537         this.field.on("focus", function(){this.dom.select();});
25538     
25539     
25540         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25541         //this.field.setHeight(18);
25542         //this.addSeparator();
25543         this.next = this.navgroup.addItem({
25544             tooltip: this.nextText,
25545             cls: "next btn-outline-secondary",
25546             html : ' <i class="fa fa-forward"></i>',
25547             disabled: true,
25548             preventDefault: true,
25549             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25550         });
25551         this.last = this.navgroup.addItem({
25552             tooltip: this.lastText,
25553             html : ' <i class="fa fa-step-forward"></i>',
25554             cls: "next btn-outline-secondary",
25555             disabled: true,
25556             preventDefault: true,
25557             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25558         });
25559     //this.addSeparator();
25560         this.loading = this.navgroup.addItem({
25561             tooltip: this.refreshText,
25562             cls: "btn-outline-secondary",
25563             html : ' <i class="fa fa-refresh"></i>',
25564             preventDefault: true,
25565             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25566         });
25567         
25568     },
25569
25570     // private
25571     updateInfo : function(){
25572         if(this.displayEl){
25573             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25574             var msg = count == 0 ?
25575                 this.emptyMsg :
25576                 String.format(
25577                     this.displayMsg,
25578                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25579                 );
25580             this.displayEl.update(msg);
25581         }
25582     },
25583
25584     // private
25585     onLoad : function(ds, r, o)
25586     {
25587         this.cursor = o.params.start ? o.params.start : 0;
25588         
25589         var d = this.getPageData(),
25590             ap = d.activePage,
25591             ps = d.pages;
25592         
25593         
25594         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25595         this.field.dom.value = ap;
25596         this.first.setDisabled(ap == 1);
25597         this.prev.setDisabled(ap == 1);
25598         this.next.setDisabled(ap == ps);
25599         this.last.setDisabled(ap == ps);
25600         this.loading.enable();
25601         this.updateInfo();
25602     },
25603
25604     // private
25605     getPageData : function(){
25606         var total = this.ds.getTotalCount();
25607         return {
25608             total : total,
25609             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25610             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25611         };
25612     },
25613
25614     // private
25615     onLoadError : function(){
25616         this.loading.enable();
25617     },
25618
25619     // private
25620     onPagingKeydown : function(e){
25621         var k = e.getKey();
25622         var d = this.getPageData();
25623         if(k == e.RETURN){
25624             var v = this.field.dom.value, pageNum;
25625             if(!v || isNaN(pageNum = parseInt(v, 10))){
25626                 this.field.dom.value = d.activePage;
25627                 return;
25628             }
25629             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25630             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25631             e.stopEvent();
25632         }
25633         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))
25634         {
25635           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25636           this.field.dom.value = pageNum;
25637           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25638           e.stopEvent();
25639         }
25640         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25641         {
25642           var v = this.field.dom.value, pageNum; 
25643           var increment = (e.shiftKey) ? 10 : 1;
25644           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25645                 increment *= -1;
25646           }
25647           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25648             this.field.dom.value = d.activePage;
25649             return;
25650           }
25651           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25652           {
25653             this.field.dom.value = parseInt(v, 10) + increment;
25654             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25655             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25656           }
25657           e.stopEvent();
25658         }
25659     },
25660
25661     // private
25662     beforeLoad : function(){
25663         if(this.loading){
25664             this.loading.disable();
25665         }
25666     },
25667
25668     // private
25669     onClick : function(which){
25670         
25671         var ds = this.ds;
25672         if (!ds) {
25673             return;
25674         }
25675         
25676         switch(which){
25677             case "first":
25678                 ds.load({params:{start: 0, limit: this.pageSize}});
25679             break;
25680             case "prev":
25681                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25682             break;
25683             case "next":
25684                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25685             break;
25686             case "last":
25687                 var total = ds.getTotalCount();
25688                 var extra = total % this.pageSize;
25689                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25690                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25691             break;
25692             case "refresh":
25693                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25694             break;
25695         }
25696     },
25697
25698     /**
25699      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25700      * @param {Roo.data.Store} store The data store to unbind
25701      */
25702     unbind : function(ds){
25703         ds.un("beforeload", this.beforeLoad, this);
25704         ds.un("load", this.onLoad, this);
25705         ds.un("loadexception", this.onLoadError, this);
25706         ds.un("remove", this.updateInfo, this);
25707         ds.un("add", this.updateInfo, this);
25708         this.ds = undefined;
25709     },
25710
25711     /**
25712      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25713      * @param {Roo.data.Store} store The data store to bind
25714      */
25715     bind : function(ds){
25716         ds.on("beforeload", this.beforeLoad, this);
25717         ds.on("load", this.onLoad, this);
25718         ds.on("loadexception", this.onLoadError, this);
25719         ds.on("remove", this.updateInfo, this);
25720         ds.on("add", this.updateInfo, this);
25721         this.ds = ds;
25722     }
25723 });/*
25724  * - LGPL
25725  *
25726  * element
25727  * 
25728  */
25729
25730 /**
25731  * @class Roo.bootstrap.MessageBar
25732  * @extends Roo.bootstrap.Component
25733  * Bootstrap MessageBar class
25734  * @cfg {String} html contents of the MessageBar
25735  * @cfg {String} weight (info | success | warning | danger) default info
25736  * @cfg {String} beforeClass insert the bar before the given class
25737  * @cfg {Boolean} closable (true | false) default false
25738  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25739  * 
25740  * @constructor
25741  * Create a new Element
25742  * @param {Object} config The config object
25743  */
25744
25745 Roo.bootstrap.MessageBar = function(config){
25746     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25747 };
25748
25749 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25750     
25751     html: '',
25752     weight: 'info',
25753     closable: false,
25754     fixed: false,
25755     beforeClass: 'bootstrap-sticky-wrap',
25756     
25757     getAutoCreate : function(){
25758         
25759         var cfg = {
25760             tag: 'div',
25761             cls: 'alert alert-dismissable alert-' + this.weight,
25762             cn: [
25763                 {
25764                     tag: 'span',
25765                     cls: 'message',
25766                     html: this.html || ''
25767                 }
25768             ]
25769         };
25770         
25771         if(this.fixed){
25772             cfg.cls += ' alert-messages-fixed';
25773         }
25774         
25775         if(this.closable){
25776             cfg.cn.push({
25777                 tag: 'button',
25778                 cls: 'close',
25779                 html: 'x'
25780             });
25781         }
25782         
25783         return cfg;
25784     },
25785     
25786     onRender : function(ct, position)
25787     {
25788         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25789         
25790         if(!this.el){
25791             var cfg = Roo.apply({},  this.getAutoCreate());
25792             cfg.id = Roo.id();
25793             
25794             if (this.cls) {
25795                 cfg.cls += ' ' + this.cls;
25796             }
25797             if (this.style) {
25798                 cfg.style = this.style;
25799             }
25800             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25801             
25802             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25803         }
25804         
25805         this.el.select('>button.close').on('click', this.hide, this);
25806         
25807     },
25808     
25809     show : function()
25810     {
25811         if (!this.rendered) {
25812             this.render();
25813         }
25814         
25815         this.el.show();
25816         
25817         this.fireEvent('show', this);
25818         
25819     },
25820     
25821     hide : function()
25822     {
25823         if (!this.rendered) {
25824             this.render();
25825         }
25826         
25827         this.el.hide();
25828         
25829         this.fireEvent('hide', this);
25830     },
25831     
25832     update : function()
25833     {
25834 //        var e = this.el.dom.firstChild;
25835 //        
25836 //        if(this.closable){
25837 //            e = e.nextSibling;
25838 //        }
25839 //        
25840 //        e.data = this.html || '';
25841
25842         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25843     }
25844    
25845 });
25846
25847  
25848
25849      /*
25850  * - LGPL
25851  *
25852  * Graph
25853  * 
25854  */
25855
25856
25857 /**
25858  * @class Roo.bootstrap.Graph
25859  * @extends Roo.bootstrap.Component
25860  * Bootstrap Graph class
25861 > Prameters
25862  -sm {number} sm 4
25863  -md {number} md 5
25864  @cfg {String} graphtype  bar | vbar | pie
25865  @cfg {number} g_x coodinator | centre x (pie)
25866  @cfg {number} g_y coodinator | centre y (pie)
25867  @cfg {number} g_r radius (pie)
25868  @cfg {number} g_height height of the chart (respected by all elements in the set)
25869  @cfg {number} g_width width of the chart (respected by all elements in the set)
25870  @cfg {Object} title The title of the chart
25871     
25872  -{Array}  values
25873  -opts (object) options for the chart 
25874      o {
25875      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25876      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25877      o vgutter (number)
25878      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.
25879      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25880      o to
25881      o stretch (boolean)
25882      o }
25883  -opts (object) options for the pie
25884      o{
25885      o cut
25886      o startAngle (number)
25887      o endAngle (number)
25888      } 
25889  *
25890  * @constructor
25891  * Create a new Input
25892  * @param {Object} config The config object
25893  */
25894
25895 Roo.bootstrap.Graph = function(config){
25896     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25897     
25898     this.addEvents({
25899         // img events
25900         /**
25901          * @event click
25902          * The img click event for the img.
25903          * @param {Roo.EventObject} e
25904          */
25905         "click" : true
25906     });
25907 };
25908
25909 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25910     
25911     sm: 4,
25912     md: 5,
25913     graphtype: 'bar',
25914     g_height: 250,
25915     g_width: 400,
25916     g_x: 50,
25917     g_y: 50,
25918     g_r: 30,
25919     opts:{
25920         //g_colors: this.colors,
25921         g_type: 'soft',
25922         g_gutter: '20%'
25923
25924     },
25925     title : false,
25926
25927     getAutoCreate : function(){
25928         
25929         var cfg = {
25930             tag: 'div',
25931             html : null
25932         };
25933         
25934         
25935         return  cfg;
25936     },
25937
25938     onRender : function(ct,position){
25939         
25940         
25941         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25942         
25943         if (typeof(Raphael) == 'undefined') {
25944             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25945             return;
25946         }
25947         
25948         this.raphael = Raphael(this.el.dom);
25949         
25950                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25951                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25952                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25953                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25954                 /*
25955                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25956                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25957                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25958                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25959                 
25960                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25961                 r.barchart(330, 10, 300, 220, data1);
25962                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25963                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25964                 */
25965                 
25966                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25967                 // r.barchart(30, 30, 560, 250,  xdata, {
25968                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25969                 //     axis : "0 0 1 1",
25970                 //     axisxlabels :  xdata
25971                 //     //yvalues : cols,
25972                    
25973                 // });
25974 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25975 //        
25976 //        this.load(null,xdata,{
25977 //                axis : "0 0 1 1",
25978 //                axisxlabels :  xdata
25979 //                });
25980
25981     },
25982
25983     load : function(graphtype,xdata,opts)
25984     {
25985         this.raphael.clear();
25986         if(!graphtype) {
25987             graphtype = this.graphtype;
25988         }
25989         if(!opts){
25990             opts = this.opts;
25991         }
25992         var r = this.raphael,
25993             fin = function () {
25994                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25995             },
25996             fout = function () {
25997                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25998             },
25999             pfin = function() {
26000                 this.sector.stop();
26001                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26002
26003                 if (this.label) {
26004                     this.label[0].stop();
26005                     this.label[0].attr({ r: 7.5 });
26006                     this.label[1].attr({ "font-weight": 800 });
26007                 }
26008             },
26009             pfout = function() {
26010                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26011
26012                 if (this.label) {
26013                     this.label[0].animate({ r: 5 }, 500, "bounce");
26014                     this.label[1].attr({ "font-weight": 400 });
26015                 }
26016             };
26017
26018         switch(graphtype){
26019             case 'bar':
26020                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26021                 break;
26022             case 'hbar':
26023                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26024                 break;
26025             case 'pie':
26026 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26027 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26028 //            
26029                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26030                 
26031                 break;
26032
26033         }
26034         
26035         if(this.title){
26036             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26037         }
26038         
26039     },
26040     
26041     setTitle: function(o)
26042     {
26043         this.title = o;
26044     },
26045     
26046     initEvents: function() {
26047         
26048         if(!this.href){
26049             this.el.on('click', this.onClick, this);
26050         }
26051     },
26052     
26053     onClick : function(e)
26054     {
26055         Roo.log('img onclick');
26056         this.fireEvent('click', this, e);
26057     }
26058    
26059 });
26060
26061  
26062 /*
26063  * - LGPL
26064  *
26065  * numberBox
26066  * 
26067  */
26068 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26069
26070 /**
26071  * @class Roo.bootstrap.dash.NumberBox
26072  * @extends Roo.bootstrap.Component
26073  * Bootstrap NumberBox class
26074  * @cfg {String} headline Box headline
26075  * @cfg {String} content Box content
26076  * @cfg {String} icon Box icon
26077  * @cfg {String} footer Footer text
26078  * @cfg {String} fhref Footer href
26079  * 
26080  * @constructor
26081  * Create a new NumberBox
26082  * @param {Object} config The config object
26083  */
26084
26085
26086 Roo.bootstrap.dash.NumberBox = function(config){
26087     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26088     
26089 };
26090
26091 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26092     
26093     headline : '',
26094     content : '',
26095     icon : '',
26096     footer : '',
26097     fhref : '',
26098     ficon : '',
26099     
26100     getAutoCreate : function(){
26101         
26102         var cfg = {
26103             tag : 'div',
26104             cls : 'small-box ',
26105             cn : [
26106                 {
26107                     tag : 'div',
26108                     cls : 'inner',
26109                     cn :[
26110                         {
26111                             tag : 'h3',
26112                             cls : 'roo-headline',
26113                             html : this.headline
26114                         },
26115                         {
26116                             tag : 'p',
26117                             cls : 'roo-content',
26118                             html : this.content
26119                         }
26120                     ]
26121                 }
26122             ]
26123         };
26124         
26125         if(this.icon){
26126             cfg.cn.push({
26127                 tag : 'div',
26128                 cls : 'icon',
26129                 cn :[
26130                     {
26131                         tag : 'i',
26132                         cls : 'ion ' + this.icon
26133                     }
26134                 ]
26135             });
26136         }
26137         
26138         if(this.footer){
26139             var footer = {
26140                 tag : 'a',
26141                 cls : 'small-box-footer',
26142                 href : this.fhref || '#',
26143                 html : this.footer
26144             };
26145             
26146             cfg.cn.push(footer);
26147             
26148         }
26149         
26150         return  cfg;
26151     },
26152
26153     onRender : function(ct,position){
26154         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26155
26156
26157        
26158                 
26159     },
26160
26161     setHeadline: function (value)
26162     {
26163         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26164     },
26165     
26166     setFooter: function (value, href)
26167     {
26168         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26169         
26170         if(href){
26171             this.el.select('a.small-box-footer',true).first().attr('href', href);
26172         }
26173         
26174     },
26175
26176     setContent: function (value)
26177     {
26178         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26179     },
26180
26181     initEvents: function() 
26182     {   
26183         
26184     }
26185     
26186 });
26187
26188  
26189 /*
26190  * - LGPL
26191  *
26192  * TabBox
26193  * 
26194  */
26195 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26196
26197 /**
26198  * @class Roo.bootstrap.dash.TabBox
26199  * @extends Roo.bootstrap.Component
26200  * Bootstrap TabBox class
26201  * @cfg {String} title Title of the TabBox
26202  * @cfg {String} icon Icon of the TabBox
26203  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26204  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26205  * 
26206  * @constructor
26207  * Create a new TabBox
26208  * @param {Object} config The config object
26209  */
26210
26211
26212 Roo.bootstrap.dash.TabBox = function(config){
26213     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26214     this.addEvents({
26215         // raw events
26216         /**
26217          * @event addpane
26218          * When a pane is added
26219          * @param {Roo.bootstrap.dash.TabPane} pane
26220          */
26221         "addpane" : true,
26222         /**
26223          * @event activatepane
26224          * When a pane is activated
26225          * @param {Roo.bootstrap.dash.TabPane} pane
26226          */
26227         "activatepane" : true
26228         
26229          
26230     });
26231     
26232     this.panes = [];
26233 };
26234
26235 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26236
26237     title : '',
26238     icon : false,
26239     showtabs : true,
26240     tabScrollable : false,
26241     
26242     getChildContainer : function()
26243     {
26244         return this.el.select('.tab-content', true).first();
26245     },
26246     
26247     getAutoCreate : function(){
26248         
26249         var header = {
26250             tag: 'li',
26251             cls: 'pull-left header',
26252             html: this.title,
26253             cn : []
26254         };
26255         
26256         if(this.icon){
26257             header.cn.push({
26258                 tag: 'i',
26259                 cls: 'fa ' + this.icon
26260             });
26261         }
26262         
26263         var h = {
26264             tag: 'ul',
26265             cls: 'nav nav-tabs pull-right',
26266             cn: [
26267                 header
26268             ]
26269         };
26270         
26271         if(this.tabScrollable){
26272             h = {
26273                 tag: 'div',
26274                 cls: 'tab-header',
26275                 cn: [
26276                     {
26277                         tag: 'ul',
26278                         cls: 'nav nav-tabs pull-right',
26279                         cn: [
26280                             header
26281                         ]
26282                     }
26283                 ]
26284             };
26285         }
26286         
26287         var cfg = {
26288             tag: 'div',
26289             cls: 'nav-tabs-custom',
26290             cn: [
26291                 h,
26292                 {
26293                     tag: 'div',
26294                     cls: 'tab-content no-padding',
26295                     cn: []
26296                 }
26297             ]
26298         };
26299
26300         return  cfg;
26301     },
26302     initEvents : function()
26303     {
26304         //Roo.log('add add pane handler');
26305         this.on('addpane', this.onAddPane, this);
26306     },
26307      /**
26308      * Updates the box title
26309      * @param {String} html to set the title to.
26310      */
26311     setTitle : function(value)
26312     {
26313         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26314     },
26315     onAddPane : function(pane)
26316     {
26317         this.panes.push(pane);
26318         //Roo.log('addpane');
26319         //Roo.log(pane);
26320         // tabs are rendere left to right..
26321         if(!this.showtabs){
26322             return;
26323         }
26324         
26325         var ctr = this.el.select('.nav-tabs', true).first();
26326          
26327          
26328         var existing = ctr.select('.nav-tab',true);
26329         var qty = existing.getCount();;
26330         
26331         
26332         var tab = ctr.createChild({
26333             tag : 'li',
26334             cls : 'nav-tab' + (qty ? '' : ' active'),
26335             cn : [
26336                 {
26337                     tag : 'a',
26338                     href:'#',
26339                     html : pane.title
26340                 }
26341             ]
26342         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26343         pane.tab = tab;
26344         
26345         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26346         if (!qty) {
26347             pane.el.addClass('active');
26348         }
26349         
26350                 
26351     },
26352     onTabClick : function(ev,un,ob,pane)
26353     {
26354         //Roo.log('tab - prev default');
26355         ev.preventDefault();
26356         
26357         
26358         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26359         pane.tab.addClass('active');
26360         //Roo.log(pane.title);
26361         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26362         // technically we should have a deactivate event.. but maybe add later.
26363         // and it should not de-activate the selected tab...
26364         this.fireEvent('activatepane', pane);
26365         pane.el.addClass('active');
26366         pane.fireEvent('activate');
26367         
26368         
26369     },
26370     
26371     getActivePane : function()
26372     {
26373         var r = false;
26374         Roo.each(this.panes, function(p) {
26375             if(p.el.hasClass('active')){
26376                 r = p;
26377                 return false;
26378             }
26379             
26380             return;
26381         });
26382         
26383         return r;
26384     }
26385     
26386     
26387 });
26388
26389  
26390 /*
26391  * - LGPL
26392  *
26393  * Tab pane
26394  * 
26395  */
26396 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26397 /**
26398  * @class Roo.bootstrap.TabPane
26399  * @extends Roo.bootstrap.Component
26400  * Bootstrap TabPane class
26401  * @cfg {Boolean} active (false | true) Default false
26402  * @cfg {String} title title of panel
26403
26404  * 
26405  * @constructor
26406  * Create a new TabPane
26407  * @param {Object} config The config object
26408  */
26409
26410 Roo.bootstrap.dash.TabPane = function(config){
26411     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26412     
26413     this.addEvents({
26414         // raw events
26415         /**
26416          * @event activate
26417          * When a pane is activated
26418          * @param {Roo.bootstrap.dash.TabPane} pane
26419          */
26420         "activate" : true
26421          
26422     });
26423 };
26424
26425 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26426     
26427     active : false,
26428     title : '',
26429     
26430     // the tabBox that this is attached to.
26431     tab : false,
26432      
26433     getAutoCreate : function() 
26434     {
26435         var cfg = {
26436             tag: 'div',
26437             cls: 'tab-pane'
26438         };
26439         
26440         if(this.active){
26441             cfg.cls += ' active';
26442         }
26443         
26444         return cfg;
26445     },
26446     initEvents  : function()
26447     {
26448         //Roo.log('trigger add pane handler');
26449         this.parent().fireEvent('addpane', this)
26450     },
26451     
26452      /**
26453      * Updates the tab title 
26454      * @param {String} html to set the title to.
26455      */
26456     setTitle: function(str)
26457     {
26458         if (!this.tab) {
26459             return;
26460         }
26461         this.title = str;
26462         this.tab.select('a', true).first().dom.innerHTML = str;
26463         
26464     }
26465     
26466     
26467     
26468 });
26469
26470  
26471
26472
26473  /*
26474  * - LGPL
26475  *
26476  * menu
26477  * 
26478  */
26479 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26480
26481 /**
26482  * @class Roo.bootstrap.menu.Menu
26483  * @extends Roo.bootstrap.Component
26484  * Bootstrap Menu class - container for Menu
26485  * @cfg {String} html Text of the menu
26486  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26487  * @cfg {String} icon Font awesome icon
26488  * @cfg {String} pos Menu align to (top | bottom) default bottom
26489  * 
26490  * 
26491  * @constructor
26492  * Create a new Menu
26493  * @param {Object} config The config object
26494  */
26495
26496
26497 Roo.bootstrap.menu.Menu = function(config){
26498     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26499     
26500     this.addEvents({
26501         /**
26502          * @event beforeshow
26503          * Fires before this menu is displayed
26504          * @param {Roo.bootstrap.menu.Menu} this
26505          */
26506         beforeshow : true,
26507         /**
26508          * @event beforehide
26509          * Fires before this menu is hidden
26510          * @param {Roo.bootstrap.menu.Menu} this
26511          */
26512         beforehide : true,
26513         /**
26514          * @event show
26515          * Fires after this menu is displayed
26516          * @param {Roo.bootstrap.menu.Menu} this
26517          */
26518         show : true,
26519         /**
26520          * @event hide
26521          * Fires after this menu is hidden
26522          * @param {Roo.bootstrap.menu.Menu} this
26523          */
26524         hide : true,
26525         /**
26526          * @event click
26527          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26528          * @param {Roo.bootstrap.menu.Menu} this
26529          * @param {Roo.EventObject} e
26530          */
26531         click : true
26532     });
26533     
26534 };
26535
26536 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26537     
26538     submenu : false,
26539     html : '',
26540     weight : 'default',
26541     icon : false,
26542     pos : 'bottom',
26543     
26544     
26545     getChildContainer : function() {
26546         if(this.isSubMenu){
26547             return this.el;
26548         }
26549         
26550         return this.el.select('ul.dropdown-menu', true).first();  
26551     },
26552     
26553     getAutoCreate : function()
26554     {
26555         var text = [
26556             {
26557                 tag : 'span',
26558                 cls : 'roo-menu-text',
26559                 html : this.html
26560             }
26561         ];
26562         
26563         if(this.icon){
26564             text.unshift({
26565                 tag : 'i',
26566                 cls : 'fa ' + this.icon
26567             })
26568         }
26569         
26570         
26571         var cfg = {
26572             tag : 'div',
26573             cls : 'btn-group',
26574             cn : [
26575                 {
26576                     tag : 'button',
26577                     cls : 'dropdown-button btn btn-' + this.weight,
26578                     cn : text
26579                 },
26580                 {
26581                     tag : 'button',
26582                     cls : 'dropdown-toggle btn btn-' + this.weight,
26583                     cn : [
26584                         {
26585                             tag : 'span',
26586                             cls : 'caret'
26587                         }
26588                     ]
26589                 },
26590                 {
26591                     tag : 'ul',
26592                     cls : 'dropdown-menu'
26593                 }
26594             ]
26595             
26596         };
26597         
26598         if(this.pos == 'top'){
26599             cfg.cls += ' dropup';
26600         }
26601         
26602         if(this.isSubMenu){
26603             cfg = {
26604                 tag : 'ul',
26605                 cls : 'dropdown-menu'
26606             }
26607         }
26608         
26609         return cfg;
26610     },
26611     
26612     onRender : function(ct, position)
26613     {
26614         this.isSubMenu = ct.hasClass('dropdown-submenu');
26615         
26616         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26617     },
26618     
26619     initEvents : function() 
26620     {
26621         if(this.isSubMenu){
26622             return;
26623         }
26624         
26625         this.hidden = true;
26626         
26627         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26628         this.triggerEl.on('click', this.onTriggerPress, this);
26629         
26630         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26631         this.buttonEl.on('click', this.onClick, this);
26632         
26633     },
26634     
26635     list : function()
26636     {
26637         if(this.isSubMenu){
26638             return this.el;
26639         }
26640         
26641         return this.el.select('ul.dropdown-menu', true).first();
26642     },
26643     
26644     onClick : function(e)
26645     {
26646         this.fireEvent("click", this, e);
26647     },
26648     
26649     onTriggerPress  : function(e)
26650     {   
26651         if (this.isVisible()) {
26652             this.hide();
26653         } else {
26654             this.show();
26655         }
26656     },
26657     
26658     isVisible : function(){
26659         return !this.hidden;
26660     },
26661     
26662     show : function()
26663     {
26664         this.fireEvent("beforeshow", this);
26665         
26666         this.hidden = false;
26667         this.el.addClass('open');
26668         
26669         Roo.get(document).on("mouseup", this.onMouseUp, this);
26670         
26671         this.fireEvent("show", this);
26672         
26673         
26674     },
26675     
26676     hide : function()
26677     {
26678         this.fireEvent("beforehide", this);
26679         
26680         this.hidden = true;
26681         this.el.removeClass('open');
26682         
26683         Roo.get(document).un("mouseup", this.onMouseUp);
26684         
26685         this.fireEvent("hide", this);
26686     },
26687     
26688     onMouseUp : function()
26689     {
26690         this.hide();
26691     }
26692     
26693 });
26694
26695  
26696  /*
26697  * - LGPL
26698  *
26699  * menu item
26700  * 
26701  */
26702 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26703
26704 /**
26705  * @class Roo.bootstrap.menu.Item
26706  * @extends Roo.bootstrap.Component
26707  * Bootstrap MenuItem class
26708  * @cfg {Boolean} submenu (true | false) default false
26709  * @cfg {String} html text of the item
26710  * @cfg {String} href the link
26711  * @cfg {Boolean} disable (true | false) default false
26712  * @cfg {Boolean} preventDefault (true | false) default true
26713  * @cfg {String} icon Font awesome icon
26714  * @cfg {String} pos Submenu align to (left | right) default right 
26715  * 
26716  * 
26717  * @constructor
26718  * Create a new Item
26719  * @param {Object} config The config object
26720  */
26721
26722
26723 Roo.bootstrap.menu.Item = function(config){
26724     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26725     this.addEvents({
26726         /**
26727          * @event mouseover
26728          * Fires when the mouse is hovering over this menu
26729          * @param {Roo.bootstrap.menu.Item} this
26730          * @param {Roo.EventObject} e
26731          */
26732         mouseover : true,
26733         /**
26734          * @event mouseout
26735          * Fires when the mouse exits this menu
26736          * @param {Roo.bootstrap.menu.Item} this
26737          * @param {Roo.EventObject} e
26738          */
26739         mouseout : true,
26740         // raw events
26741         /**
26742          * @event click
26743          * The raw click event for the entire grid.
26744          * @param {Roo.EventObject} e
26745          */
26746         click : true
26747     });
26748 };
26749
26750 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26751     
26752     submenu : false,
26753     href : '',
26754     html : '',
26755     preventDefault: true,
26756     disable : false,
26757     icon : false,
26758     pos : 'right',
26759     
26760     getAutoCreate : function()
26761     {
26762         var text = [
26763             {
26764                 tag : 'span',
26765                 cls : 'roo-menu-item-text',
26766                 html : this.html
26767             }
26768         ];
26769         
26770         if(this.icon){
26771             text.unshift({
26772                 tag : 'i',
26773                 cls : 'fa ' + this.icon
26774             })
26775         }
26776         
26777         var cfg = {
26778             tag : 'li',
26779             cn : [
26780                 {
26781                     tag : 'a',
26782                     href : this.href || '#',
26783                     cn : text
26784                 }
26785             ]
26786         };
26787         
26788         if(this.disable){
26789             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26790         }
26791         
26792         if(this.submenu){
26793             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26794             
26795             if(this.pos == 'left'){
26796                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26797             }
26798         }
26799         
26800         return cfg;
26801     },
26802     
26803     initEvents : function() 
26804     {
26805         this.el.on('mouseover', this.onMouseOver, this);
26806         this.el.on('mouseout', this.onMouseOut, this);
26807         
26808         this.el.select('a', true).first().on('click', this.onClick, this);
26809         
26810     },
26811     
26812     onClick : function(e)
26813     {
26814         if(this.preventDefault){
26815             e.preventDefault();
26816         }
26817         
26818         this.fireEvent("click", this, e);
26819     },
26820     
26821     onMouseOver : function(e)
26822     {
26823         if(this.submenu && this.pos == 'left'){
26824             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26825         }
26826         
26827         this.fireEvent("mouseover", this, e);
26828     },
26829     
26830     onMouseOut : function(e)
26831     {
26832         this.fireEvent("mouseout", this, e);
26833     }
26834 });
26835
26836  
26837
26838  /*
26839  * - LGPL
26840  *
26841  * menu separator
26842  * 
26843  */
26844 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26845
26846 /**
26847  * @class Roo.bootstrap.menu.Separator
26848  * @extends Roo.bootstrap.Component
26849  * Bootstrap Separator class
26850  * 
26851  * @constructor
26852  * Create a new Separator
26853  * @param {Object} config The config object
26854  */
26855
26856
26857 Roo.bootstrap.menu.Separator = function(config){
26858     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26859 };
26860
26861 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26862     
26863     getAutoCreate : function(){
26864         var cfg = {
26865             tag : 'li',
26866             cls: 'divider'
26867         };
26868         
26869         return cfg;
26870     }
26871    
26872 });
26873
26874  
26875
26876  /*
26877  * - LGPL
26878  *
26879  * Tooltip
26880  * 
26881  */
26882
26883 /**
26884  * @class Roo.bootstrap.Tooltip
26885  * Bootstrap Tooltip class
26886  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26887  * to determine which dom element triggers the tooltip.
26888  * 
26889  * It needs to add support for additional attributes like tooltip-position
26890  * 
26891  * @constructor
26892  * Create a new Toolti
26893  * @param {Object} config The config object
26894  */
26895
26896 Roo.bootstrap.Tooltip = function(config){
26897     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26898     
26899     this.alignment = Roo.bootstrap.Tooltip.alignment;
26900     
26901     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26902         this.alignment = config.alignment;
26903     }
26904     
26905 };
26906
26907 Roo.apply(Roo.bootstrap.Tooltip, {
26908     /**
26909      * @function init initialize tooltip monitoring.
26910      * @static
26911      */
26912     currentEl : false,
26913     currentTip : false,
26914     currentRegion : false,
26915     
26916     //  init : delay?
26917     
26918     init : function()
26919     {
26920         Roo.get(document).on('mouseover', this.enter ,this);
26921         Roo.get(document).on('mouseout', this.leave, this);
26922          
26923         
26924         this.currentTip = new Roo.bootstrap.Tooltip();
26925     },
26926     
26927     enter : function(ev)
26928     {
26929         var dom = ev.getTarget();
26930         
26931         //Roo.log(['enter',dom]);
26932         var el = Roo.fly(dom);
26933         if (this.currentEl) {
26934             //Roo.log(dom);
26935             //Roo.log(this.currentEl);
26936             //Roo.log(this.currentEl.contains(dom));
26937             if (this.currentEl == el) {
26938                 return;
26939             }
26940             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26941                 return;
26942             }
26943
26944         }
26945         
26946         if (this.currentTip.el) {
26947             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26948         }    
26949         //Roo.log(ev);
26950         
26951         if(!el || el.dom == document){
26952             return;
26953         }
26954         
26955         var bindEl = el;
26956         
26957         // you can not look for children, as if el is the body.. then everythign is the child..
26958         if (!el.attr('tooltip')) { //
26959             if (!el.select("[tooltip]").elements.length) {
26960                 return;
26961             }
26962             // is the mouse over this child...?
26963             bindEl = el.select("[tooltip]").first();
26964             var xy = ev.getXY();
26965             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26966                 //Roo.log("not in region.");
26967                 return;
26968             }
26969             //Roo.log("child element over..");
26970             
26971         }
26972         this.currentEl = bindEl;
26973         this.currentTip.bind(bindEl);
26974         this.currentRegion = Roo.lib.Region.getRegion(dom);
26975         this.currentTip.enter();
26976         
26977     },
26978     leave : function(ev)
26979     {
26980         var dom = ev.getTarget();
26981         //Roo.log(['leave',dom]);
26982         if (!this.currentEl) {
26983             return;
26984         }
26985         
26986         
26987         if (dom != this.currentEl.dom) {
26988             return;
26989         }
26990         var xy = ev.getXY();
26991         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26992             return;
26993         }
26994         // only activate leave if mouse cursor is outside... bounding box..
26995         
26996         
26997         
26998         
26999         if (this.currentTip) {
27000             this.currentTip.leave();
27001         }
27002         //Roo.log('clear currentEl');
27003         this.currentEl = false;
27004         
27005         
27006     },
27007     alignment : {
27008         'left' : ['r-l', [-2,0], 'right'],
27009         'right' : ['l-r', [2,0], 'left'],
27010         'bottom' : ['t-b', [0,2], 'top'],
27011         'top' : [ 'b-t', [0,-2], 'bottom']
27012     }
27013     
27014 });
27015
27016
27017 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27018     
27019     
27020     bindEl : false,
27021     
27022     delay : null, // can be { show : 300 , hide: 500}
27023     
27024     timeout : null,
27025     
27026     hoverState : null, //???
27027     
27028     placement : 'bottom', 
27029     
27030     alignment : false,
27031     
27032     getAutoCreate : function(){
27033     
27034         var cfg = {
27035            cls : 'tooltip',
27036            role : 'tooltip',
27037            cn : [
27038                 {
27039                     cls : 'tooltip-arrow'
27040                 },
27041                 {
27042                     cls : 'tooltip-inner'
27043                 }
27044            ]
27045         };
27046         
27047         return cfg;
27048     },
27049     bind : function(el)
27050     {
27051         this.bindEl = el;
27052     },
27053       
27054     
27055     enter : function () {
27056        
27057         if (this.timeout != null) {
27058             clearTimeout(this.timeout);
27059         }
27060         
27061         this.hoverState = 'in';
27062          //Roo.log("enter - show");
27063         if (!this.delay || !this.delay.show) {
27064             this.show();
27065             return;
27066         }
27067         var _t = this;
27068         this.timeout = setTimeout(function () {
27069             if (_t.hoverState == 'in') {
27070                 _t.show();
27071             }
27072         }, this.delay.show);
27073     },
27074     leave : function()
27075     {
27076         clearTimeout(this.timeout);
27077     
27078         this.hoverState = 'out';
27079          if (!this.delay || !this.delay.hide) {
27080             this.hide();
27081             return;
27082         }
27083        
27084         var _t = this;
27085         this.timeout = setTimeout(function () {
27086             //Roo.log("leave - timeout");
27087             
27088             if (_t.hoverState == 'out') {
27089                 _t.hide();
27090                 Roo.bootstrap.Tooltip.currentEl = false;
27091             }
27092         }, delay);
27093     },
27094     
27095     show : function (msg)
27096     {
27097         if (!this.el) {
27098             this.render(document.body);
27099         }
27100         // set content.
27101         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27102         
27103         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27104         
27105         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27106         
27107         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27108         
27109         var placement = typeof this.placement == 'function' ?
27110             this.placement.call(this, this.el, on_el) :
27111             this.placement;
27112             
27113         var autoToken = /\s?auto?\s?/i;
27114         var autoPlace = autoToken.test(placement);
27115         if (autoPlace) {
27116             placement = placement.replace(autoToken, '') || 'top';
27117         }
27118         
27119         //this.el.detach()
27120         //this.el.setXY([0,0]);
27121         this.el.show();
27122         //this.el.dom.style.display='block';
27123         
27124         //this.el.appendTo(on_el);
27125         
27126         var p = this.getPosition();
27127         var box = this.el.getBox();
27128         
27129         if (autoPlace) {
27130             // fixme..
27131         }
27132         
27133         var align = this.alignment[placement];
27134         
27135         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27136         
27137         if(placement == 'top' || placement == 'bottom'){
27138             if(xy[0] < 0){
27139                 placement = 'right';
27140             }
27141             
27142             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27143                 placement = 'left';
27144             }
27145             
27146             var scroll = Roo.select('body', true).first().getScroll();
27147             
27148             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27149                 placement = 'top';
27150             }
27151             
27152             align = this.alignment[placement];
27153         }
27154         
27155         this.el.alignTo(this.bindEl, align[0],align[1]);
27156         //var arrow = this.el.select('.arrow',true).first();
27157         //arrow.set(align[2], 
27158         
27159         this.el.addClass(placement);
27160         
27161         this.el.addClass('in fade');
27162         
27163         this.hoverState = null;
27164         
27165         if (this.el.hasClass('fade')) {
27166             // fade it?
27167         }
27168         
27169     },
27170     hide : function()
27171     {
27172          
27173         if (!this.el) {
27174             return;
27175         }
27176         //this.el.setXY([0,0]);
27177         this.el.removeClass('in');
27178         //this.el.hide();
27179         
27180     }
27181     
27182 });
27183  
27184
27185  /*
27186  * - LGPL
27187  *
27188  * Location Picker
27189  * 
27190  */
27191
27192 /**
27193  * @class Roo.bootstrap.LocationPicker
27194  * @extends Roo.bootstrap.Component
27195  * Bootstrap LocationPicker class
27196  * @cfg {Number} latitude Position when init default 0
27197  * @cfg {Number} longitude Position when init default 0
27198  * @cfg {Number} zoom default 15
27199  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27200  * @cfg {Boolean} mapTypeControl default false
27201  * @cfg {Boolean} disableDoubleClickZoom default false
27202  * @cfg {Boolean} scrollwheel default true
27203  * @cfg {Boolean} streetViewControl default false
27204  * @cfg {Number} radius default 0
27205  * @cfg {String} locationName
27206  * @cfg {Boolean} draggable default true
27207  * @cfg {Boolean} enableAutocomplete default false
27208  * @cfg {Boolean} enableReverseGeocode default true
27209  * @cfg {String} markerTitle
27210  * 
27211  * @constructor
27212  * Create a new LocationPicker
27213  * @param {Object} config The config object
27214  */
27215
27216
27217 Roo.bootstrap.LocationPicker = function(config){
27218     
27219     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27220     
27221     this.addEvents({
27222         /**
27223          * @event initial
27224          * Fires when the picker initialized.
27225          * @param {Roo.bootstrap.LocationPicker} this
27226          * @param {Google Location} location
27227          */
27228         initial : true,
27229         /**
27230          * @event positionchanged
27231          * Fires when the picker position changed.
27232          * @param {Roo.bootstrap.LocationPicker} this
27233          * @param {Google Location} location
27234          */
27235         positionchanged : true,
27236         /**
27237          * @event resize
27238          * Fires when the map resize.
27239          * @param {Roo.bootstrap.LocationPicker} this
27240          */
27241         resize : true,
27242         /**
27243          * @event show
27244          * Fires when the map show.
27245          * @param {Roo.bootstrap.LocationPicker} this
27246          */
27247         show : true,
27248         /**
27249          * @event hide
27250          * Fires when the map hide.
27251          * @param {Roo.bootstrap.LocationPicker} this
27252          */
27253         hide : true,
27254         /**
27255          * @event mapClick
27256          * Fires when click the map.
27257          * @param {Roo.bootstrap.LocationPicker} this
27258          * @param {Map event} e
27259          */
27260         mapClick : true,
27261         /**
27262          * @event mapRightClick
27263          * Fires when right click the map.
27264          * @param {Roo.bootstrap.LocationPicker} this
27265          * @param {Map event} e
27266          */
27267         mapRightClick : true,
27268         /**
27269          * @event markerClick
27270          * Fires when click the marker.
27271          * @param {Roo.bootstrap.LocationPicker} this
27272          * @param {Map event} e
27273          */
27274         markerClick : true,
27275         /**
27276          * @event markerRightClick
27277          * Fires when right click the marker.
27278          * @param {Roo.bootstrap.LocationPicker} this
27279          * @param {Map event} e
27280          */
27281         markerRightClick : true,
27282         /**
27283          * @event OverlayViewDraw
27284          * Fires when OverlayView Draw
27285          * @param {Roo.bootstrap.LocationPicker} this
27286          */
27287         OverlayViewDraw : true,
27288         /**
27289          * @event OverlayViewOnAdd
27290          * Fires when OverlayView Draw
27291          * @param {Roo.bootstrap.LocationPicker} this
27292          */
27293         OverlayViewOnAdd : true,
27294         /**
27295          * @event OverlayViewOnRemove
27296          * Fires when OverlayView Draw
27297          * @param {Roo.bootstrap.LocationPicker} this
27298          */
27299         OverlayViewOnRemove : true,
27300         /**
27301          * @event OverlayViewShow
27302          * Fires when OverlayView Draw
27303          * @param {Roo.bootstrap.LocationPicker} this
27304          * @param {Pixel} cpx
27305          */
27306         OverlayViewShow : true,
27307         /**
27308          * @event OverlayViewHide
27309          * Fires when OverlayView Draw
27310          * @param {Roo.bootstrap.LocationPicker} this
27311          */
27312         OverlayViewHide : true,
27313         /**
27314          * @event loadexception
27315          * Fires when load google lib failed.
27316          * @param {Roo.bootstrap.LocationPicker} this
27317          */
27318         loadexception : true
27319     });
27320         
27321 };
27322
27323 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27324     
27325     gMapContext: false,
27326     
27327     latitude: 0,
27328     longitude: 0,
27329     zoom: 15,
27330     mapTypeId: false,
27331     mapTypeControl: false,
27332     disableDoubleClickZoom: false,
27333     scrollwheel: true,
27334     streetViewControl: false,
27335     radius: 0,
27336     locationName: '',
27337     draggable: true,
27338     enableAutocomplete: false,
27339     enableReverseGeocode: true,
27340     markerTitle: '',
27341     
27342     getAutoCreate: function()
27343     {
27344
27345         var cfg = {
27346             tag: 'div',
27347             cls: 'roo-location-picker'
27348         };
27349         
27350         return cfg
27351     },
27352     
27353     initEvents: function(ct, position)
27354     {       
27355         if(!this.el.getWidth() || this.isApplied()){
27356             return;
27357         }
27358         
27359         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27360         
27361         this.initial();
27362     },
27363     
27364     initial: function()
27365     {
27366         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27367             this.fireEvent('loadexception', this);
27368             return;
27369         }
27370         
27371         if(!this.mapTypeId){
27372             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27373         }
27374         
27375         this.gMapContext = this.GMapContext();
27376         
27377         this.initOverlayView();
27378         
27379         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27380         
27381         var _this = this;
27382                 
27383         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27384             _this.setPosition(_this.gMapContext.marker.position);
27385         });
27386         
27387         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27388             _this.fireEvent('mapClick', this, event);
27389             
27390         });
27391
27392         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27393             _this.fireEvent('mapRightClick', this, event);
27394             
27395         });
27396         
27397         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27398             _this.fireEvent('markerClick', this, event);
27399             
27400         });
27401
27402         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27403             _this.fireEvent('markerRightClick', this, event);
27404             
27405         });
27406         
27407         this.setPosition(this.gMapContext.location);
27408         
27409         this.fireEvent('initial', this, this.gMapContext.location);
27410     },
27411     
27412     initOverlayView: function()
27413     {
27414         var _this = this;
27415         
27416         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27417             
27418             draw: function()
27419             {
27420                 _this.fireEvent('OverlayViewDraw', _this);
27421             },
27422             
27423             onAdd: function()
27424             {
27425                 _this.fireEvent('OverlayViewOnAdd', _this);
27426             },
27427             
27428             onRemove: function()
27429             {
27430                 _this.fireEvent('OverlayViewOnRemove', _this);
27431             },
27432             
27433             show: function(cpx)
27434             {
27435                 _this.fireEvent('OverlayViewShow', _this, cpx);
27436             },
27437             
27438             hide: function()
27439             {
27440                 _this.fireEvent('OverlayViewHide', _this);
27441             }
27442             
27443         });
27444     },
27445     
27446     fromLatLngToContainerPixel: function(event)
27447     {
27448         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27449     },
27450     
27451     isApplied: function() 
27452     {
27453         return this.getGmapContext() == false ? false : true;
27454     },
27455     
27456     getGmapContext: function() 
27457     {
27458         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27459     },
27460     
27461     GMapContext: function() 
27462     {
27463         var position = new google.maps.LatLng(this.latitude, this.longitude);
27464         
27465         var _map = new google.maps.Map(this.el.dom, {
27466             center: position,
27467             zoom: this.zoom,
27468             mapTypeId: this.mapTypeId,
27469             mapTypeControl: this.mapTypeControl,
27470             disableDoubleClickZoom: this.disableDoubleClickZoom,
27471             scrollwheel: this.scrollwheel,
27472             streetViewControl: this.streetViewControl,
27473             locationName: this.locationName,
27474             draggable: this.draggable,
27475             enableAutocomplete: this.enableAutocomplete,
27476             enableReverseGeocode: this.enableReverseGeocode
27477         });
27478         
27479         var _marker = new google.maps.Marker({
27480             position: position,
27481             map: _map,
27482             title: this.markerTitle,
27483             draggable: this.draggable
27484         });
27485         
27486         return {
27487             map: _map,
27488             marker: _marker,
27489             circle: null,
27490             location: position,
27491             radius: this.radius,
27492             locationName: this.locationName,
27493             addressComponents: {
27494                 formatted_address: null,
27495                 addressLine1: null,
27496                 addressLine2: null,
27497                 streetName: null,
27498                 streetNumber: null,
27499                 city: null,
27500                 district: null,
27501                 state: null,
27502                 stateOrProvince: null
27503             },
27504             settings: this,
27505             domContainer: this.el.dom,
27506             geodecoder: new google.maps.Geocoder()
27507         };
27508     },
27509     
27510     drawCircle: function(center, radius, options) 
27511     {
27512         if (this.gMapContext.circle != null) {
27513             this.gMapContext.circle.setMap(null);
27514         }
27515         if (radius > 0) {
27516             radius *= 1;
27517             options = Roo.apply({}, options, {
27518                 strokeColor: "#0000FF",
27519                 strokeOpacity: .35,
27520                 strokeWeight: 2,
27521                 fillColor: "#0000FF",
27522                 fillOpacity: .2
27523             });
27524             
27525             options.map = this.gMapContext.map;
27526             options.radius = radius;
27527             options.center = center;
27528             this.gMapContext.circle = new google.maps.Circle(options);
27529             return this.gMapContext.circle;
27530         }
27531         
27532         return null;
27533     },
27534     
27535     setPosition: function(location) 
27536     {
27537         this.gMapContext.location = location;
27538         this.gMapContext.marker.setPosition(location);
27539         this.gMapContext.map.panTo(location);
27540         this.drawCircle(location, this.gMapContext.radius, {});
27541         
27542         var _this = this;
27543         
27544         if (this.gMapContext.settings.enableReverseGeocode) {
27545             this.gMapContext.geodecoder.geocode({
27546                 latLng: this.gMapContext.location
27547             }, function(results, status) {
27548                 
27549                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27550                     _this.gMapContext.locationName = results[0].formatted_address;
27551                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27552                     
27553                     _this.fireEvent('positionchanged', this, location);
27554                 }
27555             });
27556             
27557             return;
27558         }
27559         
27560         this.fireEvent('positionchanged', this, location);
27561     },
27562     
27563     resize: function()
27564     {
27565         google.maps.event.trigger(this.gMapContext.map, "resize");
27566         
27567         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27568         
27569         this.fireEvent('resize', this);
27570     },
27571     
27572     setPositionByLatLng: function(latitude, longitude)
27573     {
27574         this.setPosition(new google.maps.LatLng(latitude, longitude));
27575     },
27576     
27577     getCurrentPosition: function() 
27578     {
27579         return {
27580             latitude: this.gMapContext.location.lat(),
27581             longitude: this.gMapContext.location.lng()
27582         };
27583     },
27584     
27585     getAddressName: function() 
27586     {
27587         return this.gMapContext.locationName;
27588     },
27589     
27590     getAddressComponents: function() 
27591     {
27592         return this.gMapContext.addressComponents;
27593     },
27594     
27595     address_component_from_google_geocode: function(address_components) 
27596     {
27597         var result = {};
27598         
27599         for (var i = 0; i < address_components.length; i++) {
27600             var component = address_components[i];
27601             if (component.types.indexOf("postal_code") >= 0) {
27602                 result.postalCode = component.short_name;
27603             } else if (component.types.indexOf("street_number") >= 0) {
27604                 result.streetNumber = component.short_name;
27605             } else if (component.types.indexOf("route") >= 0) {
27606                 result.streetName = component.short_name;
27607             } else if (component.types.indexOf("neighborhood") >= 0) {
27608                 result.city = component.short_name;
27609             } else if (component.types.indexOf("locality") >= 0) {
27610                 result.city = component.short_name;
27611             } else if (component.types.indexOf("sublocality") >= 0) {
27612                 result.district = component.short_name;
27613             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27614                 result.stateOrProvince = component.short_name;
27615             } else if (component.types.indexOf("country") >= 0) {
27616                 result.country = component.short_name;
27617             }
27618         }
27619         
27620         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27621         result.addressLine2 = "";
27622         return result;
27623     },
27624     
27625     setZoomLevel: function(zoom)
27626     {
27627         this.gMapContext.map.setZoom(zoom);
27628     },
27629     
27630     show: function()
27631     {
27632         if(!this.el){
27633             return;
27634         }
27635         
27636         this.el.show();
27637         
27638         this.resize();
27639         
27640         this.fireEvent('show', this);
27641     },
27642     
27643     hide: function()
27644     {
27645         if(!this.el){
27646             return;
27647         }
27648         
27649         this.el.hide();
27650         
27651         this.fireEvent('hide', this);
27652     }
27653     
27654 });
27655
27656 Roo.apply(Roo.bootstrap.LocationPicker, {
27657     
27658     OverlayView : function(map, options)
27659     {
27660         options = options || {};
27661         
27662         this.setMap(map);
27663     }
27664     
27665     
27666 });/**
27667  * @class Roo.bootstrap.Alert
27668  * @extends Roo.bootstrap.Component
27669  * Bootstrap Alert class - shows an alert area box
27670  * eg
27671  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27672   Enter a valid email address
27673 </div>
27674  * @licence LGPL
27675  * @cfg {String} title The title of alert
27676  * @cfg {String} html The content of alert
27677  * @cfg {String} weight (  success | info | warning | danger )
27678  * @cfg {String} faicon font-awesomeicon
27679  * 
27680  * @constructor
27681  * Create a new alert
27682  * @param {Object} config The config object
27683  */
27684
27685
27686 Roo.bootstrap.Alert = function(config){
27687     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27688     
27689 };
27690
27691 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27692     
27693     title: '',
27694     html: '',
27695     weight: false,
27696     faicon: false,
27697     
27698     getAutoCreate : function()
27699     {
27700         
27701         var cfg = {
27702             tag : 'div',
27703             cls : 'alert',
27704             cn : [
27705                 {
27706                     tag : 'i',
27707                     cls : 'roo-alert-icon'
27708                     
27709                 },
27710                 {
27711                     tag : 'b',
27712                     cls : 'roo-alert-title',
27713                     html : this.title
27714                 },
27715                 {
27716                     tag : 'span',
27717                     cls : 'roo-alert-text',
27718                     html : this.html
27719                 }
27720             ]
27721         };
27722         
27723         if(this.faicon){
27724             cfg.cn[0].cls += ' fa ' + this.faicon;
27725         }
27726         
27727         if(this.weight){
27728             cfg.cls += ' alert-' + this.weight;
27729         }
27730         
27731         return cfg;
27732     },
27733     
27734     initEvents: function() 
27735     {
27736         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27737     },
27738     
27739     setTitle : function(str)
27740     {
27741         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27742     },
27743     
27744     setText : function(str)
27745     {
27746         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27747     },
27748     
27749     setWeight : function(weight)
27750     {
27751         if(this.weight){
27752             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27753         }
27754         
27755         this.weight = weight;
27756         
27757         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27758     },
27759     
27760     setIcon : function(icon)
27761     {
27762         if(this.faicon){
27763             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27764         }
27765         
27766         this.faicon = icon;
27767         
27768         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27769     },
27770     
27771     hide: function() 
27772     {
27773         this.el.hide();   
27774     },
27775     
27776     show: function() 
27777     {  
27778         this.el.show();   
27779     }
27780     
27781 });
27782
27783  
27784 /*
27785 * Licence: LGPL
27786 */
27787
27788 /**
27789  * @class Roo.bootstrap.UploadCropbox
27790  * @extends Roo.bootstrap.Component
27791  * Bootstrap UploadCropbox class
27792  * @cfg {String} emptyText show when image has been loaded
27793  * @cfg {String} rotateNotify show when image too small to rotate
27794  * @cfg {Number} errorTimeout default 3000
27795  * @cfg {Number} minWidth default 300
27796  * @cfg {Number} minHeight default 300
27797  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27798  * @cfg {Boolean} isDocument (true|false) default false
27799  * @cfg {String} url action url
27800  * @cfg {String} paramName default 'imageUpload'
27801  * @cfg {String} method default POST
27802  * @cfg {Boolean} loadMask (true|false) default true
27803  * @cfg {Boolean} loadingText default 'Loading...'
27804  * 
27805  * @constructor
27806  * Create a new UploadCropbox
27807  * @param {Object} config The config object
27808  */
27809
27810 Roo.bootstrap.UploadCropbox = function(config){
27811     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27812     
27813     this.addEvents({
27814         /**
27815          * @event beforeselectfile
27816          * Fire before select file
27817          * @param {Roo.bootstrap.UploadCropbox} this
27818          */
27819         "beforeselectfile" : true,
27820         /**
27821          * @event initial
27822          * Fire after initEvent
27823          * @param {Roo.bootstrap.UploadCropbox} this
27824          */
27825         "initial" : true,
27826         /**
27827          * @event crop
27828          * Fire after initEvent
27829          * @param {Roo.bootstrap.UploadCropbox} this
27830          * @param {String} data
27831          */
27832         "crop" : true,
27833         /**
27834          * @event prepare
27835          * Fire when preparing the file data
27836          * @param {Roo.bootstrap.UploadCropbox} this
27837          * @param {Object} file
27838          */
27839         "prepare" : true,
27840         /**
27841          * @event exception
27842          * Fire when get exception
27843          * @param {Roo.bootstrap.UploadCropbox} this
27844          * @param {XMLHttpRequest} xhr
27845          */
27846         "exception" : true,
27847         /**
27848          * @event beforeloadcanvas
27849          * Fire before load the canvas
27850          * @param {Roo.bootstrap.UploadCropbox} this
27851          * @param {String} src
27852          */
27853         "beforeloadcanvas" : true,
27854         /**
27855          * @event trash
27856          * Fire when trash image
27857          * @param {Roo.bootstrap.UploadCropbox} this
27858          */
27859         "trash" : true,
27860         /**
27861          * @event download
27862          * Fire when download the image
27863          * @param {Roo.bootstrap.UploadCropbox} this
27864          */
27865         "download" : true,
27866         /**
27867          * @event footerbuttonclick
27868          * Fire when footerbuttonclick
27869          * @param {Roo.bootstrap.UploadCropbox} this
27870          * @param {String} type
27871          */
27872         "footerbuttonclick" : true,
27873         /**
27874          * @event resize
27875          * Fire when resize
27876          * @param {Roo.bootstrap.UploadCropbox} this
27877          */
27878         "resize" : true,
27879         /**
27880          * @event rotate
27881          * Fire when rotate the image
27882          * @param {Roo.bootstrap.UploadCropbox} this
27883          * @param {String} pos
27884          */
27885         "rotate" : true,
27886         /**
27887          * @event inspect
27888          * Fire when inspect the file
27889          * @param {Roo.bootstrap.UploadCropbox} this
27890          * @param {Object} file
27891          */
27892         "inspect" : true,
27893         /**
27894          * @event upload
27895          * Fire when xhr upload the file
27896          * @param {Roo.bootstrap.UploadCropbox} this
27897          * @param {Object} data
27898          */
27899         "upload" : true,
27900         /**
27901          * @event arrange
27902          * Fire when arrange the file data
27903          * @param {Roo.bootstrap.UploadCropbox} this
27904          * @param {Object} formData
27905          */
27906         "arrange" : true
27907     });
27908     
27909     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27910 };
27911
27912 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27913     
27914     emptyText : 'Click to upload image',
27915     rotateNotify : 'Image is too small to rotate',
27916     errorTimeout : 3000,
27917     scale : 0,
27918     baseScale : 1,
27919     rotate : 0,
27920     dragable : false,
27921     pinching : false,
27922     mouseX : 0,
27923     mouseY : 0,
27924     cropData : false,
27925     minWidth : 300,
27926     minHeight : 300,
27927     file : false,
27928     exif : {},
27929     baseRotate : 1,
27930     cropType : 'image/jpeg',
27931     buttons : false,
27932     canvasLoaded : false,
27933     isDocument : false,
27934     method : 'POST',
27935     paramName : 'imageUpload',
27936     loadMask : true,
27937     loadingText : 'Loading...',
27938     maskEl : false,
27939     
27940     getAutoCreate : function()
27941     {
27942         var cfg = {
27943             tag : 'div',
27944             cls : 'roo-upload-cropbox',
27945             cn : [
27946                 {
27947                     tag : 'input',
27948                     cls : 'roo-upload-cropbox-selector',
27949                     type : 'file'
27950                 },
27951                 {
27952                     tag : 'div',
27953                     cls : 'roo-upload-cropbox-body',
27954                     style : 'cursor:pointer',
27955                     cn : [
27956                         {
27957                             tag : 'div',
27958                             cls : 'roo-upload-cropbox-preview'
27959                         },
27960                         {
27961                             tag : 'div',
27962                             cls : 'roo-upload-cropbox-thumb'
27963                         },
27964                         {
27965                             tag : 'div',
27966                             cls : 'roo-upload-cropbox-empty-notify',
27967                             html : this.emptyText
27968                         },
27969                         {
27970                             tag : 'div',
27971                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27972                             html : this.rotateNotify
27973                         }
27974                     ]
27975                 },
27976                 {
27977                     tag : 'div',
27978                     cls : 'roo-upload-cropbox-footer',
27979                     cn : {
27980                         tag : 'div',
27981                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27982                         cn : []
27983                     }
27984                 }
27985             ]
27986         };
27987         
27988         return cfg;
27989     },
27990     
27991     onRender : function(ct, position)
27992     {
27993         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27994         
27995         if (this.buttons.length) {
27996             
27997             Roo.each(this.buttons, function(bb) {
27998                 
27999                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28000                 
28001                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28002                 
28003             }, this);
28004         }
28005         
28006         if(this.loadMask){
28007             this.maskEl = this.el;
28008         }
28009     },
28010     
28011     initEvents : function()
28012     {
28013         this.urlAPI = (window.createObjectURL && window) || 
28014                                 (window.URL && URL.revokeObjectURL && URL) || 
28015                                 (window.webkitURL && webkitURL);
28016                         
28017         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28018         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28019         
28020         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28021         this.selectorEl.hide();
28022         
28023         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28024         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28025         
28026         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28027         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28028         this.thumbEl.hide();
28029         
28030         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28031         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28032         
28033         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28034         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28035         this.errorEl.hide();
28036         
28037         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28038         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28039         this.footerEl.hide();
28040         
28041         this.setThumbBoxSize();
28042         
28043         this.bind();
28044         
28045         this.resize();
28046         
28047         this.fireEvent('initial', this);
28048     },
28049
28050     bind : function()
28051     {
28052         var _this = this;
28053         
28054         window.addEventListener("resize", function() { _this.resize(); } );
28055         
28056         this.bodyEl.on('click', this.beforeSelectFile, this);
28057         
28058         if(Roo.isTouch){
28059             this.bodyEl.on('touchstart', this.onTouchStart, this);
28060             this.bodyEl.on('touchmove', this.onTouchMove, this);
28061             this.bodyEl.on('touchend', this.onTouchEnd, this);
28062         }
28063         
28064         if(!Roo.isTouch){
28065             this.bodyEl.on('mousedown', this.onMouseDown, this);
28066             this.bodyEl.on('mousemove', this.onMouseMove, this);
28067             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28068             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28069             Roo.get(document).on('mouseup', this.onMouseUp, this);
28070         }
28071         
28072         this.selectorEl.on('change', this.onFileSelected, this);
28073     },
28074     
28075     reset : function()
28076     {    
28077         this.scale = 0;
28078         this.baseScale = 1;
28079         this.rotate = 0;
28080         this.baseRotate = 1;
28081         this.dragable = false;
28082         this.pinching = false;
28083         this.mouseX = 0;
28084         this.mouseY = 0;
28085         this.cropData = false;
28086         this.notifyEl.dom.innerHTML = this.emptyText;
28087         
28088         this.selectorEl.dom.value = '';
28089         
28090     },
28091     
28092     resize : function()
28093     {
28094         if(this.fireEvent('resize', this) != false){
28095             this.setThumbBoxPosition();
28096             this.setCanvasPosition();
28097         }
28098     },
28099     
28100     onFooterButtonClick : function(e, el, o, type)
28101     {
28102         switch (type) {
28103             case 'rotate-left' :
28104                 this.onRotateLeft(e);
28105                 break;
28106             case 'rotate-right' :
28107                 this.onRotateRight(e);
28108                 break;
28109             case 'picture' :
28110                 this.beforeSelectFile(e);
28111                 break;
28112             case 'trash' :
28113                 this.trash(e);
28114                 break;
28115             case 'crop' :
28116                 this.crop(e);
28117                 break;
28118             case 'download' :
28119                 this.download(e);
28120                 break;
28121             default :
28122                 break;
28123         }
28124         
28125         this.fireEvent('footerbuttonclick', this, type);
28126     },
28127     
28128     beforeSelectFile : function(e)
28129     {
28130         e.preventDefault();
28131         
28132         if(this.fireEvent('beforeselectfile', this) != false){
28133             this.selectorEl.dom.click();
28134         }
28135     },
28136     
28137     onFileSelected : function(e)
28138     {
28139         e.preventDefault();
28140         
28141         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28142             return;
28143         }
28144         
28145         var file = this.selectorEl.dom.files[0];
28146         
28147         if(this.fireEvent('inspect', this, file) != false){
28148             this.prepare(file);
28149         }
28150         
28151     },
28152     
28153     trash : function(e)
28154     {
28155         this.fireEvent('trash', this);
28156     },
28157     
28158     download : function(e)
28159     {
28160         this.fireEvent('download', this);
28161     },
28162     
28163     loadCanvas : function(src)
28164     {   
28165         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28166             
28167             this.reset();
28168             
28169             this.imageEl = document.createElement('img');
28170             
28171             var _this = this;
28172             
28173             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28174             
28175             this.imageEl.src = src;
28176         }
28177     },
28178     
28179     onLoadCanvas : function()
28180     {   
28181         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28182         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28183         
28184         this.bodyEl.un('click', this.beforeSelectFile, this);
28185         
28186         this.notifyEl.hide();
28187         this.thumbEl.show();
28188         this.footerEl.show();
28189         
28190         this.baseRotateLevel();
28191         
28192         if(this.isDocument){
28193             this.setThumbBoxSize();
28194         }
28195         
28196         this.setThumbBoxPosition();
28197         
28198         this.baseScaleLevel();
28199         
28200         this.draw();
28201         
28202         this.resize();
28203         
28204         this.canvasLoaded = true;
28205         
28206         if(this.loadMask){
28207             this.maskEl.unmask();
28208         }
28209         
28210     },
28211     
28212     setCanvasPosition : function()
28213     {   
28214         if(!this.canvasEl){
28215             return;
28216         }
28217         
28218         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28219         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28220         
28221         this.previewEl.setLeft(pw);
28222         this.previewEl.setTop(ph);
28223         
28224     },
28225     
28226     onMouseDown : function(e)
28227     {   
28228         e.stopEvent();
28229         
28230         this.dragable = true;
28231         this.pinching = false;
28232         
28233         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28234             this.dragable = false;
28235             return;
28236         }
28237         
28238         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28239         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28240         
28241     },
28242     
28243     onMouseMove : function(e)
28244     {   
28245         e.stopEvent();
28246         
28247         if(!this.canvasLoaded){
28248             return;
28249         }
28250         
28251         if (!this.dragable){
28252             return;
28253         }
28254         
28255         var minX = Math.ceil(this.thumbEl.getLeft(true));
28256         var minY = Math.ceil(this.thumbEl.getTop(true));
28257         
28258         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28259         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28260         
28261         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28262         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28263         
28264         x = x - this.mouseX;
28265         y = y - this.mouseY;
28266         
28267         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28268         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28269         
28270         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28271         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28272         
28273         this.previewEl.setLeft(bgX);
28274         this.previewEl.setTop(bgY);
28275         
28276         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28277         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28278     },
28279     
28280     onMouseUp : function(e)
28281     {   
28282         e.stopEvent();
28283         
28284         this.dragable = false;
28285     },
28286     
28287     onMouseWheel : function(e)
28288     {   
28289         e.stopEvent();
28290         
28291         this.startScale = this.scale;
28292         
28293         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28294         
28295         if(!this.zoomable()){
28296             this.scale = this.startScale;
28297             return;
28298         }
28299         
28300         this.draw();
28301         
28302         return;
28303     },
28304     
28305     zoomable : function()
28306     {
28307         var minScale = this.thumbEl.getWidth() / this.minWidth;
28308         
28309         if(this.minWidth < this.minHeight){
28310             minScale = this.thumbEl.getHeight() / this.minHeight;
28311         }
28312         
28313         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28314         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28315         
28316         if(
28317                 this.isDocument &&
28318                 (this.rotate == 0 || this.rotate == 180) && 
28319                 (
28320                     width > this.imageEl.OriginWidth || 
28321                     height > this.imageEl.OriginHeight ||
28322                     (width < this.minWidth && height < this.minHeight)
28323                 )
28324         ){
28325             return false;
28326         }
28327         
28328         if(
28329                 this.isDocument &&
28330                 (this.rotate == 90 || this.rotate == 270) && 
28331                 (
28332                     width > this.imageEl.OriginWidth || 
28333                     height > this.imageEl.OriginHeight ||
28334                     (width < this.minHeight && height < this.minWidth)
28335                 )
28336         ){
28337             return false;
28338         }
28339         
28340         if(
28341                 !this.isDocument &&
28342                 (this.rotate == 0 || this.rotate == 180) && 
28343                 (
28344                     width < this.minWidth || 
28345                     width > this.imageEl.OriginWidth || 
28346                     height < this.minHeight || 
28347                     height > this.imageEl.OriginHeight
28348                 )
28349         ){
28350             return false;
28351         }
28352         
28353         if(
28354                 !this.isDocument &&
28355                 (this.rotate == 90 || this.rotate == 270) && 
28356                 (
28357                     width < this.minHeight || 
28358                     width > this.imageEl.OriginWidth || 
28359                     height < this.minWidth || 
28360                     height > this.imageEl.OriginHeight
28361                 )
28362         ){
28363             return false;
28364         }
28365         
28366         return true;
28367         
28368     },
28369     
28370     onRotateLeft : function(e)
28371     {   
28372         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28373             
28374             var minScale = this.thumbEl.getWidth() / this.minWidth;
28375             
28376             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28377             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28378             
28379             this.startScale = this.scale;
28380             
28381             while (this.getScaleLevel() < minScale){
28382             
28383                 this.scale = this.scale + 1;
28384                 
28385                 if(!this.zoomable()){
28386                     break;
28387                 }
28388                 
28389                 if(
28390                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28391                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28392                 ){
28393                     continue;
28394                 }
28395                 
28396                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28397
28398                 this.draw();
28399                 
28400                 return;
28401             }
28402             
28403             this.scale = this.startScale;
28404             
28405             this.onRotateFail();
28406             
28407             return false;
28408         }
28409         
28410         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28411
28412         if(this.isDocument){
28413             this.setThumbBoxSize();
28414             this.setThumbBoxPosition();
28415             this.setCanvasPosition();
28416         }
28417         
28418         this.draw();
28419         
28420         this.fireEvent('rotate', this, 'left');
28421         
28422     },
28423     
28424     onRotateRight : function(e)
28425     {
28426         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28427             
28428             var minScale = this.thumbEl.getWidth() / this.minWidth;
28429         
28430             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28431             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28432             
28433             this.startScale = this.scale;
28434             
28435             while (this.getScaleLevel() < minScale){
28436             
28437                 this.scale = this.scale + 1;
28438                 
28439                 if(!this.zoomable()){
28440                     break;
28441                 }
28442                 
28443                 if(
28444                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28445                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28446                 ){
28447                     continue;
28448                 }
28449                 
28450                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28451
28452                 this.draw();
28453                 
28454                 return;
28455             }
28456             
28457             this.scale = this.startScale;
28458             
28459             this.onRotateFail();
28460             
28461             return false;
28462         }
28463         
28464         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28465
28466         if(this.isDocument){
28467             this.setThumbBoxSize();
28468             this.setThumbBoxPosition();
28469             this.setCanvasPosition();
28470         }
28471         
28472         this.draw();
28473         
28474         this.fireEvent('rotate', this, 'right');
28475     },
28476     
28477     onRotateFail : function()
28478     {
28479         this.errorEl.show(true);
28480         
28481         var _this = this;
28482         
28483         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28484     },
28485     
28486     draw : function()
28487     {
28488         this.previewEl.dom.innerHTML = '';
28489         
28490         var canvasEl = document.createElement("canvas");
28491         
28492         var contextEl = canvasEl.getContext("2d");
28493         
28494         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28495         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28496         var center = this.imageEl.OriginWidth / 2;
28497         
28498         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28499             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28500             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28501             center = this.imageEl.OriginHeight / 2;
28502         }
28503         
28504         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28505         
28506         contextEl.translate(center, center);
28507         contextEl.rotate(this.rotate * Math.PI / 180);
28508
28509         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28510         
28511         this.canvasEl = document.createElement("canvas");
28512         
28513         this.contextEl = this.canvasEl.getContext("2d");
28514         
28515         switch (this.rotate) {
28516             case 0 :
28517                 
28518                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28519                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28520                 
28521                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28522                 
28523                 break;
28524             case 90 : 
28525                 
28526                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28527                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28528                 
28529                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28530                     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);
28531                     break;
28532                 }
28533                 
28534                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28535                 
28536                 break;
28537             case 180 :
28538                 
28539                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28540                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28541                 
28542                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28543                     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);
28544                     break;
28545                 }
28546                 
28547                 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);
28548                 
28549                 break;
28550             case 270 :
28551                 
28552                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28553                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28554         
28555                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28556                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28557                     break;
28558                 }
28559                 
28560                 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);
28561                 
28562                 break;
28563             default : 
28564                 break;
28565         }
28566         
28567         this.previewEl.appendChild(this.canvasEl);
28568         
28569         this.setCanvasPosition();
28570     },
28571     
28572     crop : function()
28573     {
28574         if(!this.canvasLoaded){
28575             return;
28576         }
28577         
28578         var imageCanvas = document.createElement("canvas");
28579         
28580         var imageContext = imageCanvas.getContext("2d");
28581         
28582         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28583         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28584         
28585         var center = imageCanvas.width / 2;
28586         
28587         imageContext.translate(center, center);
28588         
28589         imageContext.rotate(this.rotate * Math.PI / 180);
28590         
28591         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28592         
28593         var canvas = document.createElement("canvas");
28594         
28595         var context = canvas.getContext("2d");
28596                 
28597         canvas.width = this.minWidth;
28598         canvas.height = this.minHeight;
28599
28600         switch (this.rotate) {
28601             case 0 :
28602                 
28603                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28604                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28605                 
28606                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28607                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28608                 
28609                 var targetWidth = this.minWidth - 2 * x;
28610                 var targetHeight = this.minHeight - 2 * y;
28611                 
28612                 var scale = 1;
28613                 
28614                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28615                     scale = targetWidth / width;
28616                 }
28617                 
28618                 if(x > 0 && y == 0){
28619                     scale = targetHeight / height;
28620                 }
28621                 
28622                 if(x > 0 && y > 0){
28623                     scale = targetWidth / width;
28624                     
28625                     if(width < height){
28626                         scale = targetHeight / height;
28627                     }
28628                 }
28629                 
28630                 context.scale(scale, scale);
28631                 
28632                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28633                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28634
28635                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28636                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28637
28638                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28639                 
28640                 break;
28641             case 90 : 
28642                 
28643                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28644                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28645                 
28646                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28647                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28648                 
28649                 var targetWidth = this.minWidth - 2 * x;
28650                 var targetHeight = this.minHeight - 2 * y;
28651                 
28652                 var scale = 1;
28653                 
28654                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28655                     scale = targetWidth / width;
28656                 }
28657                 
28658                 if(x > 0 && y == 0){
28659                     scale = targetHeight / height;
28660                 }
28661                 
28662                 if(x > 0 && y > 0){
28663                     scale = targetWidth / width;
28664                     
28665                     if(width < height){
28666                         scale = targetHeight / height;
28667                     }
28668                 }
28669                 
28670                 context.scale(scale, scale);
28671                 
28672                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28673                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28674
28675                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28676                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28677                 
28678                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28679                 
28680                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28681                 
28682                 break;
28683             case 180 :
28684                 
28685                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28686                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28687                 
28688                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28689                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28690                 
28691                 var targetWidth = this.minWidth - 2 * x;
28692                 var targetHeight = this.minHeight - 2 * y;
28693                 
28694                 var scale = 1;
28695                 
28696                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28697                     scale = targetWidth / width;
28698                 }
28699                 
28700                 if(x > 0 && y == 0){
28701                     scale = targetHeight / height;
28702                 }
28703                 
28704                 if(x > 0 && y > 0){
28705                     scale = targetWidth / width;
28706                     
28707                     if(width < height){
28708                         scale = targetHeight / height;
28709                     }
28710                 }
28711                 
28712                 context.scale(scale, scale);
28713                 
28714                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28715                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28716
28717                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28718                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28719
28720                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28721                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28722                 
28723                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28724                 
28725                 break;
28726             case 270 :
28727                 
28728                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28729                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28730                 
28731                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28732                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28733                 
28734                 var targetWidth = this.minWidth - 2 * x;
28735                 var targetHeight = this.minHeight - 2 * y;
28736                 
28737                 var scale = 1;
28738                 
28739                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28740                     scale = targetWidth / width;
28741                 }
28742                 
28743                 if(x > 0 && y == 0){
28744                     scale = targetHeight / height;
28745                 }
28746                 
28747                 if(x > 0 && y > 0){
28748                     scale = targetWidth / width;
28749                     
28750                     if(width < height){
28751                         scale = targetHeight / height;
28752                     }
28753                 }
28754                 
28755                 context.scale(scale, scale);
28756                 
28757                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28758                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28759
28760                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28761                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28762                 
28763                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28764                 
28765                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28766                 
28767                 break;
28768             default : 
28769                 break;
28770         }
28771         
28772         this.cropData = canvas.toDataURL(this.cropType);
28773         
28774         if(this.fireEvent('crop', this, this.cropData) !== false){
28775             this.process(this.file, this.cropData);
28776         }
28777         
28778         return;
28779         
28780     },
28781     
28782     setThumbBoxSize : function()
28783     {
28784         var width, height;
28785         
28786         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28787             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28788             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28789             
28790             this.minWidth = width;
28791             this.minHeight = height;
28792             
28793             if(this.rotate == 90 || this.rotate == 270){
28794                 this.minWidth = height;
28795                 this.minHeight = width;
28796             }
28797         }
28798         
28799         height = 300;
28800         width = Math.ceil(this.minWidth * height / this.minHeight);
28801         
28802         if(this.minWidth > this.minHeight){
28803             width = 300;
28804             height = Math.ceil(this.minHeight * width / this.minWidth);
28805         }
28806         
28807         this.thumbEl.setStyle({
28808             width : width + 'px',
28809             height : height + 'px'
28810         });
28811
28812         return;
28813             
28814     },
28815     
28816     setThumbBoxPosition : function()
28817     {
28818         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28819         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28820         
28821         this.thumbEl.setLeft(x);
28822         this.thumbEl.setTop(y);
28823         
28824     },
28825     
28826     baseRotateLevel : function()
28827     {
28828         this.baseRotate = 1;
28829         
28830         if(
28831                 typeof(this.exif) != 'undefined' &&
28832                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28833                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28834         ){
28835             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28836         }
28837         
28838         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28839         
28840     },
28841     
28842     baseScaleLevel : function()
28843     {
28844         var width, height;
28845         
28846         if(this.isDocument){
28847             
28848             if(this.baseRotate == 6 || this.baseRotate == 8){
28849             
28850                 height = this.thumbEl.getHeight();
28851                 this.baseScale = height / this.imageEl.OriginWidth;
28852
28853                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28854                     width = this.thumbEl.getWidth();
28855                     this.baseScale = width / this.imageEl.OriginHeight;
28856                 }
28857
28858                 return;
28859             }
28860
28861             height = this.thumbEl.getHeight();
28862             this.baseScale = height / this.imageEl.OriginHeight;
28863
28864             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28865                 width = this.thumbEl.getWidth();
28866                 this.baseScale = width / this.imageEl.OriginWidth;
28867             }
28868
28869             return;
28870         }
28871         
28872         if(this.baseRotate == 6 || this.baseRotate == 8){
28873             
28874             width = this.thumbEl.getHeight();
28875             this.baseScale = width / this.imageEl.OriginHeight;
28876             
28877             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28878                 height = this.thumbEl.getWidth();
28879                 this.baseScale = height / this.imageEl.OriginHeight;
28880             }
28881             
28882             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28883                 height = this.thumbEl.getWidth();
28884                 this.baseScale = height / this.imageEl.OriginHeight;
28885                 
28886                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28887                     width = this.thumbEl.getHeight();
28888                     this.baseScale = width / this.imageEl.OriginWidth;
28889                 }
28890             }
28891             
28892             return;
28893         }
28894         
28895         width = this.thumbEl.getWidth();
28896         this.baseScale = width / this.imageEl.OriginWidth;
28897         
28898         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28899             height = this.thumbEl.getHeight();
28900             this.baseScale = height / this.imageEl.OriginHeight;
28901         }
28902         
28903         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28904             
28905             height = this.thumbEl.getHeight();
28906             this.baseScale = height / this.imageEl.OriginHeight;
28907             
28908             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28909                 width = this.thumbEl.getWidth();
28910                 this.baseScale = width / this.imageEl.OriginWidth;
28911             }
28912             
28913         }
28914         
28915         return;
28916     },
28917     
28918     getScaleLevel : function()
28919     {
28920         return this.baseScale * Math.pow(1.1, this.scale);
28921     },
28922     
28923     onTouchStart : function(e)
28924     {
28925         if(!this.canvasLoaded){
28926             this.beforeSelectFile(e);
28927             return;
28928         }
28929         
28930         var touches = e.browserEvent.touches;
28931         
28932         if(!touches){
28933             return;
28934         }
28935         
28936         if(touches.length == 1){
28937             this.onMouseDown(e);
28938             return;
28939         }
28940         
28941         if(touches.length != 2){
28942             return;
28943         }
28944         
28945         var coords = [];
28946         
28947         for(var i = 0, finger; finger = touches[i]; i++){
28948             coords.push(finger.pageX, finger.pageY);
28949         }
28950         
28951         var x = Math.pow(coords[0] - coords[2], 2);
28952         var y = Math.pow(coords[1] - coords[3], 2);
28953         
28954         this.startDistance = Math.sqrt(x + y);
28955         
28956         this.startScale = this.scale;
28957         
28958         this.pinching = true;
28959         this.dragable = false;
28960         
28961     },
28962     
28963     onTouchMove : function(e)
28964     {
28965         if(!this.pinching && !this.dragable){
28966             return;
28967         }
28968         
28969         var touches = e.browserEvent.touches;
28970         
28971         if(!touches){
28972             return;
28973         }
28974         
28975         if(this.dragable){
28976             this.onMouseMove(e);
28977             return;
28978         }
28979         
28980         var coords = [];
28981         
28982         for(var i = 0, finger; finger = touches[i]; i++){
28983             coords.push(finger.pageX, finger.pageY);
28984         }
28985         
28986         var x = Math.pow(coords[0] - coords[2], 2);
28987         var y = Math.pow(coords[1] - coords[3], 2);
28988         
28989         this.endDistance = Math.sqrt(x + y);
28990         
28991         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28992         
28993         if(!this.zoomable()){
28994             this.scale = this.startScale;
28995             return;
28996         }
28997         
28998         this.draw();
28999         
29000     },
29001     
29002     onTouchEnd : function(e)
29003     {
29004         this.pinching = false;
29005         this.dragable = false;
29006         
29007     },
29008     
29009     process : function(file, crop)
29010     {
29011         if(this.loadMask){
29012             this.maskEl.mask(this.loadingText);
29013         }
29014         
29015         this.xhr = new XMLHttpRequest();
29016         
29017         file.xhr = this.xhr;
29018
29019         this.xhr.open(this.method, this.url, true);
29020         
29021         var headers = {
29022             "Accept": "application/json",
29023             "Cache-Control": "no-cache",
29024             "X-Requested-With": "XMLHttpRequest"
29025         };
29026         
29027         for (var headerName in headers) {
29028             var headerValue = headers[headerName];
29029             if (headerValue) {
29030                 this.xhr.setRequestHeader(headerName, headerValue);
29031             }
29032         }
29033         
29034         var _this = this;
29035         
29036         this.xhr.onload = function()
29037         {
29038             _this.xhrOnLoad(_this.xhr);
29039         }
29040         
29041         this.xhr.onerror = function()
29042         {
29043             _this.xhrOnError(_this.xhr);
29044         }
29045         
29046         var formData = new FormData();
29047
29048         formData.append('returnHTML', 'NO');
29049         
29050         if(crop){
29051             formData.append('crop', crop);
29052         }
29053         
29054         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29055             formData.append(this.paramName, file, file.name);
29056         }
29057         
29058         if(typeof(file.filename) != 'undefined'){
29059             formData.append('filename', file.filename);
29060         }
29061         
29062         if(typeof(file.mimetype) != 'undefined'){
29063             formData.append('mimetype', file.mimetype);
29064         }
29065         
29066         if(this.fireEvent('arrange', this, formData) != false){
29067             this.xhr.send(formData);
29068         };
29069     },
29070     
29071     xhrOnLoad : function(xhr)
29072     {
29073         if(this.loadMask){
29074             this.maskEl.unmask();
29075         }
29076         
29077         if (xhr.readyState !== 4) {
29078             this.fireEvent('exception', this, xhr);
29079             return;
29080         }
29081
29082         var response = Roo.decode(xhr.responseText);
29083         
29084         if(!response.success){
29085             this.fireEvent('exception', this, xhr);
29086             return;
29087         }
29088         
29089         var response = Roo.decode(xhr.responseText);
29090         
29091         this.fireEvent('upload', this, response);
29092         
29093     },
29094     
29095     xhrOnError : function()
29096     {
29097         if(this.loadMask){
29098             this.maskEl.unmask();
29099         }
29100         
29101         Roo.log('xhr on error');
29102         
29103         var response = Roo.decode(xhr.responseText);
29104           
29105         Roo.log(response);
29106         
29107     },
29108     
29109     prepare : function(file)
29110     {   
29111         if(this.loadMask){
29112             this.maskEl.mask(this.loadingText);
29113         }
29114         
29115         this.file = false;
29116         this.exif = {};
29117         
29118         if(typeof(file) === 'string'){
29119             this.loadCanvas(file);
29120             return;
29121         }
29122         
29123         if(!file || !this.urlAPI){
29124             return;
29125         }
29126         
29127         this.file = file;
29128         this.cropType = file.type;
29129         
29130         var _this = this;
29131         
29132         if(this.fireEvent('prepare', this, this.file) != false){
29133             
29134             var reader = new FileReader();
29135             
29136             reader.onload = function (e) {
29137                 if (e.target.error) {
29138                     Roo.log(e.target.error);
29139                     return;
29140                 }
29141                 
29142                 var buffer = e.target.result,
29143                     dataView = new DataView(buffer),
29144                     offset = 2,
29145                     maxOffset = dataView.byteLength - 4,
29146                     markerBytes,
29147                     markerLength;
29148                 
29149                 if (dataView.getUint16(0) === 0xffd8) {
29150                     while (offset < maxOffset) {
29151                         markerBytes = dataView.getUint16(offset);
29152                         
29153                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29154                             markerLength = dataView.getUint16(offset + 2) + 2;
29155                             if (offset + markerLength > dataView.byteLength) {
29156                                 Roo.log('Invalid meta data: Invalid segment size.');
29157                                 break;
29158                             }
29159                             
29160                             if(markerBytes == 0xffe1){
29161                                 _this.parseExifData(
29162                                     dataView,
29163                                     offset,
29164                                     markerLength
29165                                 );
29166                             }
29167                             
29168                             offset += markerLength;
29169                             
29170                             continue;
29171                         }
29172                         
29173                         break;
29174                     }
29175                     
29176                 }
29177                 
29178                 var url = _this.urlAPI.createObjectURL(_this.file);
29179                 
29180                 _this.loadCanvas(url);
29181                 
29182                 return;
29183             }
29184             
29185             reader.readAsArrayBuffer(this.file);
29186             
29187         }
29188         
29189     },
29190     
29191     parseExifData : function(dataView, offset, length)
29192     {
29193         var tiffOffset = offset + 10,
29194             littleEndian,
29195             dirOffset;
29196     
29197         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29198             // No Exif data, might be XMP data instead
29199             return;
29200         }
29201         
29202         // Check for the ASCII code for "Exif" (0x45786966):
29203         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29204             // No Exif data, might be XMP data instead
29205             return;
29206         }
29207         if (tiffOffset + 8 > dataView.byteLength) {
29208             Roo.log('Invalid Exif data: Invalid segment size.');
29209             return;
29210         }
29211         // Check for the two null bytes:
29212         if (dataView.getUint16(offset + 8) !== 0x0000) {
29213             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29214             return;
29215         }
29216         // Check the byte alignment:
29217         switch (dataView.getUint16(tiffOffset)) {
29218         case 0x4949:
29219             littleEndian = true;
29220             break;
29221         case 0x4D4D:
29222             littleEndian = false;
29223             break;
29224         default:
29225             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29226             return;
29227         }
29228         // Check for the TIFF tag marker (0x002A):
29229         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29230             Roo.log('Invalid Exif data: Missing TIFF marker.');
29231             return;
29232         }
29233         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29234         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29235         
29236         this.parseExifTags(
29237             dataView,
29238             tiffOffset,
29239             tiffOffset + dirOffset,
29240             littleEndian
29241         );
29242     },
29243     
29244     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29245     {
29246         var tagsNumber,
29247             dirEndOffset,
29248             i;
29249         if (dirOffset + 6 > dataView.byteLength) {
29250             Roo.log('Invalid Exif data: Invalid directory offset.');
29251             return;
29252         }
29253         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29254         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29255         if (dirEndOffset + 4 > dataView.byteLength) {
29256             Roo.log('Invalid Exif data: Invalid directory size.');
29257             return;
29258         }
29259         for (i = 0; i < tagsNumber; i += 1) {
29260             this.parseExifTag(
29261                 dataView,
29262                 tiffOffset,
29263                 dirOffset + 2 + 12 * i, // tag offset
29264                 littleEndian
29265             );
29266         }
29267         // Return the offset to the next directory:
29268         return dataView.getUint32(dirEndOffset, littleEndian);
29269     },
29270     
29271     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29272     {
29273         var tag = dataView.getUint16(offset, littleEndian);
29274         
29275         this.exif[tag] = this.getExifValue(
29276             dataView,
29277             tiffOffset,
29278             offset,
29279             dataView.getUint16(offset + 2, littleEndian), // tag type
29280             dataView.getUint32(offset + 4, littleEndian), // tag length
29281             littleEndian
29282         );
29283     },
29284     
29285     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29286     {
29287         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29288             tagSize,
29289             dataOffset,
29290             values,
29291             i,
29292             str,
29293             c;
29294     
29295         if (!tagType) {
29296             Roo.log('Invalid Exif data: Invalid tag type.');
29297             return;
29298         }
29299         
29300         tagSize = tagType.size * length;
29301         // Determine if the value is contained in the dataOffset bytes,
29302         // or if the value at the dataOffset is a pointer to the actual data:
29303         dataOffset = tagSize > 4 ?
29304                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29305         if (dataOffset + tagSize > dataView.byteLength) {
29306             Roo.log('Invalid Exif data: Invalid data offset.');
29307             return;
29308         }
29309         if (length === 1) {
29310             return tagType.getValue(dataView, dataOffset, littleEndian);
29311         }
29312         values = [];
29313         for (i = 0; i < length; i += 1) {
29314             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29315         }
29316         
29317         if (tagType.ascii) {
29318             str = '';
29319             // Concatenate the chars:
29320             for (i = 0; i < values.length; i += 1) {
29321                 c = values[i];
29322                 // Ignore the terminating NULL byte(s):
29323                 if (c === '\u0000') {
29324                     break;
29325                 }
29326                 str += c;
29327             }
29328             return str;
29329         }
29330         return values;
29331     }
29332     
29333 });
29334
29335 Roo.apply(Roo.bootstrap.UploadCropbox, {
29336     tags : {
29337         'Orientation': 0x0112
29338     },
29339     
29340     Orientation: {
29341             1: 0, //'top-left',
29342 //            2: 'top-right',
29343             3: 180, //'bottom-right',
29344 //            4: 'bottom-left',
29345 //            5: 'left-top',
29346             6: 90, //'right-top',
29347 //            7: 'right-bottom',
29348             8: 270 //'left-bottom'
29349     },
29350     
29351     exifTagTypes : {
29352         // byte, 8-bit unsigned int:
29353         1: {
29354             getValue: function (dataView, dataOffset) {
29355                 return dataView.getUint8(dataOffset);
29356             },
29357             size: 1
29358         },
29359         // ascii, 8-bit byte:
29360         2: {
29361             getValue: function (dataView, dataOffset) {
29362                 return String.fromCharCode(dataView.getUint8(dataOffset));
29363             },
29364             size: 1,
29365             ascii: true
29366         },
29367         // short, 16 bit int:
29368         3: {
29369             getValue: function (dataView, dataOffset, littleEndian) {
29370                 return dataView.getUint16(dataOffset, littleEndian);
29371             },
29372             size: 2
29373         },
29374         // long, 32 bit int:
29375         4: {
29376             getValue: function (dataView, dataOffset, littleEndian) {
29377                 return dataView.getUint32(dataOffset, littleEndian);
29378             },
29379             size: 4
29380         },
29381         // rational = two long values, first is numerator, second is denominator:
29382         5: {
29383             getValue: function (dataView, dataOffset, littleEndian) {
29384                 return dataView.getUint32(dataOffset, littleEndian) /
29385                     dataView.getUint32(dataOffset + 4, littleEndian);
29386             },
29387             size: 8
29388         },
29389         // slong, 32 bit signed int:
29390         9: {
29391             getValue: function (dataView, dataOffset, littleEndian) {
29392                 return dataView.getInt32(dataOffset, littleEndian);
29393             },
29394             size: 4
29395         },
29396         // srational, two slongs, first is numerator, second is denominator:
29397         10: {
29398             getValue: function (dataView, dataOffset, littleEndian) {
29399                 return dataView.getInt32(dataOffset, littleEndian) /
29400                     dataView.getInt32(dataOffset + 4, littleEndian);
29401             },
29402             size: 8
29403         }
29404     },
29405     
29406     footer : {
29407         STANDARD : [
29408             {
29409                 tag : 'div',
29410                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29411                 action : 'rotate-left',
29412                 cn : [
29413                     {
29414                         tag : 'button',
29415                         cls : 'btn btn-default',
29416                         html : '<i class="fa fa-undo"></i>'
29417                     }
29418                 ]
29419             },
29420             {
29421                 tag : 'div',
29422                 cls : 'btn-group roo-upload-cropbox-picture',
29423                 action : 'picture',
29424                 cn : [
29425                     {
29426                         tag : 'button',
29427                         cls : 'btn btn-default',
29428                         html : '<i class="fa fa-picture-o"></i>'
29429                     }
29430                 ]
29431             },
29432             {
29433                 tag : 'div',
29434                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29435                 action : 'rotate-right',
29436                 cn : [
29437                     {
29438                         tag : 'button',
29439                         cls : 'btn btn-default',
29440                         html : '<i class="fa fa-repeat"></i>'
29441                     }
29442                 ]
29443             }
29444         ],
29445         DOCUMENT : [
29446             {
29447                 tag : 'div',
29448                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29449                 action : 'rotate-left',
29450                 cn : [
29451                     {
29452                         tag : 'button',
29453                         cls : 'btn btn-default',
29454                         html : '<i class="fa fa-undo"></i>'
29455                     }
29456                 ]
29457             },
29458             {
29459                 tag : 'div',
29460                 cls : 'btn-group roo-upload-cropbox-download',
29461                 action : 'download',
29462                 cn : [
29463                     {
29464                         tag : 'button',
29465                         cls : 'btn btn-default',
29466                         html : '<i class="fa fa-download"></i>'
29467                     }
29468                 ]
29469             },
29470             {
29471                 tag : 'div',
29472                 cls : 'btn-group roo-upload-cropbox-crop',
29473                 action : 'crop',
29474                 cn : [
29475                     {
29476                         tag : 'button',
29477                         cls : 'btn btn-default',
29478                         html : '<i class="fa fa-crop"></i>'
29479                     }
29480                 ]
29481             },
29482             {
29483                 tag : 'div',
29484                 cls : 'btn-group roo-upload-cropbox-trash',
29485                 action : 'trash',
29486                 cn : [
29487                     {
29488                         tag : 'button',
29489                         cls : 'btn btn-default',
29490                         html : '<i class="fa fa-trash"></i>'
29491                     }
29492                 ]
29493             },
29494             {
29495                 tag : 'div',
29496                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29497                 action : 'rotate-right',
29498                 cn : [
29499                     {
29500                         tag : 'button',
29501                         cls : 'btn btn-default',
29502                         html : '<i class="fa fa-repeat"></i>'
29503                     }
29504                 ]
29505             }
29506         ],
29507         ROTATOR : [
29508             {
29509                 tag : 'div',
29510                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29511                 action : 'rotate-left',
29512                 cn : [
29513                     {
29514                         tag : 'button',
29515                         cls : 'btn btn-default',
29516                         html : '<i class="fa fa-undo"></i>'
29517                     }
29518                 ]
29519             },
29520             {
29521                 tag : 'div',
29522                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29523                 action : 'rotate-right',
29524                 cn : [
29525                     {
29526                         tag : 'button',
29527                         cls : 'btn btn-default',
29528                         html : '<i class="fa fa-repeat"></i>'
29529                     }
29530                 ]
29531             }
29532         ]
29533     }
29534 });
29535
29536 /*
29537 * Licence: LGPL
29538 */
29539
29540 /**
29541  * @class Roo.bootstrap.DocumentManager
29542  * @extends Roo.bootstrap.Component
29543  * Bootstrap DocumentManager class
29544  * @cfg {String} paramName default 'imageUpload'
29545  * @cfg {String} toolTipName default 'filename'
29546  * @cfg {String} method default POST
29547  * @cfg {String} url action url
29548  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29549  * @cfg {Boolean} multiple multiple upload default true
29550  * @cfg {Number} thumbSize default 300
29551  * @cfg {String} fieldLabel
29552  * @cfg {Number} labelWidth default 4
29553  * @cfg {String} labelAlign (left|top) default left
29554  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29555 * @cfg {Number} labellg set the width of label (1-12)
29556  * @cfg {Number} labelmd set the width of label (1-12)
29557  * @cfg {Number} labelsm set the width of label (1-12)
29558  * @cfg {Number} labelxs set the width of label (1-12)
29559  * 
29560  * @constructor
29561  * Create a new DocumentManager
29562  * @param {Object} config The config object
29563  */
29564
29565 Roo.bootstrap.DocumentManager = function(config){
29566     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29567     
29568     this.files = [];
29569     this.delegates = [];
29570     
29571     this.addEvents({
29572         /**
29573          * @event initial
29574          * Fire when initial the DocumentManager
29575          * @param {Roo.bootstrap.DocumentManager} this
29576          */
29577         "initial" : true,
29578         /**
29579          * @event inspect
29580          * inspect selected file
29581          * @param {Roo.bootstrap.DocumentManager} this
29582          * @param {File} file
29583          */
29584         "inspect" : true,
29585         /**
29586          * @event exception
29587          * Fire when xhr load exception
29588          * @param {Roo.bootstrap.DocumentManager} this
29589          * @param {XMLHttpRequest} xhr
29590          */
29591         "exception" : true,
29592         /**
29593          * @event afterupload
29594          * Fire when xhr load exception
29595          * @param {Roo.bootstrap.DocumentManager} this
29596          * @param {XMLHttpRequest} xhr
29597          */
29598         "afterupload" : true,
29599         /**
29600          * @event prepare
29601          * prepare the form data
29602          * @param {Roo.bootstrap.DocumentManager} this
29603          * @param {Object} formData
29604          */
29605         "prepare" : true,
29606         /**
29607          * @event remove
29608          * Fire when remove the file
29609          * @param {Roo.bootstrap.DocumentManager} this
29610          * @param {Object} file
29611          */
29612         "remove" : true,
29613         /**
29614          * @event refresh
29615          * Fire after refresh the file
29616          * @param {Roo.bootstrap.DocumentManager} this
29617          */
29618         "refresh" : true,
29619         /**
29620          * @event click
29621          * Fire after click the image
29622          * @param {Roo.bootstrap.DocumentManager} this
29623          * @param {Object} file
29624          */
29625         "click" : true,
29626         /**
29627          * @event edit
29628          * Fire when upload a image and editable set to true
29629          * @param {Roo.bootstrap.DocumentManager} this
29630          * @param {Object} file
29631          */
29632         "edit" : true,
29633         /**
29634          * @event beforeselectfile
29635          * Fire before select file
29636          * @param {Roo.bootstrap.DocumentManager} this
29637          */
29638         "beforeselectfile" : true,
29639         /**
29640          * @event process
29641          * Fire before process file
29642          * @param {Roo.bootstrap.DocumentManager} this
29643          * @param {Object} file
29644          */
29645         "process" : true,
29646         /**
29647          * @event previewrendered
29648          * Fire when preview rendered
29649          * @param {Roo.bootstrap.DocumentManager} this
29650          * @param {Object} file
29651          */
29652         "previewrendered" : true,
29653         /**
29654          */
29655         "previewResize" : true
29656         
29657     });
29658 };
29659
29660 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29661     
29662     boxes : 0,
29663     inputName : '',
29664     thumbSize : 300,
29665     multiple : true,
29666     files : false,
29667     method : 'POST',
29668     url : '',
29669     paramName : 'imageUpload',
29670     toolTipName : 'filename',
29671     fieldLabel : '',
29672     labelWidth : 4,
29673     labelAlign : 'left',
29674     editable : true,
29675     delegates : false,
29676     xhr : false, 
29677     
29678     labellg : 0,
29679     labelmd : 0,
29680     labelsm : 0,
29681     labelxs : 0,
29682     
29683     getAutoCreate : function()
29684     {   
29685         var managerWidget = {
29686             tag : 'div',
29687             cls : 'roo-document-manager',
29688             cn : [
29689                 {
29690                     tag : 'input',
29691                     cls : 'roo-document-manager-selector',
29692                     type : 'file'
29693                 },
29694                 {
29695                     tag : 'div',
29696                     cls : 'roo-document-manager-uploader',
29697                     cn : [
29698                         {
29699                             tag : 'div',
29700                             cls : 'roo-document-manager-upload-btn',
29701                             html : '<i class="fa fa-plus"></i>'
29702                         }
29703                     ]
29704                     
29705                 }
29706             ]
29707         };
29708         
29709         var content = [
29710             {
29711                 tag : 'div',
29712                 cls : 'column col-md-12',
29713                 cn : managerWidget
29714             }
29715         ];
29716         
29717         if(this.fieldLabel.length){
29718             
29719             content = [
29720                 {
29721                     tag : 'div',
29722                     cls : 'column col-md-12',
29723                     html : this.fieldLabel
29724                 },
29725                 {
29726                     tag : 'div',
29727                     cls : 'column col-md-12',
29728                     cn : managerWidget
29729                 }
29730             ];
29731
29732             if(this.labelAlign == 'left'){
29733                 content = [
29734                     {
29735                         tag : 'div',
29736                         cls : 'column',
29737                         html : this.fieldLabel
29738                     },
29739                     {
29740                         tag : 'div',
29741                         cls : 'column',
29742                         cn : managerWidget
29743                     }
29744                 ];
29745                 
29746                 if(this.labelWidth > 12){
29747                     content[0].style = "width: " + this.labelWidth + 'px';
29748                 }
29749
29750                 if(this.labelWidth < 13 && this.labelmd == 0){
29751                     this.labelmd = this.labelWidth;
29752                 }
29753
29754                 if(this.labellg > 0){
29755                     content[0].cls += ' col-lg-' + this.labellg;
29756                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29757                 }
29758
29759                 if(this.labelmd > 0){
29760                     content[0].cls += ' col-md-' + this.labelmd;
29761                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29762                 }
29763
29764                 if(this.labelsm > 0){
29765                     content[0].cls += ' col-sm-' + this.labelsm;
29766                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29767                 }
29768
29769                 if(this.labelxs > 0){
29770                     content[0].cls += ' col-xs-' + this.labelxs;
29771                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29772                 }
29773                 
29774             }
29775         }
29776         
29777         var cfg = {
29778             tag : 'div',
29779             cls : 'row clearfix',
29780             cn : content
29781         };
29782         
29783         return cfg;
29784         
29785     },
29786     
29787     initEvents : function()
29788     {
29789         this.managerEl = this.el.select('.roo-document-manager', true).first();
29790         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29791         
29792         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29793         this.selectorEl.hide();
29794         
29795         if(this.multiple){
29796             this.selectorEl.attr('multiple', 'multiple');
29797         }
29798         
29799         this.selectorEl.on('change', this.onFileSelected, this);
29800         
29801         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29802         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29803         
29804         this.uploader.on('click', this.onUploaderClick, this);
29805         
29806         this.renderProgressDialog();
29807         
29808         var _this = this;
29809         
29810         window.addEventListener("resize", function() { _this.refresh(); } );
29811         
29812         this.fireEvent('initial', this);
29813     },
29814     
29815     renderProgressDialog : function()
29816     {
29817         var _this = this;
29818         
29819         this.progressDialog = new Roo.bootstrap.Modal({
29820             cls : 'roo-document-manager-progress-dialog',
29821             allow_close : false,
29822             animate : false,
29823             title : '',
29824             buttons : [
29825                 {
29826                     name  :'cancel',
29827                     weight : 'danger',
29828                     html : 'Cancel'
29829                 }
29830             ], 
29831             listeners : { 
29832                 btnclick : function() {
29833                     _this.uploadCancel();
29834                     this.hide();
29835                 }
29836             }
29837         });
29838          
29839         this.progressDialog.render(Roo.get(document.body));
29840          
29841         this.progress = new Roo.bootstrap.Progress({
29842             cls : 'roo-document-manager-progress',
29843             active : true,
29844             striped : true
29845         });
29846         
29847         this.progress.render(this.progressDialog.getChildContainer());
29848         
29849         this.progressBar = new Roo.bootstrap.ProgressBar({
29850             cls : 'roo-document-manager-progress-bar',
29851             aria_valuenow : 0,
29852             aria_valuemin : 0,
29853             aria_valuemax : 12,
29854             panel : 'success'
29855         });
29856         
29857         this.progressBar.render(this.progress.getChildContainer());
29858     },
29859     
29860     onUploaderClick : function(e)
29861     {
29862         e.preventDefault();
29863      
29864         if(this.fireEvent('beforeselectfile', this) != false){
29865             this.selectorEl.dom.click();
29866         }
29867         
29868     },
29869     
29870     onFileSelected : function(e)
29871     {
29872         e.preventDefault();
29873         
29874         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29875             return;
29876         }
29877         
29878         Roo.each(this.selectorEl.dom.files, function(file){
29879             if(this.fireEvent('inspect', this, file) != false){
29880                 this.files.push(file);
29881             }
29882         }, this);
29883         
29884         this.queue();
29885         
29886     },
29887     
29888     queue : function()
29889     {
29890         this.selectorEl.dom.value = '';
29891         
29892         if(!this.files || !this.files.length){
29893             return;
29894         }
29895         
29896         if(this.boxes > 0 && this.files.length > this.boxes){
29897             this.files = this.files.slice(0, this.boxes);
29898         }
29899         
29900         this.uploader.show();
29901         
29902         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29903             this.uploader.hide();
29904         }
29905         
29906         var _this = this;
29907         
29908         var files = [];
29909         
29910         var docs = [];
29911         
29912         Roo.each(this.files, function(file){
29913             
29914             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29915                 var f = this.renderPreview(file);
29916                 files.push(f);
29917                 return;
29918             }
29919             
29920             if(file.type.indexOf('image') != -1){
29921                 this.delegates.push(
29922                     (function(){
29923                         _this.process(file);
29924                     }).createDelegate(this)
29925                 );
29926         
29927                 return;
29928             }
29929             
29930             docs.push(
29931                 (function(){
29932                     _this.process(file);
29933                 }).createDelegate(this)
29934             );
29935             
29936         }, this);
29937         
29938         this.files = files;
29939         
29940         this.delegates = this.delegates.concat(docs);
29941         
29942         if(!this.delegates.length){
29943             this.refresh();
29944             return;
29945         }
29946         
29947         this.progressBar.aria_valuemax = this.delegates.length;
29948         
29949         this.arrange();
29950         
29951         return;
29952     },
29953     
29954     arrange : function()
29955     {
29956         if(!this.delegates.length){
29957             this.progressDialog.hide();
29958             this.refresh();
29959             return;
29960         }
29961         
29962         var delegate = this.delegates.shift();
29963         
29964         this.progressDialog.show();
29965         
29966         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29967         
29968         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29969         
29970         delegate();
29971     },
29972     
29973     refresh : function()
29974     {
29975         this.uploader.show();
29976         
29977         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29978             this.uploader.hide();
29979         }
29980         
29981         Roo.isTouch ? this.closable(false) : this.closable(true);
29982         
29983         this.fireEvent('refresh', this);
29984     },
29985     
29986     onRemove : function(e, el, o)
29987     {
29988         e.preventDefault();
29989         
29990         this.fireEvent('remove', this, o);
29991         
29992     },
29993     
29994     remove : function(o)
29995     {
29996         var files = [];
29997         
29998         Roo.each(this.files, function(file){
29999             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30000                 files.push(file);
30001                 return;
30002             }
30003
30004             o.target.remove();
30005
30006         }, this);
30007         
30008         this.files = files;
30009         
30010         this.refresh();
30011     },
30012     
30013     clear : function()
30014     {
30015         Roo.each(this.files, function(file){
30016             if(!file.target){
30017                 return;
30018             }
30019             
30020             file.target.remove();
30021
30022         }, this);
30023         
30024         this.files = [];
30025         
30026         this.refresh();
30027     },
30028     
30029     onClick : function(e, el, o)
30030     {
30031         e.preventDefault();
30032         
30033         this.fireEvent('click', this, o);
30034         
30035     },
30036     
30037     closable : function(closable)
30038     {
30039         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30040             
30041             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30042             
30043             if(closable){
30044                 el.show();
30045                 return;
30046             }
30047             
30048             el.hide();
30049             
30050         }, this);
30051     },
30052     
30053     xhrOnLoad : function(xhr)
30054     {
30055         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30056             el.remove();
30057         }, this);
30058         
30059         if (xhr.readyState !== 4) {
30060             this.arrange();
30061             this.fireEvent('exception', this, xhr);
30062             return;
30063         }
30064
30065         var response = Roo.decode(xhr.responseText);
30066         
30067         if(!response.success){
30068             this.arrange();
30069             this.fireEvent('exception', this, xhr);
30070             return;
30071         }
30072         
30073         var file = this.renderPreview(response.data);
30074         
30075         this.files.push(file);
30076         
30077         this.arrange();
30078         
30079         this.fireEvent('afterupload', this, xhr);
30080         
30081     },
30082     
30083     xhrOnError : function(xhr)
30084     {
30085         Roo.log('xhr on error');
30086         
30087         var response = Roo.decode(xhr.responseText);
30088           
30089         Roo.log(response);
30090         
30091         this.arrange();
30092     },
30093     
30094     process : function(file)
30095     {
30096         if(this.fireEvent('process', this, file) !== false){
30097             if(this.editable && file.type.indexOf('image') != -1){
30098                 this.fireEvent('edit', this, file);
30099                 return;
30100             }
30101
30102             this.uploadStart(file, false);
30103
30104             return;
30105         }
30106         
30107     },
30108     
30109     uploadStart : function(file, crop)
30110     {
30111         this.xhr = new XMLHttpRequest();
30112         
30113         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30114             this.arrange();
30115             return;
30116         }
30117         
30118         file.xhr = this.xhr;
30119             
30120         this.managerEl.createChild({
30121             tag : 'div',
30122             cls : 'roo-document-manager-loading',
30123             cn : [
30124                 {
30125                     tag : 'div',
30126                     tooltip : file.name,
30127                     cls : 'roo-document-manager-thumb',
30128                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30129                 }
30130             ]
30131
30132         });
30133
30134         this.xhr.open(this.method, this.url, true);
30135         
30136         var headers = {
30137             "Accept": "application/json",
30138             "Cache-Control": "no-cache",
30139             "X-Requested-With": "XMLHttpRequest"
30140         };
30141         
30142         for (var headerName in headers) {
30143             var headerValue = headers[headerName];
30144             if (headerValue) {
30145                 this.xhr.setRequestHeader(headerName, headerValue);
30146             }
30147         }
30148         
30149         var _this = this;
30150         
30151         this.xhr.onload = function()
30152         {
30153             _this.xhrOnLoad(_this.xhr);
30154         }
30155         
30156         this.xhr.onerror = function()
30157         {
30158             _this.xhrOnError(_this.xhr);
30159         }
30160         
30161         var formData = new FormData();
30162
30163         formData.append('returnHTML', 'NO');
30164         
30165         if(crop){
30166             formData.append('crop', crop);
30167         }
30168         
30169         formData.append(this.paramName, file, file.name);
30170         
30171         var options = {
30172             file : file, 
30173             manually : false
30174         };
30175         
30176         if(this.fireEvent('prepare', this, formData, options) != false){
30177             
30178             if(options.manually){
30179                 return;
30180             }
30181             
30182             this.xhr.send(formData);
30183             return;
30184         };
30185         
30186         this.uploadCancel();
30187     },
30188     
30189     uploadCancel : function()
30190     {
30191         if (this.xhr) {
30192             this.xhr.abort();
30193         }
30194         
30195         this.delegates = [];
30196         
30197         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30198             el.remove();
30199         }, this);
30200         
30201         this.arrange();
30202     },
30203     
30204     renderPreview : function(file)
30205     {
30206         if(typeof(file.target) != 'undefined' && file.target){
30207             return file;
30208         }
30209         
30210         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30211         
30212         var previewEl = this.managerEl.createChild({
30213             tag : 'div',
30214             cls : 'roo-document-manager-preview',
30215             cn : [
30216                 {
30217                     tag : 'div',
30218                     tooltip : file[this.toolTipName],
30219                     cls : 'roo-document-manager-thumb',
30220                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30221                 },
30222                 {
30223                     tag : 'button',
30224                     cls : 'close',
30225                     html : '<i class="fa fa-times-circle"></i>'
30226                 }
30227             ]
30228         });
30229
30230         var close = previewEl.select('button.close', true).first();
30231
30232         close.on('click', this.onRemove, this, file);
30233
30234         file.target = previewEl;
30235
30236         var image = previewEl.select('img', true).first();
30237         
30238         var _this = this;
30239         
30240         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30241         
30242         image.on('click', this.onClick, this, file);
30243         
30244         this.fireEvent('previewrendered', this, file);
30245         
30246         return file;
30247         
30248     },
30249     
30250     onPreviewLoad : function(file, image)
30251     {
30252         if(typeof(file.target) == 'undefined' || !file.target){
30253             return;
30254         }
30255         
30256         var width = image.dom.naturalWidth || image.dom.width;
30257         var height = image.dom.naturalHeight || image.dom.height;
30258         
30259         if(!this.previewResize) {
30260             return;
30261         }
30262         
30263         if(width > height){
30264             file.target.addClass('wide');
30265             return;
30266         }
30267         
30268         file.target.addClass('tall');
30269         return;
30270         
30271     },
30272     
30273     uploadFromSource : function(file, crop)
30274     {
30275         this.xhr = new XMLHttpRequest();
30276         
30277         this.managerEl.createChild({
30278             tag : 'div',
30279             cls : 'roo-document-manager-loading',
30280             cn : [
30281                 {
30282                     tag : 'div',
30283                     tooltip : file.name,
30284                     cls : 'roo-document-manager-thumb',
30285                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30286                 }
30287             ]
30288
30289         });
30290
30291         this.xhr.open(this.method, this.url, true);
30292         
30293         var headers = {
30294             "Accept": "application/json",
30295             "Cache-Control": "no-cache",
30296             "X-Requested-With": "XMLHttpRequest"
30297         };
30298         
30299         for (var headerName in headers) {
30300             var headerValue = headers[headerName];
30301             if (headerValue) {
30302                 this.xhr.setRequestHeader(headerName, headerValue);
30303             }
30304         }
30305         
30306         var _this = this;
30307         
30308         this.xhr.onload = function()
30309         {
30310             _this.xhrOnLoad(_this.xhr);
30311         }
30312         
30313         this.xhr.onerror = function()
30314         {
30315             _this.xhrOnError(_this.xhr);
30316         }
30317         
30318         var formData = new FormData();
30319
30320         formData.append('returnHTML', 'NO');
30321         
30322         formData.append('crop', crop);
30323         
30324         if(typeof(file.filename) != 'undefined'){
30325             formData.append('filename', file.filename);
30326         }
30327         
30328         if(typeof(file.mimetype) != 'undefined'){
30329             formData.append('mimetype', file.mimetype);
30330         }
30331         
30332         Roo.log(formData);
30333         
30334         if(this.fireEvent('prepare', this, formData) != false){
30335             this.xhr.send(formData);
30336         };
30337     }
30338 });
30339
30340 /*
30341 * Licence: LGPL
30342 */
30343
30344 /**
30345  * @class Roo.bootstrap.DocumentViewer
30346  * @extends Roo.bootstrap.Component
30347  * Bootstrap DocumentViewer class
30348  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30349  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30350  * 
30351  * @constructor
30352  * Create a new DocumentViewer
30353  * @param {Object} config The config object
30354  */
30355
30356 Roo.bootstrap.DocumentViewer = function(config){
30357     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30358     
30359     this.addEvents({
30360         /**
30361          * @event initial
30362          * Fire after initEvent
30363          * @param {Roo.bootstrap.DocumentViewer} this
30364          */
30365         "initial" : true,
30366         /**
30367          * @event click
30368          * Fire after click
30369          * @param {Roo.bootstrap.DocumentViewer} this
30370          */
30371         "click" : true,
30372         /**
30373          * @event download
30374          * Fire after download button
30375          * @param {Roo.bootstrap.DocumentViewer} this
30376          */
30377         "download" : true,
30378         /**
30379          * @event trash
30380          * Fire after trash button
30381          * @param {Roo.bootstrap.DocumentViewer} this
30382          */
30383         "trash" : true
30384         
30385     });
30386 };
30387
30388 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30389     
30390     showDownload : true,
30391     
30392     showTrash : true,
30393     
30394     getAutoCreate : function()
30395     {
30396         var cfg = {
30397             tag : 'div',
30398             cls : 'roo-document-viewer',
30399             cn : [
30400                 {
30401                     tag : 'div',
30402                     cls : 'roo-document-viewer-body',
30403                     cn : [
30404                         {
30405                             tag : 'div',
30406                             cls : 'roo-document-viewer-thumb',
30407                             cn : [
30408                                 {
30409                                     tag : 'img',
30410                                     cls : 'roo-document-viewer-image'
30411                                 }
30412                             ]
30413                         }
30414                     ]
30415                 },
30416                 {
30417                     tag : 'div',
30418                     cls : 'roo-document-viewer-footer',
30419                     cn : {
30420                         tag : 'div',
30421                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30422                         cn : [
30423                             {
30424                                 tag : 'div',
30425                                 cls : 'btn-group roo-document-viewer-download',
30426                                 cn : [
30427                                     {
30428                                         tag : 'button',
30429                                         cls : 'btn btn-default',
30430                                         html : '<i class="fa fa-download"></i>'
30431                                     }
30432                                 ]
30433                             },
30434                             {
30435                                 tag : 'div',
30436                                 cls : 'btn-group roo-document-viewer-trash',
30437                                 cn : [
30438                                     {
30439                                         tag : 'button',
30440                                         cls : 'btn btn-default',
30441                                         html : '<i class="fa fa-trash"></i>'
30442                                     }
30443                                 ]
30444                             }
30445                         ]
30446                     }
30447                 }
30448             ]
30449         };
30450         
30451         return cfg;
30452     },
30453     
30454     initEvents : function()
30455     {
30456         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30457         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30458         
30459         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30460         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30461         
30462         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30463         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30464         
30465         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30466         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30467         
30468         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30469         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30470         
30471         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30472         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30473         
30474         this.bodyEl.on('click', this.onClick, this);
30475         this.downloadBtn.on('click', this.onDownload, this);
30476         this.trashBtn.on('click', this.onTrash, this);
30477         
30478         this.downloadBtn.hide();
30479         this.trashBtn.hide();
30480         
30481         if(this.showDownload){
30482             this.downloadBtn.show();
30483         }
30484         
30485         if(this.showTrash){
30486             this.trashBtn.show();
30487         }
30488         
30489         if(!this.showDownload && !this.showTrash) {
30490             this.footerEl.hide();
30491         }
30492         
30493     },
30494     
30495     initial : function()
30496     {
30497         this.fireEvent('initial', this);
30498         
30499     },
30500     
30501     onClick : function(e)
30502     {
30503         e.preventDefault();
30504         
30505         this.fireEvent('click', this);
30506     },
30507     
30508     onDownload : function(e)
30509     {
30510         e.preventDefault();
30511         
30512         this.fireEvent('download', this);
30513     },
30514     
30515     onTrash : function(e)
30516     {
30517         e.preventDefault();
30518         
30519         this.fireEvent('trash', this);
30520     }
30521     
30522 });
30523 /*
30524  * - LGPL
30525  *
30526  * nav progress bar
30527  * 
30528  */
30529
30530 /**
30531  * @class Roo.bootstrap.NavProgressBar
30532  * @extends Roo.bootstrap.Component
30533  * Bootstrap NavProgressBar class
30534  * 
30535  * @constructor
30536  * Create a new nav progress bar
30537  * @param {Object} config The config object
30538  */
30539
30540 Roo.bootstrap.NavProgressBar = function(config){
30541     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30542
30543     this.bullets = this.bullets || [];
30544    
30545 //    Roo.bootstrap.NavProgressBar.register(this);
30546      this.addEvents({
30547         /**
30548              * @event changed
30549              * Fires when the active item changes
30550              * @param {Roo.bootstrap.NavProgressBar} this
30551              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30552              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30553          */
30554         'changed': true
30555      });
30556     
30557 };
30558
30559 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30560     
30561     bullets : [],
30562     barItems : [],
30563     
30564     getAutoCreate : function()
30565     {
30566         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30567         
30568         cfg = {
30569             tag : 'div',
30570             cls : 'roo-navigation-bar-group',
30571             cn : [
30572                 {
30573                     tag : 'div',
30574                     cls : 'roo-navigation-top-bar'
30575                 },
30576                 {
30577                     tag : 'div',
30578                     cls : 'roo-navigation-bullets-bar',
30579                     cn : [
30580                         {
30581                             tag : 'ul',
30582                             cls : 'roo-navigation-bar'
30583                         }
30584                     ]
30585                 },
30586                 
30587                 {
30588                     tag : 'div',
30589                     cls : 'roo-navigation-bottom-bar'
30590                 }
30591             ]
30592             
30593         };
30594         
30595         return cfg;
30596         
30597     },
30598     
30599     initEvents: function() 
30600     {
30601         
30602     },
30603     
30604     onRender : function(ct, position) 
30605     {
30606         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30607         
30608         if(this.bullets.length){
30609             Roo.each(this.bullets, function(b){
30610                this.addItem(b);
30611             }, this);
30612         }
30613         
30614         this.format();
30615         
30616     },
30617     
30618     addItem : function(cfg)
30619     {
30620         var item = new Roo.bootstrap.NavProgressItem(cfg);
30621         
30622         item.parentId = this.id;
30623         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30624         
30625         if(cfg.html){
30626             var top = new Roo.bootstrap.Element({
30627                 tag : 'div',
30628                 cls : 'roo-navigation-bar-text'
30629             });
30630             
30631             var bottom = new Roo.bootstrap.Element({
30632                 tag : 'div',
30633                 cls : 'roo-navigation-bar-text'
30634             });
30635             
30636             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30637             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30638             
30639             var topText = new Roo.bootstrap.Element({
30640                 tag : 'span',
30641                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30642             });
30643             
30644             var bottomText = new Roo.bootstrap.Element({
30645                 tag : 'span',
30646                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30647             });
30648             
30649             topText.onRender(top.el, null);
30650             bottomText.onRender(bottom.el, null);
30651             
30652             item.topEl = top;
30653             item.bottomEl = bottom;
30654         }
30655         
30656         this.barItems.push(item);
30657         
30658         return item;
30659     },
30660     
30661     getActive : function()
30662     {
30663         var active = false;
30664         
30665         Roo.each(this.barItems, function(v){
30666             
30667             if (!v.isActive()) {
30668                 return;
30669             }
30670             
30671             active = v;
30672             return false;
30673             
30674         });
30675         
30676         return active;
30677     },
30678     
30679     setActiveItem : function(item)
30680     {
30681         var prev = false;
30682         
30683         Roo.each(this.barItems, function(v){
30684             if (v.rid == item.rid) {
30685                 return ;
30686             }
30687             
30688             if (v.isActive()) {
30689                 v.setActive(false);
30690                 prev = v;
30691             }
30692         });
30693
30694         item.setActive(true);
30695         
30696         this.fireEvent('changed', this, item, prev);
30697     },
30698     
30699     getBarItem: function(rid)
30700     {
30701         var ret = false;
30702         
30703         Roo.each(this.barItems, function(e) {
30704             if (e.rid != rid) {
30705                 return;
30706             }
30707             
30708             ret =  e;
30709             return false;
30710         });
30711         
30712         return ret;
30713     },
30714     
30715     indexOfItem : function(item)
30716     {
30717         var index = false;
30718         
30719         Roo.each(this.barItems, function(v, i){
30720             
30721             if (v.rid != item.rid) {
30722                 return;
30723             }
30724             
30725             index = i;
30726             return false
30727         });
30728         
30729         return index;
30730     },
30731     
30732     setActiveNext : function()
30733     {
30734         var i = this.indexOfItem(this.getActive());
30735         
30736         if (i > this.barItems.length) {
30737             return;
30738         }
30739         
30740         this.setActiveItem(this.barItems[i+1]);
30741     },
30742     
30743     setActivePrev : function()
30744     {
30745         var i = this.indexOfItem(this.getActive());
30746         
30747         if (i  < 1) {
30748             return;
30749         }
30750         
30751         this.setActiveItem(this.barItems[i-1]);
30752     },
30753     
30754     format : function()
30755     {
30756         if(!this.barItems.length){
30757             return;
30758         }
30759      
30760         var width = 100 / this.barItems.length;
30761         
30762         Roo.each(this.barItems, function(i){
30763             i.el.setStyle('width', width + '%');
30764             i.topEl.el.setStyle('width', width + '%');
30765             i.bottomEl.el.setStyle('width', width + '%');
30766         }, this);
30767         
30768     }
30769     
30770 });
30771 /*
30772  * - LGPL
30773  *
30774  * Nav Progress Item
30775  * 
30776  */
30777
30778 /**
30779  * @class Roo.bootstrap.NavProgressItem
30780  * @extends Roo.bootstrap.Component
30781  * Bootstrap NavProgressItem class
30782  * @cfg {String} rid the reference id
30783  * @cfg {Boolean} active (true|false) Is item active default false
30784  * @cfg {Boolean} disabled (true|false) Is item active default false
30785  * @cfg {String} html
30786  * @cfg {String} position (top|bottom) text position default bottom
30787  * @cfg {String} icon show icon instead of number
30788  * 
30789  * @constructor
30790  * Create a new NavProgressItem
30791  * @param {Object} config The config object
30792  */
30793 Roo.bootstrap.NavProgressItem = function(config){
30794     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30795     this.addEvents({
30796         // raw events
30797         /**
30798          * @event click
30799          * The raw click event for the entire grid.
30800          * @param {Roo.bootstrap.NavProgressItem} this
30801          * @param {Roo.EventObject} e
30802          */
30803         "click" : true
30804     });
30805    
30806 };
30807
30808 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30809     
30810     rid : '',
30811     active : false,
30812     disabled : false,
30813     html : '',
30814     position : 'bottom',
30815     icon : false,
30816     
30817     getAutoCreate : function()
30818     {
30819         var iconCls = 'roo-navigation-bar-item-icon';
30820         
30821         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30822         
30823         var cfg = {
30824             tag: 'li',
30825             cls: 'roo-navigation-bar-item',
30826             cn : [
30827                 {
30828                     tag : 'i',
30829                     cls : iconCls
30830                 }
30831             ]
30832         };
30833         
30834         if(this.active){
30835             cfg.cls += ' active';
30836         }
30837         if(this.disabled){
30838             cfg.cls += ' disabled';
30839         }
30840         
30841         return cfg;
30842     },
30843     
30844     disable : function()
30845     {
30846         this.setDisabled(true);
30847     },
30848     
30849     enable : function()
30850     {
30851         this.setDisabled(false);
30852     },
30853     
30854     initEvents: function() 
30855     {
30856         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30857         
30858         this.iconEl.on('click', this.onClick, this);
30859     },
30860     
30861     onClick : function(e)
30862     {
30863         e.preventDefault();
30864         
30865         if(this.disabled){
30866             return;
30867         }
30868         
30869         if(this.fireEvent('click', this, e) === false){
30870             return;
30871         };
30872         
30873         this.parent().setActiveItem(this);
30874     },
30875     
30876     isActive: function () 
30877     {
30878         return this.active;
30879     },
30880     
30881     setActive : function(state)
30882     {
30883         if(this.active == state){
30884             return;
30885         }
30886         
30887         this.active = state;
30888         
30889         if (state) {
30890             this.el.addClass('active');
30891             return;
30892         }
30893         
30894         this.el.removeClass('active');
30895         
30896         return;
30897     },
30898     
30899     setDisabled : function(state)
30900     {
30901         if(this.disabled == state){
30902             return;
30903         }
30904         
30905         this.disabled = state;
30906         
30907         if (state) {
30908             this.el.addClass('disabled');
30909             return;
30910         }
30911         
30912         this.el.removeClass('disabled');
30913     },
30914     
30915     tooltipEl : function()
30916     {
30917         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30918     }
30919 });
30920  
30921
30922  /*
30923  * - LGPL
30924  *
30925  * FieldLabel
30926  * 
30927  */
30928
30929 /**
30930  * @class Roo.bootstrap.FieldLabel
30931  * @extends Roo.bootstrap.Component
30932  * Bootstrap FieldLabel class
30933  * @cfg {String} html contents of the element
30934  * @cfg {String} tag tag of the element default label
30935  * @cfg {String} cls class of the element
30936  * @cfg {String} target label target 
30937  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30938  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30939  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30940  * @cfg {String} iconTooltip default "This field is required"
30941  * @cfg {String} indicatorpos (left|right) default left
30942  * 
30943  * @constructor
30944  * Create a new FieldLabel
30945  * @param {Object} config The config object
30946  */
30947
30948 Roo.bootstrap.FieldLabel = function(config){
30949     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30950     
30951     this.addEvents({
30952             /**
30953              * @event invalid
30954              * Fires after the field has been marked as invalid.
30955              * @param {Roo.form.FieldLabel} this
30956              * @param {String} msg The validation message
30957              */
30958             invalid : true,
30959             /**
30960              * @event valid
30961              * Fires after the field has been validated with no errors.
30962              * @param {Roo.form.FieldLabel} this
30963              */
30964             valid : true
30965         });
30966 };
30967
30968 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30969     
30970     tag: 'label',
30971     cls: '',
30972     html: '',
30973     target: '',
30974     allowBlank : true,
30975     invalidClass : 'has-warning',
30976     validClass : 'has-success',
30977     iconTooltip : 'This field is required',
30978     indicatorpos : 'left',
30979     
30980     getAutoCreate : function(){
30981         
30982         var cls = "";
30983         if (!this.allowBlank) {
30984             cls  = "visible";
30985         }
30986         
30987         var cfg = {
30988             tag : this.tag,
30989             cls : 'roo-bootstrap-field-label ' + this.cls,
30990             for : this.target,
30991             cn : [
30992                 {
30993                     tag : 'i',
30994                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30995                     tooltip : this.iconTooltip
30996                 },
30997                 {
30998                     tag : 'span',
30999                     html : this.html
31000                 }
31001             ] 
31002         };
31003         
31004         if(this.indicatorpos == 'right'){
31005             var cfg = {
31006                 tag : this.tag,
31007                 cls : 'roo-bootstrap-field-label ' + this.cls,
31008                 for : this.target,
31009                 cn : [
31010                     {
31011                         tag : 'span',
31012                         html : this.html
31013                     },
31014                     {
31015                         tag : 'i',
31016                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31017                         tooltip : this.iconTooltip
31018                     }
31019                 ] 
31020             };
31021         }
31022         
31023         return cfg;
31024     },
31025     
31026     initEvents: function() 
31027     {
31028         Roo.bootstrap.Element.superclass.initEvents.call(this);
31029         
31030         this.indicator = this.indicatorEl();
31031         
31032         if(this.indicator){
31033             this.indicator.removeClass('visible');
31034             this.indicator.addClass('invisible');
31035         }
31036         
31037         Roo.bootstrap.FieldLabel.register(this);
31038     },
31039     
31040     indicatorEl : function()
31041     {
31042         var indicator = this.el.select('i.roo-required-indicator',true).first();
31043         
31044         if(!indicator){
31045             return false;
31046         }
31047         
31048         return indicator;
31049         
31050     },
31051     
31052     /**
31053      * Mark this field as valid
31054      */
31055     markValid : function()
31056     {
31057         if(this.indicator){
31058             this.indicator.removeClass('visible');
31059             this.indicator.addClass('invisible');
31060         }
31061         if (Roo.bootstrap.version == 3) {
31062             this.el.removeClass(this.invalidClass);
31063             this.el.addClass(this.validClass);
31064         } else {
31065             this.el.removeClass('is-invalid');
31066             this.el.addClass('is-valid');
31067         }
31068         
31069         
31070         this.fireEvent('valid', this);
31071     },
31072     
31073     /**
31074      * Mark this field as invalid
31075      * @param {String} msg The validation message
31076      */
31077     markInvalid : function(msg)
31078     {
31079         if(this.indicator){
31080             this.indicator.removeClass('invisible');
31081             this.indicator.addClass('visible');
31082         }
31083           if (Roo.bootstrap.version == 3) {
31084             this.el.removeClass(this.validClass);
31085             this.el.addClass(this.invalidClass);
31086         } else {
31087             this.el.removeClass('is-valid');
31088             this.el.addClass('is-invalid');
31089         }
31090         
31091         
31092         this.fireEvent('invalid', this, msg);
31093     }
31094     
31095    
31096 });
31097
31098 Roo.apply(Roo.bootstrap.FieldLabel, {
31099     
31100     groups: {},
31101     
31102      /**
31103     * register a FieldLabel Group
31104     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31105     */
31106     register : function(label)
31107     {
31108         if(this.groups.hasOwnProperty(label.target)){
31109             return;
31110         }
31111      
31112         this.groups[label.target] = label;
31113         
31114     },
31115     /**
31116     * fetch a FieldLabel Group based on the target
31117     * @param {string} target
31118     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31119     */
31120     get: function(target) {
31121         if (typeof(this.groups[target]) == 'undefined') {
31122             return false;
31123         }
31124         
31125         return this.groups[target] ;
31126     }
31127 });
31128
31129  
31130
31131  /*
31132  * - LGPL
31133  *
31134  * page DateSplitField.
31135  * 
31136  */
31137
31138
31139 /**
31140  * @class Roo.bootstrap.DateSplitField
31141  * @extends Roo.bootstrap.Component
31142  * Bootstrap DateSplitField class
31143  * @cfg {string} fieldLabel - the label associated
31144  * @cfg {Number} labelWidth set the width of label (0-12)
31145  * @cfg {String} labelAlign (top|left)
31146  * @cfg {Boolean} dayAllowBlank (true|false) default false
31147  * @cfg {Boolean} monthAllowBlank (true|false) default false
31148  * @cfg {Boolean} yearAllowBlank (true|false) default false
31149  * @cfg {string} dayPlaceholder 
31150  * @cfg {string} monthPlaceholder
31151  * @cfg {string} yearPlaceholder
31152  * @cfg {string} dayFormat default 'd'
31153  * @cfg {string} monthFormat default 'm'
31154  * @cfg {string} yearFormat default 'Y'
31155  * @cfg {Number} labellg set the width of label (1-12)
31156  * @cfg {Number} labelmd set the width of label (1-12)
31157  * @cfg {Number} labelsm set the width of label (1-12)
31158  * @cfg {Number} labelxs set the width of label (1-12)
31159
31160  *     
31161  * @constructor
31162  * Create a new DateSplitField
31163  * @param {Object} config The config object
31164  */
31165
31166 Roo.bootstrap.DateSplitField = function(config){
31167     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31168     
31169     this.addEvents({
31170         // raw events
31171          /**
31172          * @event years
31173          * getting the data of years
31174          * @param {Roo.bootstrap.DateSplitField} this
31175          * @param {Object} years
31176          */
31177         "years" : true,
31178         /**
31179          * @event days
31180          * getting the data of days
31181          * @param {Roo.bootstrap.DateSplitField} this
31182          * @param {Object} days
31183          */
31184         "days" : true,
31185         /**
31186          * @event invalid
31187          * Fires after the field has been marked as invalid.
31188          * @param {Roo.form.Field} this
31189          * @param {String} msg The validation message
31190          */
31191         invalid : true,
31192        /**
31193          * @event valid
31194          * Fires after the field has been validated with no errors.
31195          * @param {Roo.form.Field} this
31196          */
31197         valid : true
31198     });
31199 };
31200
31201 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31202     
31203     fieldLabel : '',
31204     labelAlign : 'top',
31205     labelWidth : 3,
31206     dayAllowBlank : false,
31207     monthAllowBlank : false,
31208     yearAllowBlank : false,
31209     dayPlaceholder : '',
31210     monthPlaceholder : '',
31211     yearPlaceholder : '',
31212     dayFormat : 'd',
31213     monthFormat : 'm',
31214     yearFormat : 'Y',
31215     isFormField : true,
31216     labellg : 0,
31217     labelmd : 0,
31218     labelsm : 0,
31219     labelxs : 0,
31220     
31221     getAutoCreate : function()
31222     {
31223         var cfg = {
31224             tag : 'div',
31225             cls : 'row roo-date-split-field-group',
31226             cn : [
31227                 {
31228                     tag : 'input',
31229                     type : 'hidden',
31230                     cls : 'form-hidden-field roo-date-split-field-group-value',
31231                     name : this.name
31232                 }
31233             ]
31234         };
31235         
31236         var labelCls = 'col-md-12';
31237         var contentCls = 'col-md-4';
31238         
31239         if(this.fieldLabel){
31240             
31241             var label = {
31242                 tag : 'div',
31243                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31244                 cn : [
31245                     {
31246                         tag : 'label',
31247                         html : this.fieldLabel
31248                     }
31249                 ]
31250             };
31251             
31252             if(this.labelAlign == 'left'){
31253             
31254                 if(this.labelWidth > 12){
31255                     label.style = "width: " + this.labelWidth + 'px';
31256                 }
31257
31258                 if(this.labelWidth < 13 && this.labelmd == 0){
31259                     this.labelmd = this.labelWidth;
31260                 }
31261
31262                 if(this.labellg > 0){
31263                     labelCls = ' col-lg-' + this.labellg;
31264                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31265                 }
31266
31267                 if(this.labelmd > 0){
31268                     labelCls = ' col-md-' + this.labelmd;
31269                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31270                 }
31271
31272                 if(this.labelsm > 0){
31273                     labelCls = ' col-sm-' + this.labelsm;
31274                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31275                 }
31276
31277                 if(this.labelxs > 0){
31278                     labelCls = ' col-xs-' + this.labelxs;
31279                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31280                 }
31281             }
31282             
31283             label.cls += ' ' + labelCls;
31284             
31285             cfg.cn.push(label);
31286         }
31287         
31288         Roo.each(['day', 'month', 'year'], function(t){
31289             cfg.cn.push({
31290                 tag : 'div',
31291                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31292             });
31293         }, this);
31294         
31295         return cfg;
31296     },
31297     
31298     inputEl: function ()
31299     {
31300         return this.el.select('.roo-date-split-field-group-value', true).first();
31301     },
31302     
31303     onRender : function(ct, position) 
31304     {
31305         var _this = this;
31306         
31307         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31308         
31309         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31310         
31311         this.dayField = new Roo.bootstrap.ComboBox({
31312             allowBlank : this.dayAllowBlank,
31313             alwaysQuery : true,
31314             displayField : 'value',
31315             editable : false,
31316             fieldLabel : '',
31317             forceSelection : true,
31318             mode : 'local',
31319             placeholder : this.dayPlaceholder,
31320             selectOnFocus : true,
31321             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31322             triggerAction : 'all',
31323             typeAhead : true,
31324             valueField : 'value',
31325             store : new Roo.data.SimpleStore({
31326                 data : (function() {    
31327                     var days = [];
31328                     _this.fireEvent('days', _this, days);
31329                     return days;
31330                 })(),
31331                 fields : [ 'value' ]
31332             }),
31333             listeners : {
31334                 select : function (_self, record, index)
31335                 {
31336                     _this.setValue(_this.getValue());
31337                 }
31338             }
31339         });
31340
31341         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31342         
31343         this.monthField = new Roo.bootstrap.MonthField({
31344             after : '<i class=\"fa fa-calendar\"></i>',
31345             allowBlank : this.monthAllowBlank,
31346             placeholder : this.monthPlaceholder,
31347             readOnly : true,
31348             listeners : {
31349                 render : function (_self)
31350                 {
31351                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31352                         e.preventDefault();
31353                         _self.focus();
31354                     });
31355                 },
31356                 select : function (_self, oldvalue, newvalue)
31357                 {
31358                     _this.setValue(_this.getValue());
31359                 }
31360             }
31361         });
31362         
31363         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31364         
31365         this.yearField = new Roo.bootstrap.ComboBox({
31366             allowBlank : this.yearAllowBlank,
31367             alwaysQuery : true,
31368             displayField : 'value',
31369             editable : false,
31370             fieldLabel : '',
31371             forceSelection : true,
31372             mode : 'local',
31373             placeholder : this.yearPlaceholder,
31374             selectOnFocus : true,
31375             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31376             triggerAction : 'all',
31377             typeAhead : true,
31378             valueField : 'value',
31379             store : new Roo.data.SimpleStore({
31380                 data : (function() {
31381                     var years = [];
31382                     _this.fireEvent('years', _this, years);
31383                     return years;
31384                 })(),
31385                 fields : [ 'value' ]
31386             }),
31387             listeners : {
31388                 select : function (_self, record, index)
31389                 {
31390                     _this.setValue(_this.getValue());
31391                 }
31392             }
31393         });
31394
31395         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31396     },
31397     
31398     setValue : function(v, format)
31399     {
31400         this.inputEl.dom.value = v;
31401         
31402         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31403         
31404         var d = Date.parseDate(v, f);
31405         
31406         if(!d){
31407             this.validate();
31408             return;
31409         }
31410         
31411         this.setDay(d.format(this.dayFormat));
31412         this.setMonth(d.format(this.monthFormat));
31413         this.setYear(d.format(this.yearFormat));
31414         
31415         this.validate();
31416         
31417         return;
31418     },
31419     
31420     setDay : function(v)
31421     {
31422         this.dayField.setValue(v);
31423         this.inputEl.dom.value = this.getValue();
31424         this.validate();
31425         return;
31426     },
31427     
31428     setMonth : function(v)
31429     {
31430         this.monthField.setValue(v, true);
31431         this.inputEl.dom.value = this.getValue();
31432         this.validate();
31433         return;
31434     },
31435     
31436     setYear : function(v)
31437     {
31438         this.yearField.setValue(v);
31439         this.inputEl.dom.value = this.getValue();
31440         this.validate();
31441         return;
31442     },
31443     
31444     getDay : function()
31445     {
31446         return this.dayField.getValue();
31447     },
31448     
31449     getMonth : function()
31450     {
31451         return this.monthField.getValue();
31452     },
31453     
31454     getYear : function()
31455     {
31456         return this.yearField.getValue();
31457     },
31458     
31459     getValue : function()
31460     {
31461         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31462         
31463         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31464         
31465         return date;
31466     },
31467     
31468     reset : function()
31469     {
31470         this.setDay('');
31471         this.setMonth('');
31472         this.setYear('');
31473         this.inputEl.dom.value = '';
31474         this.validate();
31475         return;
31476     },
31477     
31478     validate : function()
31479     {
31480         var d = this.dayField.validate();
31481         var m = this.monthField.validate();
31482         var y = this.yearField.validate();
31483         
31484         var valid = true;
31485         
31486         if(
31487                 (!this.dayAllowBlank && !d) ||
31488                 (!this.monthAllowBlank && !m) ||
31489                 (!this.yearAllowBlank && !y)
31490         ){
31491             valid = false;
31492         }
31493         
31494         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31495             return valid;
31496         }
31497         
31498         if(valid){
31499             this.markValid();
31500             return valid;
31501         }
31502         
31503         this.markInvalid();
31504         
31505         return valid;
31506     },
31507     
31508     markValid : function()
31509     {
31510         
31511         var label = this.el.select('label', true).first();
31512         var icon = this.el.select('i.fa-star', true).first();
31513
31514         if(label && icon){
31515             icon.remove();
31516         }
31517         
31518         this.fireEvent('valid', this);
31519     },
31520     
31521      /**
31522      * Mark this field as invalid
31523      * @param {String} msg The validation message
31524      */
31525     markInvalid : function(msg)
31526     {
31527         
31528         var label = this.el.select('label', true).first();
31529         var icon = this.el.select('i.fa-star', true).first();
31530
31531         if(label && !icon){
31532             this.el.select('.roo-date-split-field-label', true).createChild({
31533                 tag : 'i',
31534                 cls : 'text-danger fa fa-lg fa-star',
31535                 tooltip : 'This field is required',
31536                 style : 'margin-right:5px;'
31537             }, label, true);
31538         }
31539         
31540         this.fireEvent('invalid', this, msg);
31541     },
31542     
31543     clearInvalid : function()
31544     {
31545         var label = this.el.select('label', true).first();
31546         var icon = this.el.select('i.fa-star', true).first();
31547
31548         if(label && icon){
31549             icon.remove();
31550         }
31551         
31552         this.fireEvent('valid', this);
31553     },
31554     
31555     getName: function()
31556     {
31557         return this.name;
31558     }
31559     
31560 });
31561
31562  /**
31563  *
31564  * This is based on 
31565  * http://masonry.desandro.com
31566  *
31567  * The idea is to render all the bricks based on vertical width...
31568  *
31569  * The original code extends 'outlayer' - we might need to use that....
31570  * 
31571  */
31572
31573
31574 /**
31575  * @class Roo.bootstrap.LayoutMasonry
31576  * @extends Roo.bootstrap.Component
31577  * Bootstrap Layout Masonry class
31578  * 
31579  * @constructor
31580  * Create a new Element
31581  * @param {Object} config The config object
31582  */
31583
31584 Roo.bootstrap.LayoutMasonry = function(config){
31585     
31586     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31587     
31588     this.bricks = [];
31589     
31590     Roo.bootstrap.LayoutMasonry.register(this);
31591     
31592     this.addEvents({
31593         // raw events
31594         /**
31595          * @event layout
31596          * Fire after layout the items
31597          * @param {Roo.bootstrap.LayoutMasonry} this
31598          * @param {Roo.EventObject} e
31599          */
31600         "layout" : true
31601     });
31602     
31603 };
31604
31605 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31606     
31607     /**
31608      * @cfg {Boolean} isLayoutInstant = no animation?
31609      */   
31610     isLayoutInstant : false, // needed?
31611    
31612     /**
31613      * @cfg {Number} boxWidth  width of the columns
31614      */   
31615     boxWidth : 450,
31616     
31617       /**
31618      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31619      */   
31620     boxHeight : 0,
31621     
31622     /**
31623      * @cfg {Number} padWidth padding below box..
31624      */   
31625     padWidth : 10, 
31626     
31627     /**
31628      * @cfg {Number} gutter gutter width..
31629      */   
31630     gutter : 10,
31631     
31632      /**
31633      * @cfg {Number} maxCols maximum number of columns
31634      */   
31635     
31636     maxCols: 0,
31637     
31638     /**
31639      * @cfg {Boolean} isAutoInitial defalut true
31640      */   
31641     isAutoInitial : true, 
31642     
31643     containerWidth: 0,
31644     
31645     /**
31646      * @cfg {Boolean} isHorizontal defalut false
31647      */   
31648     isHorizontal : false, 
31649
31650     currentSize : null,
31651     
31652     tag: 'div',
31653     
31654     cls: '',
31655     
31656     bricks: null, //CompositeElement
31657     
31658     cols : 1,
31659     
31660     _isLayoutInited : false,
31661     
31662 //    isAlternative : false, // only use for vertical layout...
31663     
31664     /**
31665      * @cfg {Number} alternativePadWidth padding below box..
31666      */   
31667     alternativePadWidth : 50,
31668     
31669     selectedBrick : [],
31670     
31671     getAutoCreate : function(){
31672         
31673         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31674         
31675         var cfg = {
31676             tag: this.tag,
31677             cls: 'blog-masonary-wrapper ' + this.cls,
31678             cn : {
31679                 cls : 'mas-boxes masonary'
31680             }
31681         };
31682         
31683         return cfg;
31684     },
31685     
31686     getChildContainer: function( )
31687     {
31688         if (this.boxesEl) {
31689             return this.boxesEl;
31690         }
31691         
31692         this.boxesEl = this.el.select('.mas-boxes').first();
31693         
31694         return this.boxesEl;
31695     },
31696     
31697     
31698     initEvents : function()
31699     {
31700         var _this = this;
31701         
31702         if(this.isAutoInitial){
31703             Roo.log('hook children rendered');
31704             this.on('childrenrendered', function() {
31705                 Roo.log('children rendered');
31706                 _this.initial();
31707             } ,this);
31708         }
31709     },
31710     
31711     initial : function()
31712     {
31713         this.selectedBrick = [];
31714         
31715         this.currentSize = this.el.getBox(true);
31716         
31717         Roo.EventManager.onWindowResize(this.resize, this); 
31718
31719         if(!this.isAutoInitial){
31720             this.layout();
31721             return;
31722         }
31723         
31724         this.layout();
31725         
31726         return;
31727         //this.layout.defer(500,this);
31728         
31729     },
31730     
31731     resize : function()
31732     {
31733         var cs = this.el.getBox(true);
31734         
31735         if (
31736                 this.currentSize.width == cs.width && 
31737                 this.currentSize.x == cs.x && 
31738                 this.currentSize.height == cs.height && 
31739                 this.currentSize.y == cs.y 
31740         ) {
31741             Roo.log("no change in with or X or Y");
31742             return;
31743         }
31744         
31745         this.currentSize = cs;
31746         
31747         this.layout();
31748         
31749     },
31750     
31751     layout : function()
31752     {   
31753         this._resetLayout();
31754         
31755         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31756         
31757         this.layoutItems( isInstant );
31758       
31759         this._isLayoutInited = true;
31760         
31761         this.fireEvent('layout', this);
31762         
31763     },
31764     
31765     _resetLayout : function()
31766     {
31767         if(this.isHorizontal){
31768             this.horizontalMeasureColumns();
31769             return;
31770         }
31771         
31772         this.verticalMeasureColumns();
31773         
31774     },
31775     
31776     verticalMeasureColumns : function()
31777     {
31778         this.getContainerWidth();
31779         
31780 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31781 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31782 //            return;
31783 //        }
31784         
31785         var boxWidth = this.boxWidth + this.padWidth;
31786         
31787         if(this.containerWidth < this.boxWidth){
31788             boxWidth = this.containerWidth
31789         }
31790         
31791         var containerWidth = this.containerWidth;
31792         
31793         var cols = Math.floor(containerWidth / boxWidth);
31794         
31795         this.cols = Math.max( cols, 1 );
31796         
31797         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31798         
31799         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31800         
31801         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31802         
31803         this.colWidth = boxWidth + avail - this.padWidth;
31804         
31805         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31806         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31807     },
31808     
31809     horizontalMeasureColumns : function()
31810     {
31811         this.getContainerWidth();
31812         
31813         var boxWidth = this.boxWidth;
31814         
31815         if(this.containerWidth < boxWidth){
31816             boxWidth = this.containerWidth;
31817         }
31818         
31819         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31820         
31821         this.el.setHeight(boxWidth);
31822         
31823     },
31824     
31825     getContainerWidth : function()
31826     {
31827         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31828     },
31829     
31830     layoutItems : function( isInstant )
31831     {
31832         Roo.log(this.bricks);
31833         
31834         var items = Roo.apply([], this.bricks);
31835         
31836         if(this.isHorizontal){
31837             this._horizontalLayoutItems( items , isInstant );
31838             return;
31839         }
31840         
31841 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31842 //            this._verticalAlternativeLayoutItems( items , isInstant );
31843 //            return;
31844 //        }
31845         
31846         this._verticalLayoutItems( items , isInstant );
31847         
31848     },
31849     
31850     _verticalLayoutItems : function ( items , isInstant)
31851     {
31852         if ( !items || !items.length ) {
31853             return;
31854         }
31855         
31856         var standard = [
31857             ['xs', 'xs', 'xs', 'tall'],
31858             ['xs', 'xs', 'tall'],
31859             ['xs', 'xs', 'sm'],
31860             ['xs', 'xs', 'xs'],
31861             ['xs', 'tall'],
31862             ['xs', 'sm'],
31863             ['xs', 'xs'],
31864             ['xs'],
31865             
31866             ['sm', 'xs', 'xs'],
31867             ['sm', 'xs'],
31868             ['sm'],
31869             
31870             ['tall', 'xs', 'xs', 'xs'],
31871             ['tall', 'xs', 'xs'],
31872             ['tall', 'xs'],
31873             ['tall']
31874             
31875         ];
31876         
31877         var queue = [];
31878         
31879         var boxes = [];
31880         
31881         var box = [];
31882         
31883         Roo.each(items, function(item, k){
31884             
31885             switch (item.size) {
31886                 // these layouts take up a full box,
31887                 case 'md' :
31888                 case 'md-left' :
31889                 case 'md-right' :
31890                 case 'wide' :
31891                     
31892                     if(box.length){
31893                         boxes.push(box);
31894                         box = [];
31895                     }
31896                     
31897                     boxes.push([item]);
31898                     
31899                     break;
31900                     
31901                 case 'xs' :
31902                 case 'sm' :
31903                 case 'tall' :
31904                     
31905                     box.push(item);
31906                     
31907                     break;
31908                 default :
31909                     break;
31910                     
31911             }
31912             
31913         }, this);
31914         
31915         if(box.length){
31916             boxes.push(box);
31917             box = [];
31918         }
31919         
31920         var filterPattern = function(box, length)
31921         {
31922             if(!box.length){
31923                 return;
31924             }
31925             
31926             var match = false;
31927             
31928             var pattern = box.slice(0, length);
31929             
31930             var format = [];
31931             
31932             Roo.each(pattern, function(i){
31933                 format.push(i.size);
31934             }, this);
31935             
31936             Roo.each(standard, function(s){
31937                 
31938                 if(String(s) != String(format)){
31939                     return;
31940                 }
31941                 
31942                 match = true;
31943                 return false;
31944                 
31945             }, this);
31946             
31947             if(!match && length == 1){
31948                 return;
31949             }
31950             
31951             if(!match){
31952                 filterPattern(box, length - 1);
31953                 return;
31954             }
31955                 
31956             queue.push(pattern);
31957
31958             box = box.slice(length, box.length);
31959
31960             filterPattern(box, 4);
31961
31962             return;
31963             
31964         }
31965         
31966         Roo.each(boxes, function(box, k){
31967             
31968             if(!box.length){
31969                 return;
31970             }
31971             
31972             if(box.length == 1){
31973                 queue.push(box);
31974                 return;
31975             }
31976             
31977             filterPattern(box, 4);
31978             
31979         }, this);
31980         
31981         this._processVerticalLayoutQueue( queue, isInstant );
31982         
31983     },
31984     
31985 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31986 //    {
31987 //        if ( !items || !items.length ) {
31988 //            return;
31989 //        }
31990 //
31991 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31992 //        
31993 //    },
31994     
31995     _horizontalLayoutItems : function ( items , isInstant)
31996     {
31997         if ( !items || !items.length || items.length < 3) {
31998             return;
31999         }
32000         
32001         items.reverse();
32002         
32003         var eItems = items.slice(0, 3);
32004         
32005         items = items.slice(3, items.length);
32006         
32007         var standard = [
32008             ['xs', 'xs', 'xs', 'wide'],
32009             ['xs', 'xs', 'wide'],
32010             ['xs', 'xs', 'sm'],
32011             ['xs', 'xs', 'xs'],
32012             ['xs', 'wide'],
32013             ['xs', 'sm'],
32014             ['xs', 'xs'],
32015             ['xs'],
32016             
32017             ['sm', 'xs', 'xs'],
32018             ['sm', 'xs'],
32019             ['sm'],
32020             
32021             ['wide', 'xs', 'xs', 'xs'],
32022             ['wide', 'xs', 'xs'],
32023             ['wide', 'xs'],
32024             ['wide'],
32025             
32026             ['wide-thin']
32027         ];
32028         
32029         var queue = [];
32030         
32031         var boxes = [];
32032         
32033         var box = [];
32034         
32035         Roo.each(items, function(item, k){
32036             
32037             switch (item.size) {
32038                 case 'md' :
32039                 case 'md-left' :
32040                 case 'md-right' :
32041                 case 'tall' :
32042                     
32043                     if(box.length){
32044                         boxes.push(box);
32045                         box = [];
32046                     }
32047                     
32048                     boxes.push([item]);
32049                     
32050                     break;
32051                     
32052                 case 'xs' :
32053                 case 'sm' :
32054                 case 'wide' :
32055                 case 'wide-thin' :
32056                     
32057                     box.push(item);
32058                     
32059                     break;
32060                 default :
32061                     break;
32062                     
32063             }
32064             
32065         }, this);
32066         
32067         if(box.length){
32068             boxes.push(box);
32069             box = [];
32070         }
32071         
32072         var filterPattern = function(box, length)
32073         {
32074             if(!box.length){
32075                 return;
32076             }
32077             
32078             var match = false;
32079             
32080             var pattern = box.slice(0, length);
32081             
32082             var format = [];
32083             
32084             Roo.each(pattern, function(i){
32085                 format.push(i.size);
32086             }, this);
32087             
32088             Roo.each(standard, function(s){
32089                 
32090                 if(String(s) != String(format)){
32091                     return;
32092                 }
32093                 
32094                 match = true;
32095                 return false;
32096                 
32097             }, this);
32098             
32099             if(!match && length == 1){
32100                 return;
32101             }
32102             
32103             if(!match){
32104                 filterPattern(box, length - 1);
32105                 return;
32106             }
32107                 
32108             queue.push(pattern);
32109
32110             box = box.slice(length, box.length);
32111
32112             filterPattern(box, 4);
32113
32114             return;
32115             
32116         }
32117         
32118         Roo.each(boxes, function(box, k){
32119             
32120             if(!box.length){
32121                 return;
32122             }
32123             
32124             if(box.length == 1){
32125                 queue.push(box);
32126                 return;
32127             }
32128             
32129             filterPattern(box, 4);
32130             
32131         }, this);
32132         
32133         
32134         var prune = [];
32135         
32136         var pos = this.el.getBox(true);
32137         
32138         var minX = pos.x;
32139         
32140         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32141         
32142         var hit_end = false;
32143         
32144         Roo.each(queue, function(box){
32145             
32146             if(hit_end){
32147                 
32148                 Roo.each(box, function(b){
32149                 
32150                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32151                     b.el.hide();
32152
32153                 }, this);
32154
32155                 return;
32156             }
32157             
32158             var mx = 0;
32159             
32160             Roo.each(box, function(b){
32161                 
32162                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32163                 b.el.show();
32164
32165                 mx = Math.max(mx, b.x);
32166                 
32167             }, this);
32168             
32169             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32170             
32171             if(maxX < minX){
32172                 
32173                 Roo.each(box, function(b){
32174                 
32175                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32176                     b.el.hide();
32177                     
32178                 }, this);
32179                 
32180                 hit_end = true;
32181                 
32182                 return;
32183             }
32184             
32185             prune.push(box);
32186             
32187         }, this);
32188         
32189         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32190     },
32191     
32192     /** Sets position of item in DOM
32193     * @param {Element} item
32194     * @param {Number} x - horizontal position
32195     * @param {Number} y - vertical position
32196     * @param {Boolean} isInstant - disables transitions
32197     */
32198     _processVerticalLayoutQueue : function( queue, isInstant )
32199     {
32200         var pos = this.el.getBox(true);
32201         var x = pos.x;
32202         var y = pos.y;
32203         var maxY = [];
32204         
32205         for (var i = 0; i < this.cols; i++){
32206             maxY[i] = pos.y;
32207         }
32208         
32209         Roo.each(queue, function(box, k){
32210             
32211             var col = k % this.cols;
32212             
32213             Roo.each(box, function(b,kk){
32214                 
32215                 b.el.position('absolute');
32216                 
32217                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32218                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32219                 
32220                 if(b.size == 'md-left' || b.size == 'md-right'){
32221                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32222                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32223                 }
32224                 
32225                 b.el.setWidth(width);
32226                 b.el.setHeight(height);
32227                 // iframe?
32228                 b.el.select('iframe',true).setSize(width,height);
32229                 
32230             }, this);
32231             
32232             for (var i = 0; i < this.cols; i++){
32233                 
32234                 if(maxY[i] < maxY[col]){
32235                     col = i;
32236                     continue;
32237                 }
32238                 
32239                 col = Math.min(col, i);
32240                 
32241             }
32242             
32243             x = pos.x + col * (this.colWidth + this.padWidth);
32244             
32245             y = maxY[col];
32246             
32247             var positions = [];
32248             
32249             switch (box.length){
32250                 case 1 :
32251                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32252                     break;
32253                 case 2 :
32254                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32255                     break;
32256                 case 3 :
32257                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32258                     break;
32259                 case 4 :
32260                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32261                     break;
32262                 default :
32263                     break;
32264             }
32265             
32266             Roo.each(box, function(b,kk){
32267                 
32268                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32269                 
32270                 var sz = b.el.getSize();
32271                 
32272                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32273                 
32274             }, this);
32275             
32276         }, this);
32277         
32278         var mY = 0;
32279         
32280         for (var i = 0; i < this.cols; i++){
32281             mY = Math.max(mY, maxY[i]);
32282         }
32283         
32284         this.el.setHeight(mY - pos.y);
32285         
32286     },
32287     
32288 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32289 //    {
32290 //        var pos = this.el.getBox(true);
32291 //        var x = pos.x;
32292 //        var y = pos.y;
32293 //        var maxX = pos.right;
32294 //        
32295 //        var maxHeight = 0;
32296 //        
32297 //        Roo.each(items, function(item, k){
32298 //            
32299 //            var c = k % 2;
32300 //            
32301 //            item.el.position('absolute');
32302 //                
32303 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32304 //
32305 //            item.el.setWidth(width);
32306 //
32307 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32308 //
32309 //            item.el.setHeight(height);
32310 //            
32311 //            if(c == 0){
32312 //                item.el.setXY([x, y], isInstant ? false : true);
32313 //            } else {
32314 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32315 //            }
32316 //            
32317 //            y = y + height + this.alternativePadWidth;
32318 //            
32319 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32320 //            
32321 //        }, this);
32322 //        
32323 //        this.el.setHeight(maxHeight);
32324 //        
32325 //    },
32326     
32327     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32328     {
32329         var pos = this.el.getBox(true);
32330         
32331         var minX = pos.x;
32332         var minY = pos.y;
32333         
32334         var maxX = pos.right;
32335         
32336         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32337         
32338         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32339         
32340         Roo.each(queue, function(box, k){
32341             
32342             Roo.each(box, function(b, kk){
32343                 
32344                 b.el.position('absolute');
32345                 
32346                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32347                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32348                 
32349                 if(b.size == 'md-left' || b.size == 'md-right'){
32350                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32351                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32352                 }
32353                 
32354                 b.el.setWidth(width);
32355                 b.el.setHeight(height);
32356                 
32357             }, this);
32358             
32359             if(!box.length){
32360                 return;
32361             }
32362             
32363             var positions = [];
32364             
32365             switch (box.length){
32366                 case 1 :
32367                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32368                     break;
32369                 case 2 :
32370                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32371                     break;
32372                 case 3 :
32373                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32374                     break;
32375                 case 4 :
32376                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32377                     break;
32378                 default :
32379                     break;
32380             }
32381             
32382             Roo.each(box, function(b,kk){
32383                 
32384                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32385                 
32386                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32387                 
32388             }, this);
32389             
32390         }, this);
32391         
32392     },
32393     
32394     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32395     {
32396         Roo.each(eItems, function(b,k){
32397             
32398             b.size = (k == 0) ? 'sm' : 'xs';
32399             b.x = (k == 0) ? 2 : 1;
32400             b.y = (k == 0) ? 2 : 1;
32401             
32402             b.el.position('absolute');
32403             
32404             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32405                 
32406             b.el.setWidth(width);
32407             
32408             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32409             
32410             b.el.setHeight(height);
32411             
32412         }, this);
32413
32414         var positions = [];
32415         
32416         positions.push({
32417             x : maxX - this.unitWidth * 2 - this.gutter,
32418             y : minY
32419         });
32420         
32421         positions.push({
32422             x : maxX - this.unitWidth,
32423             y : minY + (this.unitWidth + this.gutter) * 2
32424         });
32425         
32426         positions.push({
32427             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32428             y : minY
32429         });
32430         
32431         Roo.each(eItems, function(b,k){
32432             
32433             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32434
32435         }, this);
32436         
32437     },
32438     
32439     getVerticalOneBoxColPositions : function(x, y, box)
32440     {
32441         var pos = [];
32442         
32443         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32444         
32445         if(box[0].size == 'md-left'){
32446             rand = 0;
32447         }
32448         
32449         if(box[0].size == 'md-right'){
32450             rand = 1;
32451         }
32452         
32453         pos.push({
32454             x : x + (this.unitWidth + this.gutter) * rand,
32455             y : y
32456         });
32457         
32458         return pos;
32459     },
32460     
32461     getVerticalTwoBoxColPositions : function(x, y, box)
32462     {
32463         var pos = [];
32464         
32465         if(box[0].size == 'xs'){
32466             
32467             pos.push({
32468                 x : x,
32469                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32470             });
32471
32472             pos.push({
32473                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32474                 y : y
32475             });
32476             
32477             return pos;
32478             
32479         }
32480         
32481         pos.push({
32482             x : x,
32483             y : y
32484         });
32485
32486         pos.push({
32487             x : x + (this.unitWidth + this.gutter) * 2,
32488             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32489         });
32490         
32491         return pos;
32492         
32493     },
32494     
32495     getVerticalThreeBoxColPositions : function(x, y, box)
32496     {
32497         var pos = [];
32498         
32499         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32500             
32501             pos.push({
32502                 x : x,
32503                 y : y
32504             });
32505
32506             pos.push({
32507                 x : x + (this.unitWidth + this.gutter) * 1,
32508                 y : y
32509             });
32510             
32511             pos.push({
32512                 x : x + (this.unitWidth + this.gutter) * 2,
32513                 y : y
32514             });
32515             
32516             return pos;
32517             
32518         }
32519         
32520         if(box[0].size == 'xs' && box[1].size == 'xs'){
32521             
32522             pos.push({
32523                 x : x,
32524                 y : y
32525             });
32526
32527             pos.push({
32528                 x : x,
32529                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32530             });
32531             
32532             pos.push({
32533                 x : x + (this.unitWidth + this.gutter) * 1,
32534                 y : y
32535             });
32536             
32537             return pos;
32538             
32539         }
32540         
32541         pos.push({
32542             x : x,
32543             y : y
32544         });
32545
32546         pos.push({
32547             x : x + (this.unitWidth + this.gutter) * 2,
32548             y : y
32549         });
32550
32551         pos.push({
32552             x : x + (this.unitWidth + this.gutter) * 2,
32553             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32554         });
32555             
32556         return pos;
32557         
32558     },
32559     
32560     getVerticalFourBoxColPositions : function(x, y, box)
32561     {
32562         var pos = [];
32563         
32564         if(box[0].size == 'xs'){
32565             
32566             pos.push({
32567                 x : x,
32568                 y : y
32569             });
32570
32571             pos.push({
32572                 x : x,
32573                 y : y + (this.unitHeight + this.gutter) * 1
32574             });
32575             
32576             pos.push({
32577                 x : x,
32578                 y : y + (this.unitHeight + this.gutter) * 2
32579             });
32580             
32581             pos.push({
32582                 x : x + (this.unitWidth + this.gutter) * 1,
32583                 y : y
32584             });
32585             
32586             return pos;
32587             
32588         }
32589         
32590         pos.push({
32591             x : x,
32592             y : y
32593         });
32594
32595         pos.push({
32596             x : x + (this.unitWidth + this.gutter) * 2,
32597             y : y
32598         });
32599
32600         pos.push({
32601             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32602             y : y + (this.unitHeight + this.gutter) * 1
32603         });
32604
32605         pos.push({
32606             x : x + (this.unitWidth + this.gutter) * 2,
32607             y : y + (this.unitWidth + this.gutter) * 2
32608         });
32609
32610         return pos;
32611         
32612     },
32613     
32614     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32615     {
32616         var pos = [];
32617         
32618         if(box[0].size == 'md-left'){
32619             pos.push({
32620                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32621                 y : minY
32622             });
32623             
32624             return pos;
32625         }
32626         
32627         if(box[0].size == 'md-right'){
32628             pos.push({
32629                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32630                 y : minY + (this.unitWidth + this.gutter) * 1
32631             });
32632             
32633             return pos;
32634         }
32635         
32636         var rand = Math.floor(Math.random() * (4 - box[0].y));
32637         
32638         pos.push({
32639             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32640             y : minY + (this.unitWidth + this.gutter) * rand
32641         });
32642         
32643         return pos;
32644         
32645     },
32646     
32647     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32648     {
32649         var pos = [];
32650         
32651         if(box[0].size == 'xs'){
32652             
32653             pos.push({
32654                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32655                 y : minY
32656             });
32657
32658             pos.push({
32659                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32660                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32661             });
32662             
32663             return pos;
32664             
32665         }
32666         
32667         pos.push({
32668             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32669             y : minY
32670         });
32671
32672         pos.push({
32673             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32674             y : minY + (this.unitWidth + this.gutter) * 2
32675         });
32676         
32677         return pos;
32678         
32679     },
32680     
32681     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32682     {
32683         var pos = [];
32684         
32685         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32686             
32687             pos.push({
32688                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32689                 y : minY
32690             });
32691
32692             pos.push({
32693                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32694                 y : minY + (this.unitWidth + this.gutter) * 1
32695             });
32696             
32697             pos.push({
32698                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32699                 y : minY + (this.unitWidth + this.gutter) * 2
32700             });
32701             
32702             return pos;
32703             
32704         }
32705         
32706         if(box[0].size == 'xs' && box[1].size == 'xs'){
32707             
32708             pos.push({
32709                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32710                 y : minY
32711             });
32712
32713             pos.push({
32714                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32715                 y : minY
32716             });
32717             
32718             pos.push({
32719                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32720                 y : minY + (this.unitWidth + this.gutter) * 1
32721             });
32722             
32723             return pos;
32724             
32725         }
32726         
32727         pos.push({
32728             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32729             y : minY
32730         });
32731
32732         pos.push({
32733             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32734             y : minY + (this.unitWidth + this.gutter) * 2
32735         });
32736
32737         pos.push({
32738             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32739             y : minY + (this.unitWidth + this.gutter) * 2
32740         });
32741             
32742         return pos;
32743         
32744     },
32745     
32746     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32747     {
32748         var pos = [];
32749         
32750         if(box[0].size == 'xs'){
32751             
32752             pos.push({
32753                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32754                 y : minY
32755             });
32756
32757             pos.push({
32758                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32759                 y : minY
32760             });
32761             
32762             pos.push({
32763                 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),
32764                 y : minY
32765             });
32766             
32767             pos.push({
32768                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32769                 y : minY + (this.unitWidth + this.gutter) * 1
32770             });
32771             
32772             return pos;
32773             
32774         }
32775         
32776         pos.push({
32777             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32778             y : minY
32779         });
32780         
32781         pos.push({
32782             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32783             y : minY + (this.unitWidth + this.gutter) * 2
32784         });
32785         
32786         pos.push({
32787             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32788             y : minY + (this.unitWidth + this.gutter) * 2
32789         });
32790         
32791         pos.push({
32792             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),
32793             y : minY + (this.unitWidth + this.gutter) * 2
32794         });
32795
32796         return pos;
32797         
32798     },
32799     
32800     /**
32801     * remove a Masonry Brick
32802     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32803     */
32804     removeBrick : function(brick_id)
32805     {
32806         if (!brick_id) {
32807             return;
32808         }
32809         
32810         for (var i = 0; i<this.bricks.length; i++) {
32811             if (this.bricks[i].id == brick_id) {
32812                 this.bricks.splice(i,1);
32813                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32814                 this.initial();
32815             }
32816         }
32817     },
32818     
32819     /**
32820     * adds a Masonry Brick
32821     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32822     */
32823     addBrick : function(cfg)
32824     {
32825         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32826         //this.register(cn);
32827         cn.parentId = this.id;
32828         cn.render(this.el);
32829         return cn;
32830     },
32831     
32832     /**
32833     * register a Masonry Brick
32834     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32835     */
32836     
32837     register : function(brick)
32838     {
32839         this.bricks.push(brick);
32840         brick.masonryId = this.id;
32841     },
32842     
32843     /**
32844     * clear all the Masonry Brick
32845     */
32846     clearAll : function()
32847     {
32848         this.bricks = [];
32849         //this.getChildContainer().dom.innerHTML = "";
32850         this.el.dom.innerHTML = '';
32851     },
32852     
32853     getSelected : function()
32854     {
32855         if (!this.selectedBrick) {
32856             return false;
32857         }
32858         
32859         return this.selectedBrick;
32860     }
32861 });
32862
32863 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32864     
32865     groups: {},
32866      /**
32867     * register a Masonry Layout
32868     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32869     */
32870     
32871     register : function(layout)
32872     {
32873         this.groups[layout.id] = layout;
32874     },
32875     /**
32876     * fetch a  Masonry Layout based on the masonry layout ID
32877     * @param {string} the masonry layout to add
32878     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32879     */
32880     
32881     get: function(layout_id) {
32882         if (typeof(this.groups[layout_id]) == 'undefined') {
32883             return false;
32884         }
32885         return this.groups[layout_id] ;
32886     }
32887     
32888     
32889     
32890 });
32891
32892  
32893
32894  /**
32895  *
32896  * This is based on 
32897  * http://masonry.desandro.com
32898  *
32899  * The idea is to render all the bricks based on vertical width...
32900  *
32901  * The original code extends 'outlayer' - we might need to use that....
32902  * 
32903  */
32904
32905
32906 /**
32907  * @class Roo.bootstrap.LayoutMasonryAuto
32908  * @extends Roo.bootstrap.Component
32909  * Bootstrap Layout Masonry class
32910  * 
32911  * @constructor
32912  * Create a new Element
32913  * @param {Object} config The config object
32914  */
32915
32916 Roo.bootstrap.LayoutMasonryAuto = function(config){
32917     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32918 };
32919
32920 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32921     
32922       /**
32923      * @cfg {Boolean} isFitWidth  - resize the width..
32924      */   
32925     isFitWidth : false,  // options..
32926     /**
32927      * @cfg {Boolean} isOriginLeft = left align?
32928      */   
32929     isOriginLeft : true,
32930     /**
32931      * @cfg {Boolean} isOriginTop = top align?
32932      */   
32933     isOriginTop : false,
32934     /**
32935      * @cfg {Boolean} isLayoutInstant = no animation?
32936      */   
32937     isLayoutInstant : false, // needed?
32938     /**
32939      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32940      */   
32941     isResizingContainer : true,
32942     /**
32943      * @cfg {Number} columnWidth  width of the columns 
32944      */   
32945     
32946     columnWidth : 0,
32947     
32948     /**
32949      * @cfg {Number} maxCols maximum number of columns
32950      */   
32951     
32952     maxCols: 0,
32953     /**
32954      * @cfg {Number} padHeight padding below box..
32955      */   
32956     
32957     padHeight : 10, 
32958     
32959     /**
32960      * @cfg {Boolean} isAutoInitial defalut true
32961      */   
32962     
32963     isAutoInitial : true, 
32964     
32965     // private?
32966     gutter : 0,
32967     
32968     containerWidth: 0,
32969     initialColumnWidth : 0,
32970     currentSize : null,
32971     
32972     colYs : null, // array.
32973     maxY : 0,
32974     padWidth: 10,
32975     
32976     
32977     tag: 'div',
32978     cls: '',
32979     bricks: null, //CompositeElement
32980     cols : 0, // array?
32981     // element : null, // wrapped now this.el
32982     _isLayoutInited : null, 
32983     
32984     
32985     getAutoCreate : function(){
32986         
32987         var cfg = {
32988             tag: this.tag,
32989             cls: 'blog-masonary-wrapper ' + this.cls,
32990             cn : {
32991                 cls : 'mas-boxes masonary'
32992             }
32993         };
32994         
32995         return cfg;
32996     },
32997     
32998     getChildContainer: function( )
32999     {
33000         if (this.boxesEl) {
33001             return this.boxesEl;
33002         }
33003         
33004         this.boxesEl = this.el.select('.mas-boxes').first();
33005         
33006         return this.boxesEl;
33007     },
33008     
33009     
33010     initEvents : function()
33011     {
33012         var _this = this;
33013         
33014         if(this.isAutoInitial){
33015             Roo.log('hook children rendered');
33016             this.on('childrenrendered', function() {
33017                 Roo.log('children rendered');
33018                 _this.initial();
33019             } ,this);
33020         }
33021         
33022     },
33023     
33024     initial : function()
33025     {
33026         this.reloadItems();
33027
33028         this.currentSize = this.el.getBox(true);
33029
33030         /// was window resize... - let's see if this works..
33031         Roo.EventManager.onWindowResize(this.resize, this); 
33032
33033         if(!this.isAutoInitial){
33034             this.layout();
33035             return;
33036         }
33037         
33038         this.layout.defer(500,this);
33039     },
33040     
33041     reloadItems: function()
33042     {
33043         this.bricks = this.el.select('.masonry-brick', true);
33044         
33045         this.bricks.each(function(b) {
33046             //Roo.log(b.getSize());
33047             if (!b.attr('originalwidth')) {
33048                 b.attr('originalwidth',  b.getSize().width);
33049             }
33050             
33051         });
33052         
33053         Roo.log(this.bricks.elements.length);
33054     },
33055     
33056     resize : function()
33057     {
33058         Roo.log('resize');
33059         var cs = this.el.getBox(true);
33060         
33061         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33062             Roo.log("no change in with or X");
33063             return;
33064         }
33065         this.currentSize = cs;
33066         this.layout();
33067     },
33068     
33069     layout : function()
33070     {
33071          Roo.log('layout');
33072         this._resetLayout();
33073         //this._manageStamps();
33074       
33075         // don't animate first layout
33076         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33077         this.layoutItems( isInstant );
33078       
33079         // flag for initalized
33080         this._isLayoutInited = true;
33081     },
33082     
33083     layoutItems : function( isInstant )
33084     {
33085         //var items = this._getItemsForLayout( this.items );
33086         // original code supports filtering layout items.. we just ignore it..
33087         
33088         this._layoutItems( this.bricks , isInstant );
33089       
33090         this._postLayout();
33091     },
33092     _layoutItems : function ( items , isInstant)
33093     {
33094        //this.fireEvent( 'layout', this, items );
33095     
33096
33097         if ( !items || !items.elements.length ) {
33098           // no items, emit event with empty array
33099             return;
33100         }
33101
33102         var queue = [];
33103         items.each(function(item) {
33104             Roo.log("layout item");
33105             Roo.log(item);
33106             // get x/y object from method
33107             var position = this._getItemLayoutPosition( item );
33108             // enqueue
33109             position.item = item;
33110             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33111             queue.push( position );
33112         }, this);
33113       
33114         this._processLayoutQueue( queue );
33115     },
33116     /** Sets position of item in DOM
33117     * @param {Element} item
33118     * @param {Number} x - horizontal position
33119     * @param {Number} y - vertical position
33120     * @param {Boolean} isInstant - disables transitions
33121     */
33122     _processLayoutQueue : function( queue )
33123     {
33124         for ( var i=0, len = queue.length; i < len; i++ ) {
33125             var obj = queue[i];
33126             obj.item.position('absolute');
33127             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33128         }
33129     },
33130       
33131     
33132     /**
33133     * Any logic you want to do after each layout,
33134     * i.e. size the container
33135     */
33136     _postLayout : function()
33137     {
33138         this.resizeContainer();
33139     },
33140     
33141     resizeContainer : function()
33142     {
33143         if ( !this.isResizingContainer ) {
33144             return;
33145         }
33146         var size = this._getContainerSize();
33147         if ( size ) {
33148             this.el.setSize(size.width,size.height);
33149             this.boxesEl.setSize(size.width,size.height);
33150         }
33151     },
33152     
33153     
33154     
33155     _resetLayout : function()
33156     {
33157         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33158         this.colWidth = this.el.getWidth();
33159         //this.gutter = this.el.getWidth(); 
33160         
33161         this.measureColumns();
33162
33163         // reset column Y
33164         var i = this.cols;
33165         this.colYs = [];
33166         while (i--) {
33167             this.colYs.push( 0 );
33168         }
33169     
33170         this.maxY = 0;
33171     },
33172
33173     measureColumns : function()
33174     {
33175         this.getContainerWidth();
33176       // if columnWidth is 0, default to outerWidth of first item
33177         if ( !this.columnWidth ) {
33178             var firstItem = this.bricks.first();
33179             Roo.log(firstItem);
33180             this.columnWidth  = this.containerWidth;
33181             if (firstItem && firstItem.attr('originalwidth') ) {
33182                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33183             }
33184             // columnWidth fall back to item of first element
33185             Roo.log("set column width?");
33186                         this.initialColumnWidth = this.columnWidth  ;
33187
33188             // if first elem has no width, default to size of container
33189             
33190         }
33191         
33192         
33193         if (this.initialColumnWidth) {
33194             this.columnWidth = this.initialColumnWidth;
33195         }
33196         
33197         
33198             
33199         // column width is fixed at the top - however if container width get's smaller we should
33200         // reduce it...
33201         
33202         // this bit calcs how man columns..
33203             
33204         var columnWidth = this.columnWidth += this.gutter;
33205       
33206         // calculate columns
33207         var containerWidth = this.containerWidth + this.gutter;
33208         
33209         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33210         // fix rounding errors, typically with gutters
33211         var excess = columnWidth - containerWidth % columnWidth;
33212         
33213         
33214         // if overshoot is less than a pixel, round up, otherwise floor it
33215         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33216         cols = Math[ mathMethod ]( cols );
33217         this.cols = Math.max( cols, 1 );
33218         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33219         
33220          // padding positioning..
33221         var totalColWidth = this.cols * this.columnWidth;
33222         var padavail = this.containerWidth - totalColWidth;
33223         // so for 2 columns - we need 3 'pads'
33224         
33225         var padNeeded = (1+this.cols) * this.padWidth;
33226         
33227         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33228         
33229         this.columnWidth += padExtra
33230         //this.padWidth = Math.floor(padavail /  ( this.cols));
33231         
33232         // adjust colum width so that padding is fixed??
33233         
33234         // we have 3 columns ... total = width * 3
33235         // we have X left over... that should be used by 
33236         
33237         //if (this.expandC) {
33238             
33239         //}
33240         
33241         
33242         
33243     },
33244     
33245     getContainerWidth : function()
33246     {
33247        /* // container is parent if fit width
33248         var container = this.isFitWidth ? this.element.parentNode : this.element;
33249         // check that this.size and size are there
33250         // IE8 triggers resize on body size change, so they might not be
33251         
33252         var size = getSize( container );  //FIXME
33253         this.containerWidth = size && size.innerWidth; //FIXME
33254         */
33255          
33256         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33257         
33258     },
33259     
33260     _getItemLayoutPosition : function( item )  // what is item?
33261     {
33262         // we resize the item to our columnWidth..
33263       
33264         item.setWidth(this.columnWidth);
33265         item.autoBoxAdjust  = false;
33266         
33267         var sz = item.getSize();
33268  
33269         // how many columns does this brick span
33270         var remainder = this.containerWidth % this.columnWidth;
33271         
33272         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33273         // round if off by 1 pixel, otherwise use ceil
33274         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33275         colSpan = Math.min( colSpan, this.cols );
33276         
33277         // normally this should be '1' as we dont' currently allow multi width columns..
33278         
33279         var colGroup = this._getColGroup( colSpan );
33280         // get the minimum Y value from the columns
33281         var minimumY = Math.min.apply( Math, colGroup );
33282         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33283         
33284         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33285          
33286         // position the brick
33287         var position = {
33288             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33289             y: this.currentSize.y + minimumY + this.padHeight
33290         };
33291         
33292         Roo.log(position);
33293         // apply setHeight to necessary columns
33294         var setHeight = minimumY + sz.height + this.padHeight;
33295         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33296         
33297         var setSpan = this.cols + 1 - colGroup.length;
33298         for ( var i = 0; i < setSpan; i++ ) {
33299           this.colYs[ shortColIndex + i ] = setHeight ;
33300         }
33301       
33302         return position;
33303     },
33304     
33305     /**
33306      * @param {Number} colSpan - number of columns the element spans
33307      * @returns {Array} colGroup
33308      */
33309     _getColGroup : function( colSpan )
33310     {
33311         if ( colSpan < 2 ) {
33312           // if brick spans only one column, use all the column Ys
33313           return this.colYs;
33314         }
33315       
33316         var colGroup = [];
33317         // how many different places could this brick fit horizontally
33318         var groupCount = this.cols + 1 - colSpan;
33319         // for each group potential horizontal position
33320         for ( var i = 0; i < groupCount; i++ ) {
33321           // make an array of colY values for that one group
33322           var groupColYs = this.colYs.slice( i, i + colSpan );
33323           // and get the max value of the array
33324           colGroup[i] = Math.max.apply( Math, groupColYs );
33325         }
33326         return colGroup;
33327     },
33328     /*
33329     _manageStamp : function( stamp )
33330     {
33331         var stampSize =  stamp.getSize();
33332         var offset = stamp.getBox();
33333         // get the columns that this stamp affects
33334         var firstX = this.isOriginLeft ? offset.x : offset.right;
33335         var lastX = firstX + stampSize.width;
33336         var firstCol = Math.floor( firstX / this.columnWidth );
33337         firstCol = Math.max( 0, firstCol );
33338         
33339         var lastCol = Math.floor( lastX / this.columnWidth );
33340         // lastCol should not go over if multiple of columnWidth #425
33341         lastCol -= lastX % this.columnWidth ? 0 : 1;
33342         lastCol = Math.min( this.cols - 1, lastCol );
33343         
33344         // set colYs to bottom of the stamp
33345         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33346             stampSize.height;
33347             
33348         for ( var i = firstCol; i <= lastCol; i++ ) {
33349           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33350         }
33351     },
33352     */
33353     
33354     _getContainerSize : function()
33355     {
33356         this.maxY = Math.max.apply( Math, this.colYs );
33357         var size = {
33358             height: this.maxY
33359         };
33360       
33361         if ( this.isFitWidth ) {
33362             size.width = this._getContainerFitWidth();
33363         }
33364       
33365         return size;
33366     },
33367     
33368     _getContainerFitWidth : function()
33369     {
33370         var unusedCols = 0;
33371         // count unused columns
33372         var i = this.cols;
33373         while ( --i ) {
33374           if ( this.colYs[i] !== 0 ) {
33375             break;
33376           }
33377           unusedCols++;
33378         }
33379         // fit container to columns that have been used
33380         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33381     },
33382     
33383     needsResizeLayout : function()
33384     {
33385         var previousWidth = this.containerWidth;
33386         this.getContainerWidth();
33387         return previousWidth !== this.containerWidth;
33388     }
33389  
33390 });
33391
33392  
33393
33394  /*
33395  * - LGPL
33396  *
33397  * element
33398  * 
33399  */
33400
33401 /**
33402  * @class Roo.bootstrap.MasonryBrick
33403  * @extends Roo.bootstrap.Component
33404  * Bootstrap MasonryBrick class
33405  * 
33406  * @constructor
33407  * Create a new MasonryBrick
33408  * @param {Object} config The config object
33409  */
33410
33411 Roo.bootstrap.MasonryBrick = function(config){
33412     
33413     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33414     
33415     Roo.bootstrap.MasonryBrick.register(this);
33416     
33417     this.addEvents({
33418         // raw events
33419         /**
33420          * @event click
33421          * When a MasonryBrick is clcik
33422          * @param {Roo.bootstrap.MasonryBrick} this
33423          * @param {Roo.EventObject} e
33424          */
33425         "click" : true
33426     });
33427 };
33428
33429 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33430     
33431     /**
33432      * @cfg {String} title
33433      */   
33434     title : '',
33435     /**
33436      * @cfg {String} html
33437      */   
33438     html : '',
33439     /**
33440      * @cfg {String} bgimage
33441      */   
33442     bgimage : '',
33443     /**
33444      * @cfg {String} videourl
33445      */   
33446     videourl : '',
33447     /**
33448      * @cfg {String} cls
33449      */   
33450     cls : '',
33451     /**
33452      * @cfg {String} href
33453      */   
33454     href : '',
33455     /**
33456      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33457      */   
33458     size : 'xs',
33459     
33460     /**
33461      * @cfg {String} placetitle (center|bottom)
33462      */   
33463     placetitle : '',
33464     
33465     /**
33466      * @cfg {Boolean} isFitContainer defalut true
33467      */   
33468     isFitContainer : true, 
33469     
33470     /**
33471      * @cfg {Boolean} preventDefault defalut false
33472      */   
33473     preventDefault : false, 
33474     
33475     /**
33476      * @cfg {Boolean} inverse defalut false
33477      */   
33478     maskInverse : false, 
33479     
33480     getAutoCreate : function()
33481     {
33482         if(!this.isFitContainer){
33483             return this.getSplitAutoCreate();
33484         }
33485         
33486         var cls = 'masonry-brick masonry-brick-full';
33487         
33488         if(this.href.length){
33489             cls += ' masonry-brick-link';
33490         }
33491         
33492         if(this.bgimage.length){
33493             cls += ' masonry-brick-image';
33494         }
33495         
33496         if(this.maskInverse){
33497             cls += ' mask-inverse';
33498         }
33499         
33500         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33501             cls += ' enable-mask';
33502         }
33503         
33504         if(this.size){
33505             cls += ' masonry-' + this.size + '-brick';
33506         }
33507         
33508         if(this.placetitle.length){
33509             
33510             switch (this.placetitle) {
33511                 case 'center' :
33512                     cls += ' masonry-center-title';
33513                     break;
33514                 case 'bottom' :
33515                     cls += ' masonry-bottom-title';
33516                     break;
33517                 default:
33518                     break;
33519             }
33520             
33521         } else {
33522             if(!this.html.length && !this.bgimage.length){
33523                 cls += ' masonry-center-title';
33524             }
33525
33526             if(!this.html.length && this.bgimage.length){
33527                 cls += ' masonry-bottom-title';
33528             }
33529         }
33530         
33531         if(this.cls){
33532             cls += ' ' + this.cls;
33533         }
33534         
33535         var cfg = {
33536             tag: (this.href.length) ? 'a' : 'div',
33537             cls: cls,
33538             cn: [
33539                 {
33540                     tag: 'div',
33541                     cls: 'masonry-brick-mask'
33542                 },
33543                 {
33544                     tag: 'div',
33545                     cls: 'masonry-brick-paragraph',
33546                     cn: []
33547                 }
33548             ]
33549         };
33550         
33551         if(this.href.length){
33552             cfg.href = this.href;
33553         }
33554         
33555         var cn = cfg.cn[1].cn;
33556         
33557         if(this.title.length){
33558             cn.push({
33559                 tag: 'h4',
33560                 cls: 'masonry-brick-title',
33561                 html: this.title
33562             });
33563         }
33564         
33565         if(this.html.length){
33566             cn.push({
33567                 tag: 'p',
33568                 cls: 'masonry-brick-text',
33569                 html: this.html
33570             });
33571         }
33572         
33573         if (!this.title.length && !this.html.length) {
33574             cfg.cn[1].cls += ' hide';
33575         }
33576         
33577         if(this.bgimage.length){
33578             cfg.cn.push({
33579                 tag: 'img',
33580                 cls: 'masonry-brick-image-view',
33581                 src: this.bgimage
33582             });
33583         }
33584         
33585         if(this.videourl.length){
33586             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33587             // youtube support only?
33588             cfg.cn.push({
33589                 tag: 'iframe',
33590                 cls: 'masonry-brick-image-view',
33591                 src: vurl,
33592                 frameborder : 0,
33593                 allowfullscreen : true
33594             });
33595         }
33596         
33597         return cfg;
33598         
33599     },
33600     
33601     getSplitAutoCreate : function()
33602     {
33603         var cls = 'masonry-brick masonry-brick-split';
33604         
33605         if(this.href.length){
33606             cls += ' masonry-brick-link';
33607         }
33608         
33609         if(this.bgimage.length){
33610             cls += ' masonry-brick-image';
33611         }
33612         
33613         if(this.size){
33614             cls += ' masonry-' + this.size + '-brick';
33615         }
33616         
33617         switch (this.placetitle) {
33618             case 'center' :
33619                 cls += ' masonry-center-title';
33620                 break;
33621             case 'bottom' :
33622                 cls += ' masonry-bottom-title';
33623                 break;
33624             default:
33625                 if(!this.bgimage.length){
33626                     cls += ' masonry-center-title';
33627                 }
33628
33629                 if(this.bgimage.length){
33630                     cls += ' masonry-bottom-title';
33631                 }
33632                 break;
33633         }
33634         
33635         if(this.cls){
33636             cls += ' ' + this.cls;
33637         }
33638         
33639         var cfg = {
33640             tag: (this.href.length) ? 'a' : 'div',
33641             cls: cls,
33642             cn: [
33643                 {
33644                     tag: 'div',
33645                     cls: 'masonry-brick-split-head',
33646                     cn: [
33647                         {
33648                             tag: 'div',
33649                             cls: 'masonry-brick-paragraph',
33650                             cn: []
33651                         }
33652                     ]
33653                 },
33654                 {
33655                     tag: 'div',
33656                     cls: 'masonry-brick-split-body',
33657                     cn: []
33658                 }
33659             ]
33660         };
33661         
33662         if(this.href.length){
33663             cfg.href = this.href;
33664         }
33665         
33666         if(this.title.length){
33667             cfg.cn[0].cn[0].cn.push({
33668                 tag: 'h4',
33669                 cls: 'masonry-brick-title',
33670                 html: this.title
33671             });
33672         }
33673         
33674         if(this.html.length){
33675             cfg.cn[1].cn.push({
33676                 tag: 'p',
33677                 cls: 'masonry-brick-text',
33678                 html: this.html
33679             });
33680         }
33681
33682         if(this.bgimage.length){
33683             cfg.cn[0].cn.push({
33684                 tag: 'img',
33685                 cls: 'masonry-brick-image-view',
33686                 src: this.bgimage
33687             });
33688         }
33689         
33690         if(this.videourl.length){
33691             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33692             // youtube support only?
33693             cfg.cn[0].cn.cn.push({
33694                 tag: 'iframe',
33695                 cls: 'masonry-brick-image-view',
33696                 src: vurl,
33697                 frameborder : 0,
33698                 allowfullscreen : true
33699             });
33700         }
33701         
33702         return cfg;
33703     },
33704     
33705     initEvents: function() 
33706     {
33707         switch (this.size) {
33708             case 'xs' :
33709                 this.x = 1;
33710                 this.y = 1;
33711                 break;
33712             case 'sm' :
33713                 this.x = 2;
33714                 this.y = 2;
33715                 break;
33716             case 'md' :
33717             case 'md-left' :
33718             case 'md-right' :
33719                 this.x = 3;
33720                 this.y = 3;
33721                 break;
33722             case 'tall' :
33723                 this.x = 2;
33724                 this.y = 3;
33725                 break;
33726             case 'wide' :
33727                 this.x = 3;
33728                 this.y = 2;
33729                 break;
33730             case 'wide-thin' :
33731                 this.x = 3;
33732                 this.y = 1;
33733                 break;
33734                         
33735             default :
33736                 break;
33737         }
33738         
33739         if(Roo.isTouch){
33740             this.el.on('touchstart', this.onTouchStart, this);
33741             this.el.on('touchmove', this.onTouchMove, this);
33742             this.el.on('touchend', this.onTouchEnd, this);
33743             this.el.on('contextmenu', this.onContextMenu, this);
33744         } else {
33745             this.el.on('mouseenter'  ,this.enter, this);
33746             this.el.on('mouseleave', this.leave, this);
33747             this.el.on('click', this.onClick, this);
33748         }
33749         
33750         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33751             this.parent().bricks.push(this);   
33752         }
33753         
33754     },
33755     
33756     onClick: function(e, el)
33757     {
33758         var time = this.endTimer - this.startTimer;
33759         // Roo.log(e.preventDefault());
33760         if(Roo.isTouch){
33761             if(time > 1000){
33762                 e.preventDefault();
33763                 return;
33764             }
33765         }
33766         
33767         if(!this.preventDefault){
33768             return;
33769         }
33770         
33771         e.preventDefault();
33772         
33773         if (this.activeClass != '') {
33774             this.selectBrick();
33775         }
33776         
33777         this.fireEvent('click', this, e);
33778     },
33779     
33780     enter: function(e, el)
33781     {
33782         e.preventDefault();
33783         
33784         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33785             return;
33786         }
33787         
33788         if(this.bgimage.length && this.html.length){
33789             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33790         }
33791     },
33792     
33793     leave: function(e, el)
33794     {
33795         e.preventDefault();
33796         
33797         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33798             return;
33799         }
33800         
33801         if(this.bgimage.length && this.html.length){
33802             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33803         }
33804     },
33805     
33806     onTouchStart: function(e, el)
33807     {
33808 //        e.preventDefault();
33809         
33810         this.touchmoved = false;
33811         
33812         if(!this.isFitContainer){
33813             return;
33814         }
33815         
33816         if(!this.bgimage.length || !this.html.length){
33817             return;
33818         }
33819         
33820         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33821         
33822         this.timer = new Date().getTime();
33823         
33824     },
33825     
33826     onTouchMove: function(e, el)
33827     {
33828         this.touchmoved = true;
33829     },
33830     
33831     onContextMenu : function(e,el)
33832     {
33833         e.preventDefault();
33834         e.stopPropagation();
33835         return false;
33836     },
33837     
33838     onTouchEnd: function(e, el)
33839     {
33840 //        e.preventDefault();
33841         
33842         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33843         
33844             this.leave(e,el);
33845             
33846             return;
33847         }
33848         
33849         if(!this.bgimage.length || !this.html.length){
33850             
33851             if(this.href.length){
33852                 window.location.href = this.href;
33853             }
33854             
33855             return;
33856         }
33857         
33858         if(!this.isFitContainer){
33859             return;
33860         }
33861         
33862         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33863         
33864         window.location.href = this.href;
33865     },
33866     
33867     //selection on single brick only
33868     selectBrick : function() {
33869         
33870         if (!this.parentId) {
33871             return;
33872         }
33873         
33874         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33875         var index = m.selectedBrick.indexOf(this.id);
33876         
33877         if ( index > -1) {
33878             m.selectedBrick.splice(index,1);
33879             this.el.removeClass(this.activeClass);
33880             return;
33881         }
33882         
33883         for(var i = 0; i < m.selectedBrick.length; i++) {
33884             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33885             b.el.removeClass(b.activeClass);
33886         }
33887         
33888         m.selectedBrick = [];
33889         
33890         m.selectedBrick.push(this.id);
33891         this.el.addClass(this.activeClass);
33892         return;
33893     },
33894     
33895     isSelected : function(){
33896         return this.el.hasClass(this.activeClass);
33897         
33898     }
33899 });
33900
33901 Roo.apply(Roo.bootstrap.MasonryBrick, {
33902     
33903     //groups: {},
33904     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33905      /**
33906     * register a Masonry Brick
33907     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33908     */
33909     
33910     register : function(brick)
33911     {
33912         //this.groups[brick.id] = brick;
33913         this.groups.add(brick.id, brick);
33914     },
33915     /**
33916     * fetch a  masonry brick based on the masonry brick ID
33917     * @param {string} the masonry brick to add
33918     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33919     */
33920     
33921     get: function(brick_id) 
33922     {
33923         // if (typeof(this.groups[brick_id]) == 'undefined') {
33924         //     return false;
33925         // }
33926         // return this.groups[brick_id] ;
33927         
33928         if(this.groups.key(brick_id)) {
33929             return this.groups.key(brick_id);
33930         }
33931         
33932         return false;
33933     }
33934     
33935     
33936     
33937 });
33938
33939  /*
33940  * - LGPL
33941  *
33942  * element
33943  * 
33944  */
33945
33946 /**
33947  * @class Roo.bootstrap.Brick
33948  * @extends Roo.bootstrap.Component
33949  * Bootstrap Brick class
33950  * 
33951  * @constructor
33952  * Create a new Brick
33953  * @param {Object} config The config object
33954  */
33955
33956 Roo.bootstrap.Brick = function(config){
33957     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33958     
33959     this.addEvents({
33960         // raw events
33961         /**
33962          * @event click
33963          * When a Brick is click
33964          * @param {Roo.bootstrap.Brick} this
33965          * @param {Roo.EventObject} e
33966          */
33967         "click" : true
33968     });
33969 };
33970
33971 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33972     
33973     /**
33974      * @cfg {String} title
33975      */   
33976     title : '',
33977     /**
33978      * @cfg {String} html
33979      */   
33980     html : '',
33981     /**
33982      * @cfg {String} bgimage
33983      */   
33984     bgimage : '',
33985     /**
33986      * @cfg {String} cls
33987      */   
33988     cls : '',
33989     /**
33990      * @cfg {String} href
33991      */   
33992     href : '',
33993     /**
33994      * @cfg {String} video
33995      */   
33996     video : '',
33997     /**
33998      * @cfg {Boolean} square
33999      */   
34000     square : true,
34001     
34002     getAutoCreate : function()
34003     {
34004         var cls = 'roo-brick';
34005         
34006         if(this.href.length){
34007             cls += ' roo-brick-link';
34008         }
34009         
34010         if(this.bgimage.length){
34011             cls += ' roo-brick-image';
34012         }
34013         
34014         if(!this.html.length && !this.bgimage.length){
34015             cls += ' roo-brick-center-title';
34016         }
34017         
34018         if(!this.html.length && this.bgimage.length){
34019             cls += ' roo-brick-bottom-title';
34020         }
34021         
34022         if(this.cls){
34023             cls += ' ' + this.cls;
34024         }
34025         
34026         var cfg = {
34027             tag: (this.href.length) ? 'a' : 'div',
34028             cls: cls,
34029             cn: [
34030                 {
34031                     tag: 'div',
34032                     cls: 'roo-brick-paragraph',
34033                     cn: []
34034                 }
34035             ]
34036         };
34037         
34038         if(this.href.length){
34039             cfg.href = this.href;
34040         }
34041         
34042         var cn = cfg.cn[0].cn;
34043         
34044         if(this.title.length){
34045             cn.push({
34046                 tag: 'h4',
34047                 cls: 'roo-brick-title',
34048                 html: this.title
34049             });
34050         }
34051         
34052         if(this.html.length){
34053             cn.push({
34054                 tag: 'p',
34055                 cls: 'roo-brick-text',
34056                 html: this.html
34057             });
34058         } else {
34059             cn.cls += ' hide';
34060         }
34061         
34062         if(this.bgimage.length){
34063             cfg.cn.push({
34064                 tag: 'img',
34065                 cls: 'roo-brick-image-view',
34066                 src: this.bgimage
34067             });
34068         }
34069         
34070         return cfg;
34071     },
34072     
34073     initEvents: function() 
34074     {
34075         if(this.title.length || this.html.length){
34076             this.el.on('mouseenter'  ,this.enter, this);
34077             this.el.on('mouseleave', this.leave, this);
34078         }
34079         
34080         Roo.EventManager.onWindowResize(this.resize, this); 
34081         
34082         if(this.bgimage.length){
34083             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34084             this.imageEl.on('load', this.onImageLoad, this);
34085             return;
34086         }
34087         
34088         this.resize();
34089     },
34090     
34091     onImageLoad : function()
34092     {
34093         this.resize();
34094     },
34095     
34096     resize : function()
34097     {
34098         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34099         
34100         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34101         
34102         if(this.bgimage.length){
34103             var image = this.el.select('.roo-brick-image-view', true).first();
34104             
34105             image.setWidth(paragraph.getWidth());
34106             
34107             if(this.square){
34108                 image.setHeight(paragraph.getWidth());
34109             }
34110             
34111             this.el.setHeight(image.getHeight());
34112             paragraph.setHeight(image.getHeight());
34113             
34114         }
34115         
34116     },
34117     
34118     enter: function(e, el)
34119     {
34120         e.preventDefault();
34121         
34122         if(this.bgimage.length){
34123             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34124             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34125         }
34126     },
34127     
34128     leave: function(e, el)
34129     {
34130         e.preventDefault();
34131         
34132         if(this.bgimage.length){
34133             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34134             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34135         }
34136     }
34137     
34138 });
34139
34140  
34141
34142  /*
34143  * - LGPL
34144  *
34145  * Number field 
34146  */
34147
34148 /**
34149  * @class Roo.bootstrap.NumberField
34150  * @extends Roo.bootstrap.Input
34151  * Bootstrap NumberField class
34152  * 
34153  * 
34154  * 
34155  * 
34156  * @constructor
34157  * Create a new NumberField
34158  * @param {Object} config The config object
34159  */
34160
34161 Roo.bootstrap.NumberField = function(config){
34162     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34163 };
34164
34165 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34166     
34167     /**
34168      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34169      */
34170     allowDecimals : true,
34171     /**
34172      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34173      */
34174     decimalSeparator : ".",
34175     /**
34176      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34177      */
34178     decimalPrecision : 2,
34179     /**
34180      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34181      */
34182     allowNegative : true,
34183     
34184     /**
34185      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34186      */
34187     allowZero: true,
34188     /**
34189      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34190      */
34191     minValue : Number.NEGATIVE_INFINITY,
34192     /**
34193      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34194      */
34195     maxValue : Number.MAX_VALUE,
34196     /**
34197      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34198      */
34199     minText : "The minimum value for this field is {0}",
34200     /**
34201      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34202      */
34203     maxText : "The maximum value for this field is {0}",
34204     /**
34205      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34206      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34207      */
34208     nanText : "{0} is not a valid number",
34209     /**
34210      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34211      */
34212     thousandsDelimiter : false,
34213     /**
34214      * @cfg {String} valueAlign alignment of value
34215      */
34216     valueAlign : "left",
34217
34218     getAutoCreate : function()
34219     {
34220         var hiddenInput = {
34221             tag: 'input',
34222             type: 'hidden',
34223             id: Roo.id(),
34224             cls: 'hidden-number-input'
34225         };
34226         
34227         if (this.name) {
34228             hiddenInput.name = this.name;
34229         }
34230         
34231         this.name = '';
34232         
34233         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34234         
34235         this.name = hiddenInput.name;
34236         
34237         if(cfg.cn.length > 0) {
34238             cfg.cn.push(hiddenInput);
34239         }
34240         
34241         return cfg;
34242     },
34243
34244     // private
34245     initEvents : function()
34246     {   
34247         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34248         
34249         var allowed = "0123456789";
34250         
34251         if(this.allowDecimals){
34252             allowed += this.decimalSeparator;
34253         }
34254         
34255         if(this.allowNegative){
34256             allowed += "-";
34257         }
34258         
34259         if(this.thousandsDelimiter) {
34260             allowed += ",";
34261         }
34262         
34263         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34264         
34265         var keyPress = function(e){
34266             
34267             var k = e.getKey();
34268             
34269             var c = e.getCharCode();
34270             
34271             if(
34272                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34273                     allowed.indexOf(String.fromCharCode(c)) === -1
34274             ){
34275                 e.stopEvent();
34276                 return;
34277             }
34278             
34279             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34280                 return;
34281             }
34282             
34283             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34284                 e.stopEvent();
34285             }
34286         };
34287         
34288         this.el.on("keypress", keyPress, this);
34289     },
34290     
34291     validateValue : function(value)
34292     {
34293         
34294         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34295             return false;
34296         }
34297         
34298         var num = this.parseValue(value);
34299         
34300         if(isNaN(num)){
34301             this.markInvalid(String.format(this.nanText, value));
34302             return false;
34303         }
34304         
34305         if(num < this.minValue){
34306             this.markInvalid(String.format(this.minText, this.minValue));
34307             return false;
34308         }
34309         
34310         if(num > this.maxValue){
34311             this.markInvalid(String.format(this.maxText, this.maxValue));
34312             return false;
34313         }
34314         
34315         return true;
34316     },
34317
34318     getValue : function()
34319     {
34320         var v = this.hiddenEl().getValue();
34321         
34322         return this.fixPrecision(this.parseValue(v));
34323     },
34324
34325     parseValue : function(value)
34326     {
34327         if(this.thousandsDelimiter) {
34328             value += "";
34329             r = new RegExp(",", "g");
34330             value = value.replace(r, "");
34331         }
34332         
34333         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34334         return isNaN(value) ? '' : value;
34335     },
34336
34337     fixPrecision : function(value)
34338     {
34339         if(this.thousandsDelimiter) {
34340             value += "";
34341             r = new RegExp(",", "g");
34342             value = value.replace(r, "");
34343         }
34344         
34345         var nan = isNaN(value);
34346         
34347         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34348             return nan ? '' : value;
34349         }
34350         return parseFloat(value).toFixed(this.decimalPrecision);
34351     },
34352
34353     setValue : function(v)
34354     {
34355         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34356         
34357         this.value = v;
34358         
34359         if(this.rendered){
34360             
34361             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34362             
34363             this.inputEl().dom.value = (v == '') ? '' :
34364                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34365             
34366             if(!this.allowZero && v === '0') {
34367                 this.hiddenEl().dom.value = '';
34368                 this.inputEl().dom.value = '';
34369             }
34370             
34371             this.validate();
34372         }
34373     },
34374
34375     decimalPrecisionFcn : function(v)
34376     {
34377         return Math.floor(v);
34378     },
34379
34380     beforeBlur : function()
34381     {
34382         var v = this.parseValue(this.getRawValue());
34383         
34384         if(v || v === 0 || v === ''){
34385             this.setValue(v);
34386         }
34387     },
34388     
34389     hiddenEl : function()
34390     {
34391         return this.el.select('input.hidden-number-input',true).first();
34392     }
34393     
34394 });
34395
34396  
34397
34398 /*
34399 * Licence: LGPL
34400 */
34401
34402 /**
34403  * @class Roo.bootstrap.DocumentSlider
34404  * @extends Roo.bootstrap.Component
34405  * Bootstrap DocumentSlider class
34406  * 
34407  * @constructor
34408  * Create a new DocumentViewer
34409  * @param {Object} config The config object
34410  */
34411
34412 Roo.bootstrap.DocumentSlider = function(config){
34413     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34414     
34415     this.files = [];
34416     
34417     this.addEvents({
34418         /**
34419          * @event initial
34420          * Fire after initEvent
34421          * @param {Roo.bootstrap.DocumentSlider} this
34422          */
34423         "initial" : true,
34424         /**
34425          * @event update
34426          * Fire after update
34427          * @param {Roo.bootstrap.DocumentSlider} this
34428          */
34429         "update" : true,
34430         /**
34431          * @event click
34432          * Fire after click
34433          * @param {Roo.bootstrap.DocumentSlider} this
34434          */
34435         "click" : true
34436     });
34437 };
34438
34439 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34440     
34441     files : false,
34442     
34443     indicator : 0,
34444     
34445     getAutoCreate : function()
34446     {
34447         var cfg = {
34448             tag : 'div',
34449             cls : 'roo-document-slider',
34450             cn : [
34451                 {
34452                     tag : 'div',
34453                     cls : 'roo-document-slider-header',
34454                     cn : [
34455                         {
34456                             tag : 'div',
34457                             cls : 'roo-document-slider-header-title'
34458                         }
34459                     ]
34460                 },
34461                 {
34462                     tag : 'div',
34463                     cls : 'roo-document-slider-body',
34464                     cn : [
34465                         {
34466                             tag : 'div',
34467                             cls : 'roo-document-slider-prev',
34468                             cn : [
34469                                 {
34470                                     tag : 'i',
34471                                     cls : 'fa fa-chevron-left'
34472                                 }
34473                             ]
34474                         },
34475                         {
34476                             tag : 'div',
34477                             cls : 'roo-document-slider-thumb',
34478                             cn : [
34479                                 {
34480                                     tag : 'img',
34481                                     cls : 'roo-document-slider-image'
34482                                 }
34483                             ]
34484                         },
34485                         {
34486                             tag : 'div',
34487                             cls : 'roo-document-slider-next',
34488                             cn : [
34489                                 {
34490                                     tag : 'i',
34491                                     cls : 'fa fa-chevron-right'
34492                                 }
34493                             ]
34494                         }
34495                     ]
34496                 }
34497             ]
34498         };
34499         
34500         return cfg;
34501     },
34502     
34503     initEvents : function()
34504     {
34505         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34506         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34507         
34508         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34509         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34510         
34511         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34512         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34513         
34514         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34515         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34516         
34517         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34518         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34519         
34520         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34521         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34522         
34523         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34524         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34525         
34526         this.thumbEl.on('click', this.onClick, this);
34527         
34528         this.prevIndicator.on('click', this.prev, this);
34529         
34530         this.nextIndicator.on('click', this.next, this);
34531         
34532     },
34533     
34534     initial : function()
34535     {
34536         if(this.files.length){
34537             this.indicator = 1;
34538             this.update()
34539         }
34540         
34541         this.fireEvent('initial', this);
34542     },
34543     
34544     update : function()
34545     {
34546         this.imageEl.attr('src', this.files[this.indicator - 1]);
34547         
34548         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34549         
34550         this.prevIndicator.show();
34551         
34552         if(this.indicator == 1){
34553             this.prevIndicator.hide();
34554         }
34555         
34556         this.nextIndicator.show();
34557         
34558         if(this.indicator == this.files.length){
34559             this.nextIndicator.hide();
34560         }
34561         
34562         this.thumbEl.scrollTo('top');
34563         
34564         this.fireEvent('update', this);
34565     },
34566     
34567     onClick : function(e)
34568     {
34569         e.preventDefault();
34570         
34571         this.fireEvent('click', this);
34572     },
34573     
34574     prev : function(e)
34575     {
34576         e.preventDefault();
34577         
34578         this.indicator = Math.max(1, this.indicator - 1);
34579         
34580         this.update();
34581     },
34582     
34583     next : function(e)
34584     {
34585         e.preventDefault();
34586         
34587         this.indicator = Math.min(this.files.length, this.indicator + 1);
34588         
34589         this.update();
34590     }
34591 });
34592 /*
34593  * - LGPL
34594  *
34595  * RadioSet
34596  *
34597  *
34598  */
34599
34600 /**
34601  * @class Roo.bootstrap.RadioSet
34602  * @extends Roo.bootstrap.Input
34603  * Bootstrap RadioSet class
34604  * @cfg {String} indicatorpos (left|right) default left
34605  * @cfg {Boolean} inline (true|false) inline the element (default true)
34606  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34607  * @constructor
34608  * Create a new RadioSet
34609  * @param {Object} config The config object
34610  */
34611
34612 Roo.bootstrap.RadioSet = function(config){
34613     
34614     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34615     
34616     this.radioes = [];
34617     
34618     Roo.bootstrap.RadioSet.register(this);
34619     
34620     this.addEvents({
34621         /**
34622         * @event check
34623         * Fires when the element is checked or unchecked.
34624         * @param {Roo.bootstrap.RadioSet} this This radio
34625         * @param {Roo.bootstrap.Radio} item The checked item
34626         */
34627        check : true,
34628        /**
34629         * @event click
34630         * Fires when the element is click.
34631         * @param {Roo.bootstrap.RadioSet} this This radio set
34632         * @param {Roo.bootstrap.Radio} item The checked item
34633         * @param {Roo.EventObject} e The event object
34634         */
34635        click : true
34636     });
34637     
34638 };
34639
34640 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34641
34642     radioes : false,
34643     
34644     inline : true,
34645     
34646     weight : '',
34647     
34648     indicatorpos : 'left',
34649     
34650     getAutoCreate : function()
34651     {
34652         var label = {
34653             tag : 'label',
34654             cls : 'roo-radio-set-label',
34655             cn : [
34656                 {
34657                     tag : 'span',
34658                     html : this.fieldLabel
34659                 }
34660             ]
34661         };
34662         if (Roo.bootstrap.version == 3) {
34663             
34664             
34665             if(this.indicatorpos == 'left'){
34666                 label.cn.unshift({
34667                     tag : 'i',
34668                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34669                     tooltip : 'This field is required'
34670                 });
34671             } else {
34672                 label.cn.push({
34673                     tag : 'i',
34674                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34675                     tooltip : 'This field is required'
34676                 });
34677             }
34678         }
34679         var items = {
34680             tag : 'div',
34681             cls : 'roo-radio-set-items'
34682         };
34683         
34684         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34685         
34686         if (align === 'left' && this.fieldLabel.length) {
34687             
34688             items = {
34689                 cls : "roo-radio-set-right", 
34690                 cn: [
34691                     items
34692                 ]
34693             };
34694             
34695             if(this.labelWidth > 12){
34696                 label.style = "width: " + this.labelWidth + 'px';
34697             }
34698             
34699             if(this.labelWidth < 13 && this.labelmd == 0){
34700                 this.labelmd = this.labelWidth;
34701             }
34702             
34703             if(this.labellg > 0){
34704                 label.cls += ' col-lg-' + this.labellg;
34705                 items.cls += ' col-lg-' + (12 - this.labellg);
34706             }
34707             
34708             if(this.labelmd > 0){
34709                 label.cls += ' col-md-' + this.labelmd;
34710                 items.cls += ' col-md-' + (12 - this.labelmd);
34711             }
34712             
34713             if(this.labelsm > 0){
34714                 label.cls += ' col-sm-' + this.labelsm;
34715                 items.cls += ' col-sm-' + (12 - this.labelsm);
34716             }
34717             
34718             if(this.labelxs > 0){
34719                 label.cls += ' col-xs-' + this.labelxs;
34720                 items.cls += ' col-xs-' + (12 - this.labelxs);
34721             }
34722         }
34723         
34724         var cfg = {
34725             tag : 'div',
34726             cls : 'roo-radio-set',
34727             cn : [
34728                 {
34729                     tag : 'input',
34730                     cls : 'roo-radio-set-input',
34731                     type : 'hidden',
34732                     name : this.name,
34733                     value : this.value ? this.value :  ''
34734                 },
34735                 label,
34736                 items
34737             ]
34738         };
34739         
34740         if(this.weight.length){
34741             cfg.cls += ' roo-radio-' + this.weight;
34742         }
34743         
34744         if(this.inline) {
34745             cfg.cls += ' roo-radio-set-inline';
34746         }
34747         
34748         var settings=this;
34749         ['xs','sm','md','lg'].map(function(size){
34750             if (settings[size]) {
34751                 cfg.cls += ' col-' + size + '-' + settings[size];
34752             }
34753         });
34754         
34755         return cfg;
34756         
34757     },
34758
34759     initEvents : function()
34760     {
34761         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34762         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34763         
34764         if(!this.fieldLabel.length){
34765             this.labelEl.hide();
34766         }
34767         
34768         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34769         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34770         
34771         this.indicator = this.indicatorEl();
34772         
34773         if(this.indicator){
34774             this.indicator.addClass('invisible');
34775         }
34776         
34777         this.originalValue = this.getValue();
34778         
34779     },
34780     
34781     inputEl: function ()
34782     {
34783         return this.el.select('.roo-radio-set-input', true).first();
34784     },
34785     
34786     getChildContainer : function()
34787     {
34788         return this.itemsEl;
34789     },
34790     
34791     register : function(item)
34792     {
34793         this.radioes.push(item);
34794         
34795     },
34796     
34797     validate : function()
34798     {   
34799         if(this.getVisibilityEl().hasClass('hidden')){
34800             return true;
34801         }
34802         
34803         var valid = false;
34804         
34805         Roo.each(this.radioes, function(i){
34806             if(!i.checked){
34807                 return;
34808             }
34809             
34810             valid = true;
34811             return false;
34812         });
34813         
34814         if(this.allowBlank) {
34815             return true;
34816         }
34817         
34818         if(this.disabled || valid){
34819             this.markValid();
34820             return true;
34821         }
34822         
34823         this.markInvalid();
34824         return false;
34825         
34826     },
34827     
34828     markValid : function()
34829     {
34830         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34831             this.indicatorEl().removeClass('visible');
34832             this.indicatorEl().addClass('invisible');
34833         }
34834         
34835         
34836         if (Roo.bootstrap.version == 3) {
34837             this.el.removeClass([this.invalidClass, this.validClass]);
34838             this.el.addClass(this.validClass);
34839         } else {
34840             this.el.removeClass(['is-invalid','is-valid']);
34841             this.el.addClass(['is-valid']);
34842         }
34843         this.fireEvent('valid', this);
34844     },
34845     
34846     markInvalid : function(msg)
34847     {
34848         if(this.allowBlank || this.disabled){
34849             return;
34850         }
34851         
34852         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34853             this.indicatorEl().removeClass('invisible');
34854             this.indicatorEl().addClass('visible');
34855         }
34856         if (Roo.bootstrap.version == 3) {
34857             this.el.removeClass([this.invalidClass, this.validClass]);
34858             this.el.addClass(this.invalidClass);
34859         } else {
34860             this.el.removeClass(['is-invalid','is-valid']);
34861             this.el.addClass(['is-invalid']);
34862         }
34863         
34864         this.fireEvent('invalid', this, msg);
34865         
34866     },
34867     
34868     setValue : function(v, suppressEvent)
34869     {   
34870         if(this.value === v){
34871             return;
34872         }
34873         
34874         this.value = v;
34875         
34876         if(this.rendered){
34877             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34878         }
34879         
34880         Roo.each(this.radioes, function(i){
34881             i.checked = false;
34882             i.el.removeClass('checked');
34883         });
34884         
34885         Roo.each(this.radioes, function(i){
34886             
34887             if(i.value === v || i.value.toString() === v.toString()){
34888                 i.checked = true;
34889                 i.el.addClass('checked');
34890                 
34891                 if(suppressEvent !== true){
34892                     this.fireEvent('check', this, i);
34893                 }
34894                 
34895                 return false;
34896             }
34897             
34898         }, this);
34899         
34900         this.validate();
34901     },
34902     
34903     clearInvalid : function(){
34904         
34905         if(!this.el || this.preventMark){
34906             return;
34907         }
34908         
34909         this.el.removeClass([this.invalidClass]);
34910         
34911         this.fireEvent('valid', this);
34912     }
34913     
34914 });
34915
34916 Roo.apply(Roo.bootstrap.RadioSet, {
34917     
34918     groups: {},
34919     
34920     register : function(set)
34921     {
34922         this.groups[set.name] = set;
34923     },
34924     
34925     get: function(name) 
34926     {
34927         if (typeof(this.groups[name]) == 'undefined') {
34928             return false;
34929         }
34930         
34931         return this.groups[name] ;
34932     }
34933     
34934 });
34935 /*
34936  * Based on:
34937  * Ext JS Library 1.1.1
34938  * Copyright(c) 2006-2007, Ext JS, LLC.
34939  *
34940  * Originally Released Under LGPL - original licence link has changed is not relivant.
34941  *
34942  * Fork - LGPL
34943  * <script type="text/javascript">
34944  */
34945
34946
34947 /**
34948  * @class Roo.bootstrap.SplitBar
34949  * @extends Roo.util.Observable
34950  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34951  * <br><br>
34952  * Usage:
34953  * <pre><code>
34954 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34955                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34956 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34957 split.minSize = 100;
34958 split.maxSize = 600;
34959 split.animate = true;
34960 split.on('moved', splitterMoved);
34961 </code></pre>
34962  * @constructor
34963  * Create a new SplitBar
34964  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34965  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34966  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34967  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34968                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34969                         position of the SplitBar).
34970  */
34971 Roo.bootstrap.SplitBar = function(cfg){
34972     
34973     /** @private */
34974     
34975     //{
34976     //  dragElement : elm
34977     //  resizingElement: el,
34978         // optional..
34979     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34980     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34981         // existingProxy ???
34982     //}
34983     
34984     this.el = Roo.get(cfg.dragElement, true);
34985     this.el.dom.unselectable = "on";
34986     /** @private */
34987     this.resizingEl = Roo.get(cfg.resizingElement, true);
34988
34989     /**
34990      * @private
34991      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34992      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34993      * @type Number
34994      */
34995     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34996     
34997     /**
34998      * The minimum size of the resizing element. (Defaults to 0)
34999      * @type Number
35000      */
35001     this.minSize = 0;
35002     
35003     /**
35004      * The maximum size of the resizing element. (Defaults to 2000)
35005      * @type Number
35006      */
35007     this.maxSize = 2000;
35008     
35009     /**
35010      * Whether to animate the transition to the new size
35011      * @type Boolean
35012      */
35013     this.animate = false;
35014     
35015     /**
35016      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35017      * @type Boolean
35018      */
35019     this.useShim = false;
35020     
35021     /** @private */
35022     this.shim = null;
35023     
35024     if(!cfg.existingProxy){
35025         /** @private */
35026         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35027     }else{
35028         this.proxy = Roo.get(cfg.existingProxy).dom;
35029     }
35030     /** @private */
35031     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35032     
35033     /** @private */
35034     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35035     
35036     /** @private */
35037     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35038     
35039     /** @private */
35040     this.dragSpecs = {};
35041     
35042     /**
35043      * @private The adapter to use to positon and resize elements
35044      */
35045     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35046     this.adapter.init(this);
35047     
35048     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35049         /** @private */
35050         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35051         this.el.addClass("roo-splitbar-h");
35052     }else{
35053         /** @private */
35054         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35055         this.el.addClass("roo-splitbar-v");
35056     }
35057     
35058     this.addEvents({
35059         /**
35060          * @event resize
35061          * Fires when the splitter is moved (alias for {@link #event-moved})
35062          * @param {Roo.bootstrap.SplitBar} this
35063          * @param {Number} newSize the new width or height
35064          */
35065         "resize" : true,
35066         /**
35067          * @event moved
35068          * Fires when the splitter is moved
35069          * @param {Roo.bootstrap.SplitBar} this
35070          * @param {Number} newSize the new width or height
35071          */
35072         "moved" : true,
35073         /**
35074          * @event beforeresize
35075          * Fires before the splitter is dragged
35076          * @param {Roo.bootstrap.SplitBar} this
35077          */
35078         "beforeresize" : true,
35079
35080         "beforeapply" : true
35081     });
35082
35083     Roo.util.Observable.call(this);
35084 };
35085
35086 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35087     onStartProxyDrag : function(x, y){
35088         this.fireEvent("beforeresize", this);
35089         if(!this.overlay){
35090             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35091             o.unselectable();
35092             o.enableDisplayMode("block");
35093             // all splitbars share the same overlay
35094             Roo.bootstrap.SplitBar.prototype.overlay = o;
35095         }
35096         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35097         this.overlay.show();
35098         Roo.get(this.proxy).setDisplayed("block");
35099         var size = this.adapter.getElementSize(this);
35100         this.activeMinSize = this.getMinimumSize();;
35101         this.activeMaxSize = this.getMaximumSize();;
35102         var c1 = size - this.activeMinSize;
35103         var c2 = Math.max(this.activeMaxSize - size, 0);
35104         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35105             this.dd.resetConstraints();
35106             this.dd.setXConstraint(
35107                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35108                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35109             );
35110             this.dd.setYConstraint(0, 0);
35111         }else{
35112             this.dd.resetConstraints();
35113             this.dd.setXConstraint(0, 0);
35114             this.dd.setYConstraint(
35115                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35116                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35117             );
35118          }
35119         this.dragSpecs.startSize = size;
35120         this.dragSpecs.startPoint = [x, y];
35121         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35122     },
35123     
35124     /** 
35125      * @private Called after the drag operation by the DDProxy
35126      */
35127     onEndProxyDrag : function(e){
35128         Roo.get(this.proxy).setDisplayed(false);
35129         var endPoint = Roo.lib.Event.getXY(e);
35130         if(this.overlay){
35131             this.overlay.hide();
35132         }
35133         var newSize;
35134         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35135             newSize = this.dragSpecs.startSize + 
35136                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35137                     endPoint[0] - this.dragSpecs.startPoint[0] :
35138                     this.dragSpecs.startPoint[0] - endPoint[0]
35139                 );
35140         }else{
35141             newSize = this.dragSpecs.startSize + 
35142                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35143                     endPoint[1] - this.dragSpecs.startPoint[1] :
35144                     this.dragSpecs.startPoint[1] - endPoint[1]
35145                 );
35146         }
35147         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35148         if(newSize != this.dragSpecs.startSize){
35149             if(this.fireEvent('beforeapply', this, newSize) !== false){
35150                 this.adapter.setElementSize(this, newSize);
35151                 this.fireEvent("moved", this, newSize);
35152                 this.fireEvent("resize", this, newSize);
35153             }
35154         }
35155     },
35156     
35157     /**
35158      * Get the adapter this SplitBar uses
35159      * @return The adapter object
35160      */
35161     getAdapter : function(){
35162         return this.adapter;
35163     },
35164     
35165     /**
35166      * Set the adapter this SplitBar uses
35167      * @param {Object} adapter A SplitBar adapter object
35168      */
35169     setAdapter : function(adapter){
35170         this.adapter = adapter;
35171         this.adapter.init(this);
35172     },
35173     
35174     /**
35175      * Gets the minimum size for the resizing element
35176      * @return {Number} The minimum size
35177      */
35178     getMinimumSize : function(){
35179         return this.minSize;
35180     },
35181     
35182     /**
35183      * Sets the minimum size for the resizing element
35184      * @param {Number} minSize The minimum size
35185      */
35186     setMinimumSize : function(minSize){
35187         this.minSize = minSize;
35188     },
35189     
35190     /**
35191      * Gets the maximum size for the resizing element
35192      * @return {Number} The maximum size
35193      */
35194     getMaximumSize : function(){
35195         return this.maxSize;
35196     },
35197     
35198     /**
35199      * Sets the maximum size for the resizing element
35200      * @param {Number} maxSize The maximum size
35201      */
35202     setMaximumSize : function(maxSize){
35203         this.maxSize = maxSize;
35204     },
35205     
35206     /**
35207      * Sets the initialize size for the resizing element
35208      * @param {Number} size The initial size
35209      */
35210     setCurrentSize : function(size){
35211         var oldAnimate = this.animate;
35212         this.animate = false;
35213         this.adapter.setElementSize(this, size);
35214         this.animate = oldAnimate;
35215     },
35216     
35217     /**
35218      * Destroy this splitbar. 
35219      * @param {Boolean} removeEl True to remove the element
35220      */
35221     destroy : function(removeEl){
35222         if(this.shim){
35223             this.shim.remove();
35224         }
35225         this.dd.unreg();
35226         this.proxy.parentNode.removeChild(this.proxy);
35227         if(removeEl){
35228             this.el.remove();
35229         }
35230     }
35231 });
35232
35233 /**
35234  * @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.
35235  */
35236 Roo.bootstrap.SplitBar.createProxy = function(dir){
35237     var proxy = new Roo.Element(document.createElement("div"));
35238     proxy.unselectable();
35239     var cls = 'roo-splitbar-proxy';
35240     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35241     document.body.appendChild(proxy.dom);
35242     return proxy.dom;
35243 };
35244
35245 /** 
35246  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35247  * Default Adapter. It assumes the splitter and resizing element are not positioned
35248  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35249  */
35250 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35251 };
35252
35253 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35254     // do nothing for now
35255     init : function(s){
35256     
35257     },
35258     /**
35259      * Called before drag operations to get the current size of the resizing element. 
35260      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35261      */
35262      getElementSize : function(s){
35263         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35264             return s.resizingEl.getWidth();
35265         }else{
35266             return s.resizingEl.getHeight();
35267         }
35268     },
35269     
35270     /**
35271      * Called after drag operations to set the size of the resizing element.
35272      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35273      * @param {Number} newSize The new size to set
35274      * @param {Function} onComplete A function to be invoked when resizing is complete
35275      */
35276     setElementSize : function(s, newSize, onComplete){
35277         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35278             if(!s.animate){
35279                 s.resizingEl.setWidth(newSize);
35280                 if(onComplete){
35281                     onComplete(s, newSize);
35282                 }
35283             }else{
35284                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35285             }
35286         }else{
35287             
35288             if(!s.animate){
35289                 s.resizingEl.setHeight(newSize);
35290                 if(onComplete){
35291                     onComplete(s, newSize);
35292                 }
35293             }else{
35294                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35295             }
35296         }
35297     }
35298 };
35299
35300 /** 
35301  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35302  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35303  * Adapter that  moves the splitter element to align with the resized sizing element. 
35304  * Used with an absolute positioned SplitBar.
35305  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35306  * document.body, make sure you assign an id to the body element.
35307  */
35308 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35309     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35310     this.container = Roo.get(container);
35311 };
35312
35313 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35314     init : function(s){
35315         this.basic.init(s);
35316     },
35317     
35318     getElementSize : function(s){
35319         return this.basic.getElementSize(s);
35320     },
35321     
35322     setElementSize : function(s, newSize, onComplete){
35323         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35324     },
35325     
35326     moveSplitter : function(s){
35327         var yes = Roo.bootstrap.SplitBar;
35328         switch(s.placement){
35329             case yes.LEFT:
35330                 s.el.setX(s.resizingEl.getRight());
35331                 break;
35332             case yes.RIGHT:
35333                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35334                 break;
35335             case yes.TOP:
35336                 s.el.setY(s.resizingEl.getBottom());
35337                 break;
35338             case yes.BOTTOM:
35339                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35340                 break;
35341         }
35342     }
35343 };
35344
35345 /**
35346  * Orientation constant - Create a vertical SplitBar
35347  * @static
35348  * @type Number
35349  */
35350 Roo.bootstrap.SplitBar.VERTICAL = 1;
35351
35352 /**
35353  * Orientation constant - Create a horizontal SplitBar
35354  * @static
35355  * @type Number
35356  */
35357 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35358
35359 /**
35360  * Placement constant - The resizing element is to the left of the splitter element
35361  * @static
35362  * @type Number
35363  */
35364 Roo.bootstrap.SplitBar.LEFT = 1;
35365
35366 /**
35367  * Placement constant - The resizing element is to the right of the splitter element
35368  * @static
35369  * @type Number
35370  */
35371 Roo.bootstrap.SplitBar.RIGHT = 2;
35372
35373 /**
35374  * Placement constant - The resizing element is positioned above the splitter element
35375  * @static
35376  * @type Number
35377  */
35378 Roo.bootstrap.SplitBar.TOP = 3;
35379
35380 /**
35381  * Placement constant - The resizing element is positioned under splitter element
35382  * @static
35383  * @type Number
35384  */
35385 Roo.bootstrap.SplitBar.BOTTOM = 4;
35386 Roo.namespace("Roo.bootstrap.layout");/*
35387  * Based on:
35388  * Ext JS Library 1.1.1
35389  * Copyright(c) 2006-2007, Ext JS, LLC.
35390  *
35391  * Originally Released Under LGPL - original licence link has changed is not relivant.
35392  *
35393  * Fork - LGPL
35394  * <script type="text/javascript">
35395  */
35396
35397 /**
35398  * @class Roo.bootstrap.layout.Manager
35399  * @extends Roo.bootstrap.Component
35400  * Base class for layout managers.
35401  */
35402 Roo.bootstrap.layout.Manager = function(config)
35403 {
35404     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35405
35406
35407
35408
35409
35410     /** false to disable window resize monitoring @type Boolean */
35411     this.monitorWindowResize = true;
35412     this.regions = {};
35413     this.addEvents({
35414         /**
35415          * @event layout
35416          * Fires when a layout is performed.
35417          * @param {Roo.LayoutManager} this
35418          */
35419         "layout" : true,
35420         /**
35421          * @event regionresized
35422          * Fires when the user resizes a region.
35423          * @param {Roo.LayoutRegion} region The resized region
35424          * @param {Number} newSize The new size (width for east/west, height for north/south)
35425          */
35426         "regionresized" : true,
35427         /**
35428          * @event regioncollapsed
35429          * Fires when a region is collapsed.
35430          * @param {Roo.LayoutRegion} region The collapsed region
35431          */
35432         "regioncollapsed" : true,
35433         /**
35434          * @event regionexpanded
35435          * Fires when a region is expanded.
35436          * @param {Roo.LayoutRegion} region The expanded region
35437          */
35438         "regionexpanded" : true
35439     });
35440     this.updating = false;
35441
35442     if (config.el) {
35443         this.el = Roo.get(config.el);
35444         this.initEvents();
35445     }
35446
35447 };
35448
35449 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35450
35451
35452     regions : null,
35453
35454     monitorWindowResize : true,
35455
35456
35457     updating : false,
35458
35459
35460     onRender : function(ct, position)
35461     {
35462         if(!this.el){
35463             this.el = Roo.get(ct);
35464             this.initEvents();
35465         }
35466         //this.fireEvent('render',this);
35467     },
35468
35469
35470     initEvents: function()
35471     {
35472
35473
35474         // ie scrollbar fix
35475         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35476             document.body.scroll = "no";
35477         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35478             this.el.position('relative');
35479         }
35480         this.id = this.el.id;
35481         this.el.addClass("roo-layout-container");
35482         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35483         if(this.el.dom != document.body ) {
35484             this.el.on('resize', this.layout,this);
35485             this.el.on('show', this.layout,this);
35486         }
35487
35488     },
35489
35490     /**
35491      * Returns true if this layout is currently being updated
35492      * @return {Boolean}
35493      */
35494     isUpdating : function(){
35495         return this.updating;
35496     },
35497
35498     /**
35499      * Suspend the LayoutManager from doing auto-layouts while
35500      * making multiple add or remove calls
35501      */
35502     beginUpdate : function(){
35503         this.updating = true;
35504     },
35505
35506     /**
35507      * Restore auto-layouts and optionally disable the manager from performing a layout
35508      * @param {Boolean} noLayout true to disable a layout update
35509      */
35510     endUpdate : function(noLayout){
35511         this.updating = false;
35512         if(!noLayout){
35513             this.layout();
35514         }
35515     },
35516
35517     layout: function(){
35518         // abstract...
35519     },
35520
35521     onRegionResized : function(region, newSize){
35522         this.fireEvent("regionresized", region, newSize);
35523         this.layout();
35524     },
35525
35526     onRegionCollapsed : function(region){
35527         this.fireEvent("regioncollapsed", region);
35528     },
35529
35530     onRegionExpanded : function(region){
35531         this.fireEvent("regionexpanded", region);
35532     },
35533
35534     /**
35535      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35536      * performs box-model adjustments.
35537      * @return {Object} The size as an object {width: (the width), height: (the height)}
35538      */
35539     getViewSize : function()
35540     {
35541         var size;
35542         if(this.el.dom != document.body){
35543             size = this.el.getSize();
35544         }else{
35545             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35546         }
35547         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35548         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35549         return size;
35550     },
35551
35552     /**
35553      * Returns the Element this layout is bound to.
35554      * @return {Roo.Element}
35555      */
35556     getEl : function(){
35557         return this.el;
35558     },
35559
35560     /**
35561      * Returns the specified region.
35562      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35563      * @return {Roo.LayoutRegion}
35564      */
35565     getRegion : function(target){
35566         return this.regions[target.toLowerCase()];
35567     },
35568
35569     onWindowResize : function(){
35570         if(this.monitorWindowResize){
35571             this.layout();
35572         }
35573     }
35574 });
35575 /*
35576  * Based on:
35577  * Ext JS Library 1.1.1
35578  * Copyright(c) 2006-2007, Ext JS, LLC.
35579  *
35580  * Originally Released Under LGPL - original licence link has changed is not relivant.
35581  *
35582  * Fork - LGPL
35583  * <script type="text/javascript">
35584  */
35585 /**
35586  * @class Roo.bootstrap.layout.Border
35587  * @extends Roo.bootstrap.layout.Manager
35588  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35589  * please see: examples/bootstrap/nested.html<br><br>
35590  
35591 <b>The container the layout is rendered into can be either the body element or any other element.
35592 If it is not the body element, the container needs to either be an absolute positioned element,
35593 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35594 the container size if it is not the body element.</b>
35595
35596 * @constructor
35597 * Create a new Border
35598 * @param {Object} config Configuration options
35599  */
35600 Roo.bootstrap.layout.Border = function(config){
35601     config = config || {};
35602     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35603     
35604     
35605     
35606     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35607         if(config[region]){
35608             config[region].region = region;
35609             this.addRegion(config[region]);
35610         }
35611     },this);
35612     
35613 };
35614
35615 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35616
35617 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35618     
35619     parent : false, // this might point to a 'nest' or a ???
35620     
35621     /**
35622      * Creates and adds a new region if it doesn't already exist.
35623      * @param {String} target The target region key (north, south, east, west or center).
35624      * @param {Object} config The regions config object
35625      * @return {BorderLayoutRegion} The new region
35626      */
35627     addRegion : function(config)
35628     {
35629         if(!this.regions[config.region]){
35630             var r = this.factory(config);
35631             this.bindRegion(r);
35632         }
35633         return this.regions[config.region];
35634     },
35635
35636     // private (kinda)
35637     bindRegion : function(r){
35638         this.regions[r.config.region] = r;
35639         
35640         r.on("visibilitychange",    this.layout, this);
35641         r.on("paneladded",          this.layout, this);
35642         r.on("panelremoved",        this.layout, this);
35643         r.on("invalidated",         this.layout, this);
35644         r.on("resized",             this.onRegionResized, this);
35645         r.on("collapsed",           this.onRegionCollapsed, this);
35646         r.on("expanded",            this.onRegionExpanded, this);
35647     },
35648
35649     /**
35650      * Performs a layout update.
35651      */
35652     layout : function()
35653     {
35654         if(this.updating) {
35655             return;
35656         }
35657         
35658         // render all the rebions if they have not been done alreayd?
35659         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35660             if(this.regions[region] && !this.regions[region].bodyEl){
35661                 this.regions[region].onRender(this.el)
35662             }
35663         },this);
35664         
35665         var size = this.getViewSize();
35666         var w = size.width;
35667         var h = size.height;
35668         var centerW = w;
35669         var centerH = h;
35670         var centerY = 0;
35671         var centerX = 0;
35672         //var x = 0, y = 0;
35673
35674         var rs = this.regions;
35675         var north = rs["north"];
35676         var south = rs["south"]; 
35677         var west = rs["west"];
35678         var east = rs["east"];
35679         var center = rs["center"];
35680         //if(this.hideOnLayout){ // not supported anymore
35681             //c.el.setStyle("display", "none");
35682         //}
35683         if(north && north.isVisible()){
35684             var b = north.getBox();
35685             var m = north.getMargins();
35686             b.width = w - (m.left+m.right);
35687             b.x = m.left;
35688             b.y = m.top;
35689             centerY = b.height + b.y + m.bottom;
35690             centerH -= centerY;
35691             north.updateBox(this.safeBox(b));
35692         }
35693         if(south && south.isVisible()){
35694             var b = south.getBox();
35695             var m = south.getMargins();
35696             b.width = w - (m.left+m.right);
35697             b.x = m.left;
35698             var totalHeight = (b.height + m.top + m.bottom);
35699             b.y = h - totalHeight + m.top;
35700             centerH -= totalHeight;
35701             south.updateBox(this.safeBox(b));
35702         }
35703         if(west && west.isVisible()){
35704             var b = west.getBox();
35705             var m = west.getMargins();
35706             b.height = centerH - (m.top+m.bottom);
35707             b.x = m.left;
35708             b.y = centerY + m.top;
35709             var totalWidth = (b.width + m.left + m.right);
35710             centerX += totalWidth;
35711             centerW -= totalWidth;
35712             west.updateBox(this.safeBox(b));
35713         }
35714         if(east && east.isVisible()){
35715             var b = east.getBox();
35716             var m = east.getMargins();
35717             b.height = centerH - (m.top+m.bottom);
35718             var totalWidth = (b.width + m.left + m.right);
35719             b.x = w - totalWidth + m.left;
35720             b.y = centerY + m.top;
35721             centerW -= totalWidth;
35722             east.updateBox(this.safeBox(b));
35723         }
35724         if(center){
35725             var m = center.getMargins();
35726             var centerBox = {
35727                 x: centerX + m.left,
35728                 y: centerY + m.top,
35729                 width: centerW - (m.left+m.right),
35730                 height: centerH - (m.top+m.bottom)
35731             };
35732             //if(this.hideOnLayout){
35733                 //center.el.setStyle("display", "block");
35734             //}
35735             center.updateBox(this.safeBox(centerBox));
35736         }
35737         this.el.repaint();
35738         this.fireEvent("layout", this);
35739     },
35740
35741     // private
35742     safeBox : function(box){
35743         box.width = Math.max(0, box.width);
35744         box.height = Math.max(0, box.height);
35745         return box;
35746     },
35747
35748     /**
35749      * Adds a ContentPanel (or subclass) to this layout.
35750      * @param {String} target The target region key (north, south, east, west or center).
35751      * @param {Roo.ContentPanel} panel The panel to add
35752      * @return {Roo.ContentPanel} The added panel
35753      */
35754     add : function(target, panel){
35755          
35756         target = target.toLowerCase();
35757         return this.regions[target].add(panel);
35758     },
35759
35760     /**
35761      * Remove a ContentPanel (or subclass) to this layout.
35762      * @param {String} target The target region key (north, south, east, west or center).
35763      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35764      * @return {Roo.ContentPanel} The removed panel
35765      */
35766     remove : function(target, panel){
35767         target = target.toLowerCase();
35768         return this.regions[target].remove(panel);
35769     },
35770
35771     /**
35772      * Searches all regions for a panel with the specified id
35773      * @param {String} panelId
35774      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35775      */
35776     findPanel : function(panelId){
35777         var rs = this.regions;
35778         for(var target in rs){
35779             if(typeof rs[target] != "function"){
35780                 var p = rs[target].getPanel(panelId);
35781                 if(p){
35782                     return p;
35783                 }
35784             }
35785         }
35786         return null;
35787     },
35788
35789     /**
35790      * Searches all regions for a panel with the specified id and activates (shows) it.
35791      * @param {String/ContentPanel} panelId The panels id or the panel itself
35792      * @return {Roo.ContentPanel} The shown panel or null
35793      */
35794     showPanel : function(panelId) {
35795       var rs = this.regions;
35796       for(var target in rs){
35797          var r = rs[target];
35798          if(typeof r != "function"){
35799             if(r.hasPanel(panelId)){
35800                return r.showPanel(panelId);
35801             }
35802          }
35803       }
35804       return null;
35805    },
35806
35807    /**
35808      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35809      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35810      */
35811    /*
35812     restoreState : function(provider){
35813         if(!provider){
35814             provider = Roo.state.Manager;
35815         }
35816         var sm = new Roo.LayoutStateManager();
35817         sm.init(this, provider);
35818     },
35819 */
35820  
35821  
35822     /**
35823      * Adds a xtype elements to the layout.
35824      * <pre><code>
35825
35826 layout.addxtype({
35827        xtype : 'ContentPanel',
35828        region: 'west',
35829        items: [ .... ]
35830    }
35831 );
35832
35833 layout.addxtype({
35834         xtype : 'NestedLayoutPanel',
35835         region: 'west',
35836         layout: {
35837            center: { },
35838            west: { }   
35839         },
35840         items : [ ... list of content panels or nested layout panels.. ]
35841    }
35842 );
35843 </code></pre>
35844      * @param {Object} cfg Xtype definition of item to add.
35845      */
35846     addxtype : function(cfg)
35847     {
35848         // basically accepts a pannel...
35849         // can accept a layout region..!?!?
35850         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35851         
35852         
35853         // theory?  children can only be panels??
35854         
35855         //if (!cfg.xtype.match(/Panel$/)) {
35856         //    return false;
35857         //}
35858         var ret = false;
35859         
35860         if (typeof(cfg.region) == 'undefined') {
35861             Roo.log("Failed to add Panel, region was not set");
35862             Roo.log(cfg);
35863             return false;
35864         }
35865         var region = cfg.region;
35866         delete cfg.region;
35867         
35868           
35869         var xitems = [];
35870         if (cfg.items) {
35871             xitems = cfg.items;
35872             delete cfg.items;
35873         }
35874         var nb = false;
35875         
35876         if ( region == 'center') {
35877             Roo.log("Center: " + cfg.title);
35878         }
35879         
35880         
35881         switch(cfg.xtype) 
35882         {
35883             case 'Content':  // ContentPanel (el, cfg)
35884             case 'Scroll':  // ContentPanel (el, cfg)
35885             case 'View': 
35886                 cfg.autoCreate = cfg.autoCreate || true;
35887                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35888                 //} else {
35889                 //    var el = this.el.createChild();
35890                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35891                 //}
35892                 
35893                 this.add(region, ret);
35894                 break;
35895             
35896             /*
35897             case 'TreePanel': // our new panel!
35898                 cfg.el = this.el.createChild();
35899                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35900                 this.add(region, ret);
35901                 break;
35902             */
35903             
35904             case 'Nest': 
35905                 // create a new Layout (which is  a Border Layout...
35906                 
35907                 var clayout = cfg.layout;
35908                 clayout.el  = this.el.createChild();
35909                 clayout.items   = clayout.items  || [];
35910                 
35911                 delete cfg.layout;
35912                 
35913                 // replace this exitems with the clayout ones..
35914                 xitems = clayout.items;
35915                  
35916                 // force background off if it's in center...
35917                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35918                     cfg.background = false;
35919                 }
35920                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35921                 
35922                 
35923                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35924                 //console.log('adding nested layout panel '  + cfg.toSource());
35925                 this.add(region, ret);
35926                 nb = {}; /// find first...
35927                 break;
35928             
35929             case 'Grid':
35930                 
35931                 // needs grid and region
35932                 
35933                 //var el = this.getRegion(region).el.createChild();
35934                 /*
35935                  *var el = this.el.createChild();
35936                 // create the grid first...
35937                 cfg.grid.container = el;
35938                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35939                 */
35940                 
35941                 if (region == 'center' && this.active ) {
35942                     cfg.background = false;
35943                 }
35944                 
35945                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35946                 
35947                 this.add(region, ret);
35948                 /*
35949                 if (cfg.background) {
35950                     // render grid on panel activation (if panel background)
35951                     ret.on('activate', function(gp) {
35952                         if (!gp.grid.rendered) {
35953                     //        gp.grid.render(el);
35954                         }
35955                     });
35956                 } else {
35957                   //  cfg.grid.render(el);
35958                 }
35959                 */
35960                 break;
35961            
35962            
35963             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35964                 // it was the old xcomponent building that caused this before.
35965                 // espeically if border is the top element in the tree.
35966                 ret = this;
35967                 break; 
35968                 
35969                     
35970                 
35971                 
35972                 
35973             default:
35974                 /*
35975                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35976                     
35977                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35978                     this.add(region, ret);
35979                 } else {
35980                 */
35981                     Roo.log(cfg);
35982                     throw "Can not add '" + cfg.xtype + "' to Border";
35983                     return null;
35984              
35985                                 
35986              
35987         }
35988         this.beginUpdate();
35989         // add children..
35990         var region = '';
35991         var abn = {};
35992         Roo.each(xitems, function(i)  {
35993             region = nb && i.region ? i.region : false;
35994             
35995             var add = ret.addxtype(i);
35996            
35997             if (region) {
35998                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35999                 if (!i.background) {
36000                     abn[region] = nb[region] ;
36001                 }
36002             }
36003             
36004         });
36005         this.endUpdate();
36006
36007         // make the last non-background panel active..
36008         //if (nb) { Roo.log(abn); }
36009         if (nb) {
36010             
36011             for(var r in abn) {
36012                 region = this.getRegion(r);
36013                 if (region) {
36014                     // tried using nb[r], but it does not work..
36015                      
36016                     region.showPanel(abn[r]);
36017                    
36018                 }
36019             }
36020         }
36021         return ret;
36022         
36023     },
36024     
36025     
36026 // private
36027     factory : function(cfg)
36028     {
36029         
36030         var validRegions = Roo.bootstrap.layout.Border.regions;
36031
36032         var target = cfg.region;
36033         cfg.mgr = this;
36034         
36035         var r = Roo.bootstrap.layout;
36036         Roo.log(target);
36037         switch(target){
36038             case "north":
36039                 return new r.North(cfg);
36040             case "south":
36041                 return new r.South(cfg);
36042             case "east":
36043                 return new r.East(cfg);
36044             case "west":
36045                 return new r.West(cfg);
36046             case "center":
36047                 return new r.Center(cfg);
36048         }
36049         throw 'Layout region "'+target+'" not supported.';
36050     }
36051     
36052     
36053 });
36054  /*
36055  * Based on:
36056  * Ext JS Library 1.1.1
36057  * Copyright(c) 2006-2007, Ext JS, LLC.
36058  *
36059  * Originally Released Under LGPL - original licence link has changed is not relivant.
36060  *
36061  * Fork - LGPL
36062  * <script type="text/javascript">
36063  */
36064  
36065 /**
36066  * @class Roo.bootstrap.layout.Basic
36067  * @extends Roo.util.Observable
36068  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36069  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36070  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36071  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36072  * @cfg {string}   region  the region that it inhabits..
36073  * @cfg {bool}   skipConfig skip config?
36074  * 
36075
36076  */
36077 Roo.bootstrap.layout.Basic = function(config){
36078     
36079     this.mgr = config.mgr;
36080     
36081     this.position = config.region;
36082     
36083     var skipConfig = config.skipConfig;
36084     
36085     this.events = {
36086         /**
36087          * @scope Roo.BasicLayoutRegion
36088          */
36089         
36090         /**
36091          * @event beforeremove
36092          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36093          * @param {Roo.LayoutRegion} this
36094          * @param {Roo.ContentPanel} panel The panel
36095          * @param {Object} e The cancel event object
36096          */
36097         "beforeremove" : true,
36098         /**
36099          * @event invalidated
36100          * Fires when the layout for this region is changed.
36101          * @param {Roo.LayoutRegion} this
36102          */
36103         "invalidated" : true,
36104         /**
36105          * @event visibilitychange
36106          * Fires when this region is shown or hidden 
36107          * @param {Roo.LayoutRegion} this
36108          * @param {Boolean} visibility true or false
36109          */
36110         "visibilitychange" : true,
36111         /**
36112          * @event paneladded
36113          * Fires when a panel is added. 
36114          * @param {Roo.LayoutRegion} this
36115          * @param {Roo.ContentPanel} panel The panel
36116          */
36117         "paneladded" : true,
36118         /**
36119          * @event panelremoved
36120          * Fires when a panel is removed. 
36121          * @param {Roo.LayoutRegion} this
36122          * @param {Roo.ContentPanel} panel The panel
36123          */
36124         "panelremoved" : true,
36125         /**
36126          * @event beforecollapse
36127          * Fires when this region before collapse.
36128          * @param {Roo.LayoutRegion} this
36129          */
36130         "beforecollapse" : true,
36131         /**
36132          * @event collapsed
36133          * Fires when this region is collapsed.
36134          * @param {Roo.LayoutRegion} this
36135          */
36136         "collapsed" : true,
36137         /**
36138          * @event expanded
36139          * Fires when this region is expanded.
36140          * @param {Roo.LayoutRegion} this
36141          */
36142         "expanded" : true,
36143         /**
36144          * @event slideshow
36145          * Fires when this region is slid into view.
36146          * @param {Roo.LayoutRegion} this
36147          */
36148         "slideshow" : true,
36149         /**
36150          * @event slidehide
36151          * Fires when this region slides out of view. 
36152          * @param {Roo.LayoutRegion} this
36153          */
36154         "slidehide" : true,
36155         /**
36156          * @event panelactivated
36157          * Fires when a panel is activated. 
36158          * @param {Roo.LayoutRegion} this
36159          * @param {Roo.ContentPanel} panel The activated panel
36160          */
36161         "panelactivated" : true,
36162         /**
36163          * @event resized
36164          * Fires when the user resizes this region. 
36165          * @param {Roo.LayoutRegion} this
36166          * @param {Number} newSize The new size (width for east/west, height for north/south)
36167          */
36168         "resized" : true
36169     };
36170     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36171     this.panels = new Roo.util.MixedCollection();
36172     this.panels.getKey = this.getPanelId.createDelegate(this);
36173     this.box = null;
36174     this.activePanel = null;
36175     // ensure listeners are added...
36176     
36177     if (config.listeners || config.events) {
36178         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36179             listeners : config.listeners || {},
36180             events : config.events || {}
36181         });
36182     }
36183     
36184     if(skipConfig !== true){
36185         this.applyConfig(config);
36186     }
36187 };
36188
36189 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36190 {
36191     getPanelId : function(p){
36192         return p.getId();
36193     },
36194     
36195     applyConfig : function(config){
36196         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36197         this.config = config;
36198         
36199     },
36200     
36201     /**
36202      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36203      * the width, for horizontal (north, south) the height.
36204      * @param {Number} newSize The new width or height
36205      */
36206     resizeTo : function(newSize){
36207         var el = this.el ? this.el :
36208                  (this.activePanel ? this.activePanel.getEl() : null);
36209         if(el){
36210             switch(this.position){
36211                 case "east":
36212                 case "west":
36213                     el.setWidth(newSize);
36214                     this.fireEvent("resized", this, newSize);
36215                 break;
36216                 case "north":
36217                 case "south":
36218                     el.setHeight(newSize);
36219                     this.fireEvent("resized", this, newSize);
36220                 break;                
36221             }
36222         }
36223     },
36224     
36225     getBox : function(){
36226         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36227     },
36228     
36229     getMargins : function(){
36230         return this.margins;
36231     },
36232     
36233     updateBox : function(box){
36234         this.box = box;
36235         var el = this.activePanel.getEl();
36236         el.dom.style.left = box.x + "px";
36237         el.dom.style.top = box.y + "px";
36238         this.activePanel.setSize(box.width, box.height);
36239     },
36240     
36241     /**
36242      * Returns the container element for this region.
36243      * @return {Roo.Element}
36244      */
36245     getEl : function(){
36246         return this.activePanel;
36247     },
36248     
36249     /**
36250      * Returns true if this region is currently visible.
36251      * @return {Boolean}
36252      */
36253     isVisible : function(){
36254         return this.activePanel ? true : false;
36255     },
36256     
36257     setActivePanel : function(panel){
36258         panel = this.getPanel(panel);
36259         if(this.activePanel && this.activePanel != panel){
36260             this.activePanel.setActiveState(false);
36261             this.activePanel.getEl().setLeftTop(-10000,-10000);
36262         }
36263         this.activePanel = panel;
36264         panel.setActiveState(true);
36265         if(this.box){
36266             panel.setSize(this.box.width, this.box.height);
36267         }
36268         this.fireEvent("panelactivated", this, panel);
36269         this.fireEvent("invalidated");
36270     },
36271     
36272     /**
36273      * Show the specified panel.
36274      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36275      * @return {Roo.ContentPanel} The shown panel or null
36276      */
36277     showPanel : function(panel){
36278         panel = this.getPanel(panel);
36279         if(panel){
36280             this.setActivePanel(panel);
36281         }
36282         return panel;
36283     },
36284     
36285     /**
36286      * Get the active panel for this region.
36287      * @return {Roo.ContentPanel} The active panel or null
36288      */
36289     getActivePanel : function(){
36290         return this.activePanel;
36291     },
36292     
36293     /**
36294      * Add the passed ContentPanel(s)
36295      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36296      * @return {Roo.ContentPanel} The panel added (if only one was added)
36297      */
36298     add : function(panel){
36299         if(arguments.length > 1){
36300             for(var i = 0, len = arguments.length; i < len; i++) {
36301                 this.add(arguments[i]);
36302             }
36303             return null;
36304         }
36305         if(this.hasPanel(panel)){
36306             this.showPanel(panel);
36307             return panel;
36308         }
36309         var el = panel.getEl();
36310         if(el.dom.parentNode != this.mgr.el.dom){
36311             this.mgr.el.dom.appendChild(el.dom);
36312         }
36313         if(panel.setRegion){
36314             panel.setRegion(this);
36315         }
36316         this.panels.add(panel);
36317         el.setStyle("position", "absolute");
36318         if(!panel.background){
36319             this.setActivePanel(panel);
36320             if(this.config.initialSize && this.panels.getCount()==1){
36321                 this.resizeTo(this.config.initialSize);
36322             }
36323         }
36324         this.fireEvent("paneladded", this, panel);
36325         return panel;
36326     },
36327     
36328     /**
36329      * Returns true if the panel is in this region.
36330      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36331      * @return {Boolean}
36332      */
36333     hasPanel : function(panel){
36334         if(typeof panel == "object"){ // must be panel obj
36335             panel = panel.getId();
36336         }
36337         return this.getPanel(panel) ? true : false;
36338     },
36339     
36340     /**
36341      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36342      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36343      * @param {Boolean} preservePanel Overrides the config preservePanel option
36344      * @return {Roo.ContentPanel} The panel that was removed
36345      */
36346     remove : function(panel, preservePanel){
36347         panel = this.getPanel(panel);
36348         if(!panel){
36349             return null;
36350         }
36351         var e = {};
36352         this.fireEvent("beforeremove", this, panel, e);
36353         if(e.cancel === true){
36354             return null;
36355         }
36356         var panelId = panel.getId();
36357         this.panels.removeKey(panelId);
36358         return panel;
36359     },
36360     
36361     /**
36362      * Returns the panel specified or null if it's not in this region.
36363      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36364      * @return {Roo.ContentPanel}
36365      */
36366     getPanel : function(id){
36367         if(typeof id == "object"){ // must be panel obj
36368             return id;
36369         }
36370         return this.panels.get(id);
36371     },
36372     
36373     /**
36374      * Returns this regions position (north/south/east/west/center).
36375      * @return {String} 
36376      */
36377     getPosition: function(){
36378         return this.position;    
36379     }
36380 });/*
36381  * Based on:
36382  * Ext JS Library 1.1.1
36383  * Copyright(c) 2006-2007, Ext JS, LLC.
36384  *
36385  * Originally Released Under LGPL - original licence link has changed is not relivant.
36386  *
36387  * Fork - LGPL
36388  * <script type="text/javascript">
36389  */
36390  
36391 /**
36392  * @class Roo.bootstrap.layout.Region
36393  * @extends Roo.bootstrap.layout.Basic
36394  * This class represents a region in a layout manager.
36395  
36396  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36397  * @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})
36398  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36399  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36400  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36401  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36402  * @cfg {String}    title           The title for the region (overrides panel titles)
36403  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36404  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36405  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36406  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36407  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36408  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36409  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36410  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36411  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36412  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36413
36414  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36415  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36416  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36417  * @cfg {Number}    width           For East/West panels
36418  * @cfg {Number}    height          For North/South panels
36419  * @cfg {Boolean}   split           To show the splitter
36420  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36421  * 
36422  * @cfg {string}   cls             Extra CSS classes to add to region
36423  * 
36424  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36425  * @cfg {string}   region  the region that it inhabits..
36426  *
36427
36428  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36429  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36430
36431  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36432  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36433  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36434  */
36435 Roo.bootstrap.layout.Region = function(config)
36436 {
36437     this.applyConfig(config);
36438
36439     var mgr = config.mgr;
36440     var pos = config.region;
36441     config.skipConfig = true;
36442     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36443     
36444     if (mgr.el) {
36445         this.onRender(mgr.el);   
36446     }
36447      
36448     this.visible = true;
36449     this.collapsed = false;
36450     this.unrendered_panels = [];
36451 };
36452
36453 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36454
36455     position: '', // set by wrapper (eg. north/south etc..)
36456     unrendered_panels : null,  // unrendered panels.
36457     
36458     tabPosition : false,
36459     
36460     mgr: false, // points to 'Border'
36461     
36462     
36463     createBody : function(){
36464         /** This region's body element 
36465         * @type Roo.Element */
36466         this.bodyEl = this.el.createChild({
36467                 tag: "div",
36468                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36469         });
36470     },
36471
36472     onRender: function(ctr, pos)
36473     {
36474         var dh = Roo.DomHelper;
36475         /** This region's container element 
36476         * @type Roo.Element */
36477         this.el = dh.append(ctr.dom, {
36478                 tag: "div",
36479                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36480             }, true);
36481         /** This region's title element 
36482         * @type Roo.Element */
36483     
36484         this.titleEl = dh.append(this.el.dom,  {
36485                 tag: "div",
36486                 unselectable: "on",
36487                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36488                 children:[
36489                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36490                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36491                 ]
36492             }, true);
36493         
36494         this.titleEl.enableDisplayMode();
36495         /** This region's title text element 
36496         * @type HTMLElement */
36497         this.titleTextEl = this.titleEl.dom.firstChild;
36498         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36499         /*
36500         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36501         this.closeBtn.enableDisplayMode();
36502         this.closeBtn.on("click", this.closeClicked, this);
36503         this.closeBtn.hide();
36504     */
36505         this.createBody(this.config);
36506         if(this.config.hideWhenEmpty){
36507             this.hide();
36508             this.on("paneladded", this.validateVisibility, this);
36509             this.on("panelremoved", this.validateVisibility, this);
36510         }
36511         if(this.autoScroll){
36512             this.bodyEl.setStyle("overflow", "auto");
36513         }else{
36514             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36515         }
36516         //if(c.titlebar !== false){
36517             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36518                 this.titleEl.hide();
36519             }else{
36520                 this.titleEl.show();
36521                 if(this.config.title){
36522                     this.titleTextEl.innerHTML = this.config.title;
36523                 }
36524             }
36525         //}
36526         if(this.config.collapsed){
36527             this.collapse(true);
36528         }
36529         if(this.config.hidden){
36530             this.hide();
36531         }
36532         
36533         if (this.unrendered_panels && this.unrendered_panels.length) {
36534             for (var i =0;i< this.unrendered_panels.length; i++) {
36535                 this.add(this.unrendered_panels[i]);
36536             }
36537             this.unrendered_panels = null;
36538             
36539         }
36540         
36541     },
36542     
36543     applyConfig : function(c)
36544     {
36545         /*
36546          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36547             var dh = Roo.DomHelper;
36548             if(c.titlebar !== false){
36549                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36550                 this.collapseBtn.on("click", this.collapse, this);
36551                 this.collapseBtn.enableDisplayMode();
36552                 /*
36553                 if(c.showPin === true || this.showPin){
36554                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36555                     this.stickBtn.enableDisplayMode();
36556                     this.stickBtn.on("click", this.expand, this);
36557                     this.stickBtn.hide();
36558                 }
36559                 
36560             }
36561             */
36562             /** This region's collapsed element
36563             * @type Roo.Element */
36564             /*
36565              *
36566             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36567                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36568             ]}, true);
36569             
36570             if(c.floatable !== false){
36571                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36572                this.collapsedEl.on("click", this.collapseClick, this);
36573             }
36574
36575             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36576                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36577                    id: "message", unselectable: "on", style:{"float":"left"}});
36578                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36579              }
36580             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36581             this.expandBtn.on("click", this.expand, this);
36582             
36583         }
36584         
36585         if(this.collapseBtn){
36586             this.collapseBtn.setVisible(c.collapsible == true);
36587         }
36588         
36589         this.cmargins = c.cmargins || this.cmargins ||
36590                          (this.position == "west" || this.position == "east" ?
36591                              {top: 0, left: 2, right:2, bottom: 0} :
36592                              {top: 2, left: 0, right:0, bottom: 2});
36593         */
36594         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36595         
36596         
36597         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36598         
36599         this.autoScroll = c.autoScroll || false;
36600         
36601         
36602        
36603         
36604         this.duration = c.duration || .30;
36605         this.slideDuration = c.slideDuration || .45;
36606         this.config = c;
36607        
36608     },
36609     /**
36610      * Returns true if this region is currently visible.
36611      * @return {Boolean}
36612      */
36613     isVisible : function(){
36614         return this.visible;
36615     },
36616
36617     /**
36618      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36619      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36620      */
36621     //setCollapsedTitle : function(title){
36622     //    title = title || "&#160;";
36623      //   if(this.collapsedTitleTextEl){
36624       //      this.collapsedTitleTextEl.innerHTML = title;
36625        // }
36626     //},
36627
36628     getBox : function(){
36629         var b;
36630       //  if(!this.collapsed){
36631             b = this.el.getBox(false, true);
36632        // }else{
36633           //  b = this.collapsedEl.getBox(false, true);
36634         //}
36635         return b;
36636     },
36637
36638     getMargins : function(){
36639         return this.margins;
36640         //return this.collapsed ? this.cmargins : this.margins;
36641     },
36642 /*
36643     highlight : function(){
36644         this.el.addClass("x-layout-panel-dragover");
36645     },
36646
36647     unhighlight : function(){
36648         this.el.removeClass("x-layout-panel-dragover");
36649     },
36650 */
36651     updateBox : function(box)
36652     {
36653         if (!this.bodyEl) {
36654             return; // not rendered yet..
36655         }
36656         
36657         this.box = box;
36658         if(!this.collapsed){
36659             this.el.dom.style.left = box.x + "px";
36660             this.el.dom.style.top = box.y + "px";
36661             this.updateBody(box.width, box.height);
36662         }else{
36663             this.collapsedEl.dom.style.left = box.x + "px";
36664             this.collapsedEl.dom.style.top = box.y + "px";
36665             this.collapsedEl.setSize(box.width, box.height);
36666         }
36667         if(this.tabs){
36668             this.tabs.autoSizeTabs();
36669         }
36670     },
36671
36672     updateBody : function(w, h)
36673     {
36674         if(w !== null){
36675             this.el.setWidth(w);
36676             w -= this.el.getBorderWidth("rl");
36677             if(this.config.adjustments){
36678                 w += this.config.adjustments[0];
36679             }
36680         }
36681         if(h !== null && h > 0){
36682             this.el.setHeight(h);
36683             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36684             h -= this.el.getBorderWidth("tb");
36685             if(this.config.adjustments){
36686                 h += this.config.adjustments[1];
36687             }
36688             this.bodyEl.setHeight(h);
36689             if(this.tabs){
36690                 h = this.tabs.syncHeight(h);
36691             }
36692         }
36693         if(this.panelSize){
36694             w = w !== null ? w : this.panelSize.width;
36695             h = h !== null ? h : this.panelSize.height;
36696         }
36697         if(this.activePanel){
36698             var el = this.activePanel.getEl();
36699             w = w !== null ? w : el.getWidth();
36700             h = h !== null ? h : el.getHeight();
36701             this.panelSize = {width: w, height: h};
36702             this.activePanel.setSize(w, h);
36703         }
36704         if(Roo.isIE && this.tabs){
36705             this.tabs.el.repaint();
36706         }
36707     },
36708
36709     /**
36710      * Returns the container element for this region.
36711      * @return {Roo.Element}
36712      */
36713     getEl : function(){
36714         return this.el;
36715     },
36716
36717     /**
36718      * Hides this region.
36719      */
36720     hide : function(){
36721         //if(!this.collapsed){
36722             this.el.dom.style.left = "-2000px";
36723             this.el.hide();
36724         //}else{
36725          //   this.collapsedEl.dom.style.left = "-2000px";
36726          //   this.collapsedEl.hide();
36727        // }
36728         this.visible = false;
36729         this.fireEvent("visibilitychange", this, false);
36730     },
36731
36732     /**
36733      * Shows this region if it was previously hidden.
36734      */
36735     show : function(){
36736         //if(!this.collapsed){
36737             this.el.show();
36738         //}else{
36739         //    this.collapsedEl.show();
36740        // }
36741         this.visible = true;
36742         this.fireEvent("visibilitychange", this, true);
36743     },
36744 /*
36745     closeClicked : function(){
36746         if(this.activePanel){
36747             this.remove(this.activePanel);
36748         }
36749     },
36750
36751     collapseClick : function(e){
36752         if(this.isSlid){
36753            e.stopPropagation();
36754            this.slideIn();
36755         }else{
36756            e.stopPropagation();
36757            this.slideOut();
36758         }
36759     },
36760 */
36761     /**
36762      * Collapses this region.
36763      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36764      */
36765     /*
36766     collapse : function(skipAnim, skipCheck = false){
36767         if(this.collapsed) {
36768             return;
36769         }
36770         
36771         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36772             
36773             this.collapsed = true;
36774             if(this.split){
36775                 this.split.el.hide();
36776             }
36777             if(this.config.animate && skipAnim !== true){
36778                 this.fireEvent("invalidated", this);
36779                 this.animateCollapse();
36780             }else{
36781                 this.el.setLocation(-20000,-20000);
36782                 this.el.hide();
36783                 this.collapsedEl.show();
36784                 this.fireEvent("collapsed", this);
36785                 this.fireEvent("invalidated", this);
36786             }
36787         }
36788         
36789     },
36790 */
36791     animateCollapse : function(){
36792         // overridden
36793     },
36794
36795     /**
36796      * Expands this region if it was previously collapsed.
36797      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36798      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36799      */
36800     /*
36801     expand : function(e, skipAnim){
36802         if(e) {
36803             e.stopPropagation();
36804         }
36805         if(!this.collapsed || this.el.hasActiveFx()) {
36806             return;
36807         }
36808         if(this.isSlid){
36809             this.afterSlideIn();
36810             skipAnim = true;
36811         }
36812         this.collapsed = false;
36813         if(this.config.animate && skipAnim !== true){
36814             this.animateExpand();
36815         }else{
36816             this.el.show();
36817             if(this.split){
36818                 this.split.el.show();
36819             }
36820             this.collapsedEl.setLocation(-2000,-2000);
36821             this.collapsedEl.hide();
36822             this.fireEvent("invalidated", this);
36823             this.fireEvent("expanded", this);
36824         }
36825     },
36826 */
36827     animateExpand : function(){
36828         // overridden
36829     },
36830
36831     initTabs : function()
36832     {
36833         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36834         
36835         var ts = new Roo.bootstrap.panel.Tabs({
36836             el: this.bodyEl.dom,
36837             region : this,
36838             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36839             disableTooltips: this.config.disableTabTips,
36840             toolbar : this.config.toolbar
36841         });
36842         
36843         if(this.config.hideTabs){
36844             ts.stripWrap.setDisplayed(false);
36845         }
36846         this.tabs = ts;
36847         ts.resizeTabs = this.config.resizeTabs === true;
36848         ts.minTabWidth = this.config.minTabWidth || 40;
36849         ts.maxTabWidth = this.config.maxTabWidth || 250;
36850         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36851         ts.monitorResize = false;
36852         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36853         ts.bodyEl.addClass('roo-layout-tabs-body');
36854         this.panels.each(this.initPanelAsTab, this);
36855     },
36856
36857     initPanelAsTab : function(panel){
36858         var ti = this.tabs.addTab(
36859             panel.getEl().id,
36860             panel.getTitle(),
36861             null,
36862             this.config.closeOnTab && panel.isClosable(),
36863             panel.tpl
36864         );
36865         if(panel.tabTip !== undefined){
36866             ti.setTooltip(panel.tabTip);
36867         }
36868         ti.on("activate", function(){
36869               this.setActivePanel(panel);
36870         }, this);
36871         
36872         if(this.config.closeOnTab){
36873             ti.on("beforeclose", function(t, e){
36874                 e.cancel = true;
36875                 this.remove(panel);
36876             }, this);
36877         }
36878         
36879         panel.tabItem = ti;
36880         
36881         return ti;
36882     },
36883
36884     updatePanelTitle : function(panel, title)
36885     {
36886         if(this.activePanel == panel){
36887             this.updateTitle(title);
36888         }
36889         if(this.tabs){
36890             var ti = this.tabs.getTab(panel.getEl().id);
36891             ti.setText(title);
36892             if(panel.tabTip !== undefined){
36893                 ti.setTooltip(panel.tabTip);
36894             }
36895         }
36896     },
36897
36898     updateTitle : function(title){
36899         if(this.titleTextEl && !this.config.title){
36900             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36901         }
36902     },
36903
36904     setActivePanel : function(panel)
36905     {
36906         panel = this.getPanel(panel);
36907         if(this.activePanel && this.activePanel != panel){
36908             if(this.activePanel.setActiveState(false) === false){
36909                 return;
36910             }
36911         }
36912         this.activePanel = panel;
36913         panel.setActiveState(true);
36914         if(this.panelSize){
36915             panel.setSize(this.panelSize.width, this.panelSize.height);
36916         }
36917         if(this.closeBtn){
36918             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36919         }
36920         this.updateTitle(panel.getTitle());
36921         if(this.tabs){
36922             this.fireEvent("invalidated", this);
36923         }
36924         this.fireEvent("panelactivated", this, panel);
36925     },
36926
36927     /**
36928      * Shows the specified panel.
36929      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36930      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36931      */
36932     showPanel : function(panel)
36933     {
36934         panel = this.getPanel(panel);
36935         if(panel){
36936             if(this.tabs){
36937                 var tab = this.tabs.getTab(panel.getEl().id);
36938                 if(tab.isHidden()){
36939                     this.tabs.unhideTab(tab.id);
36940                 }
36941                 tab.activate();
36942             }else{
36943                 this.setActivePanel(panel);
36944             }
36945         }
36946         return panel;
36947     },
36948
36949     /**
36950      * Get the active panel for this region.
36951      * @return {Roo.ContentPanel} The active panel or null
36952      */
36953     getActivePanel : function(){
36954         return this.activePanel;
36955     },
36956
36957     validateVisibility : function(){
36958         if(this.panels.getCount() < 1){
36959             this.updateTitle("&#160;");
36960             this.closeBtn.hide();
36961             this.hide();
36962         }else{
36963             if(!this.isVisible()){
36964                 this.show();
36965             }
36966         }
36967     },
36968
36969     /**
36970      * Adds the passed ContentPanel(s) to this region.
36971      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36972      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36973      */
36974     add : function(panel)
36975     {
36976         if(arguments.length > 1){
36977             for(var i = 0, len = arguments.length; i < len; i++) {
36978                 this.add(arguments[i]);
36979             }
36980             return null;
36981         }
36982         
36983         // if we have not been rendered yet, then we can not really do much of this..
36984         if (!this.bodyEl) {
36985             this.unrendered_panels.push(panel);
36986             return panel;
36987         }
36988         
36989         
36990         
36991         
36992         if(this.hasPanel(panel)){
36993             this.showPanel(panel);
36994             return panel;
36995         }
36996         panel.setRegion(this);
36997         this.panels.add(panel);
36998        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36999             // sinle panel - no tab...?? would it not be better to render it with the tabs,
37000             // and hide them... ???
37001             this.bodyEl.dom.appendChild(panel.getEl().dom);
37002             if(panel.background !== true){
37003                 this.setActivePanel(panel);
37004             }
37005             this.fireEvent("paneladded", this, panel);
37006             return panel;
37007         }
37008         */
37009         if(!this.tabs){
37010             this.initTabs();
37011         }else{
37012             this.initPanelAsTab(panel);
37013         }
37014         
37015         
37016         if(panel.background !== true){
37017             this.tabs.activate(panel.getEl().id);
37018         }
37019         this.fireEvent("paneladded", this, panel);
37020         return panel;
37021     },
37022
37023     /**
37024      * Hides the tab for the specified panel.
37025      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37026      */
37027     hidePanel : function(panel){
37028         if(this.tabs && (panel = this.getPanel(panel))){
37029             this.tabs.hideTab(panel.getEl().id);
37030         }
37031     },
37032
37033     /**
37034      * Unhides the tab for a previously hidden panel.
37035      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37036      */
37037     unhidePanel : function(panel){
37038         if(this.tabs && (panel = this.getPanel(panel))){
37039             this.tabs.unhideTab(panel.getEl().id);
37040         }
37041     },
37042
37043     clearPanels : function(){
37044         while(this.panels.getCount() > 0){
37045              this.remove(this.panels.first());
37046         }
37047     },
37048
37049     /**
37050      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37051      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37052      * @param {Boolean} preservePanel Overrides the config preservePanel option
37053      * @return {Roo.ContentPanel} The panel that was removed
37054      */
37055     remove : function(panel, preservePanel)
37056     {
37057         panel = this.getPanel(panel);
37058         if(!panel){
37059             return null;
37060         }
37061         var e = {};
37062         this.fireEvent("beforeremove", this, panel, e);
37063         if(e.cancel === true){
37064             return null;
37065         }
37066         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37067         var panelId = panel.getId();
37068         this.panels.removeKey(panelId);
37069         if(preservePanel){
37070             document.body.appendChild(panel.getEl().dom);
37071         }
37072         if(this.tabs){
37073             this.tabs.removeTab(panel.getEl().id);
37074         }else if (!preservePanel){
37075             this.bodyEl.dom.removeChild(panel.getEl().dom);
37076         }
37077         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37078             var p = this.panels.first();
37079             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37080             tempEl.appendChild(p.getEl().dom);
37081             this.bodyEl.update("");
37082             this.bodyEl.dom.appendChild(p.getEl().dom);
37083             tempEl = null;
37084             this.updateTitle(p.getTitle());
37085             this.tabs = null;
37086             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37087             this.setActivePanel(p);
37088         }
37089         panel.setRegion(null);
37090         if(this.activePanel == panel){
37091             this.activePanel = null;
37092         }
37093         if(this.config.autoDestroy !== false && preservePanel !== true){
37094             try{panel.destroy();}catch(e){}
37095         }
37096         this.fireEvent("panelremoved", this, panel);
37097         return panel;
37098     },
37099
37100     /**
37101      * Returns the TabPanel component used by this region
37102      * @return {Roo.TabPanel}
37103      */
37104     getTabs : function(){
37105         return this.tabs;
37106     },
37107
37108     createTool : function(parentEl, className){
37109         var btn = Roo.DomHelper.append(parentEl, {
37110             tag: "div",
37111             cls: "x-layout-tools-button",
37112             children: [ {
37113                 tag: "div",
37114                 cls: "roo-layout-tools-button-inner " + className,
37115                 html: "&#160;"
37116             }]
37117         }, true);
37118         btn.addClassOnOver("roo-layout-tools-button-over");
37119         return btn;
37120     }
37121 });/*
37122  * Based on:
37123  * Ext JS Library 1.1.1
37124  * Copyright(c) 2006-2007, Ext JS, LLC.
37125  *
37126  * Originally Released Under LGPL - original licence link has changed is not relivant.
37127  *
37128  * Fork - LGPL
37129  * <script type="text/javascript">
37130  */
37131  
37132
37133
37134 /**
37135  * @class Roo.SplitLayoutRegion
37136  * @extends Roo.LayoutRegion
37137  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37138  */
37139 Roo.bootstrap.layout.Split = function(config){
37140     this.cursor = config.cursor;
37141     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37142 };
37143
37144 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37145 {
37146     splitTip : "Drag to resize.",
37147     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37148     useSplitTips : false,
37149
37150     applyConfig : function(config){
37151         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37152     },
37153     
37154     onRender : function(ctr,pos) {
37155         
37156         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37157         if(!this.config.split){
37158             return;
37159         }
37160         if(!this.split){
37161             
37162             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37163                             tag: "div",
37164                             id: this.el.id + "-split",
37165                             cls: "roo-layout-split roo-layout-split-"+this.position,
37166                             html: "&#160;"
37167             });
37168             /** The SplitBar for this region 
37169             * @type Roo.SplitBar */
37170             // does not exist yet...
37171             Roo.log([this.position, this.orientation]);
37172             
37173             this.split = new Roo.bootstrap.SplitBar({
37174                 dragElement : splitEl,
37175                 resizingElement: this.el,
37176                 orientation : this.orientation
37177             });
37178             
37179             this.split.on("moved", this.onSplitMove, this);
37180             this.split.useShim = this.config.useShim === true;
37181             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37182             if(this.useSplitTips){
37183                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37184             }
37185             //if(config.collapsible){
37186             //    this.split.el.on("dblclick", this.collapse,  this);
37187             //}
37188         }
37189         if(typeof this.config.minSize != "undefined"){
37190             this.split.minSize = this.config.minSize;
37191         }
37192         if(typeof this.config.maxSize != "undefined"){
37193             this.split.maxSize = this.config.maxSize;
37194         }
37195         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37196             this.hideSplitter();
37197         }
37198         
37199     },
37200
37201     getHMaxSize : function(){
37202          var cmax = this.config.maxSize || 10000;
37203          var center = this.mgr.getRegion("center");
37204          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37205     },
37206
37207     getVMaxSize : function(){
37208          var cmax = this.config.maxSize || 10000;
37209          var center = this.mgr.getRegion("center");
37210          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37211     },
37212
37213     onSplitMove : function(split, newSize){
37214         this.fireEvent("resized", this, newSize);
37215     },
37216     
37217     /** 
37218      * Returns the {@link Roo.SplitBar} for this region.
37219      * @return {Roo.SplitBar}
37220      */
37221     getSplitBar : function(){
37222         return this.split;
37223     },
37224     
37225     hide : function(){
37226         this.hideSplitter();
37227         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37228     },
37229
37230     hideSplitter : function(){
37231         if(this.split){
37232             this.split.el.setLocation(-2000,-2000);
37233             this.split.el.hide();
37234         }
37235     },
37236
37237     show : function(){
37238         if(this.split){
37239             this.split.el.show();
37240         }
37241         Roo.bootstrap.layout.Split.superclass.show.call(this);
37242     },
37243     
37244     beforeSlide: function(){
37245         if(Roo.isGecko){// firefox overflow auto bug workaround
37246             this.bodyEl.clip();
37247             if(this.tabs) {
37248                 this.tabs.bodyEl.clip();
37249             }
37250             if(this.activePanel){
37251                 this.activePanel.getEl().clip();
37252                 
37253                 if(this.activePanel.beforeSlide){
37254                     this.activePanel.beforeSlide();
37255                 }
37256             }
37257         }
37258     },
37259     
37260     afterSlide : function(){
37261         if(Roo.isGecko){// firefox overflow auto bug workaround
37262             this.bodyEl.unclip();
37263             if(this.tabs) {
37264                 this.tabs.bodyEl.unclip();
37265             }
37266             if(this.activePanel){
37267                 this.activePanel.getEl().unclip();
37268                 if(this.activePanel.afterSlide){
37269                     this.activePanel.afterSlide();
37270                 }
37271             }
37272         }
37273     },
37274
37275     initAutoHide : function(){
37276         if(this.autoHide !== false){
37277             if(!this.autoHideHd){
37278                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37279                 this.autoHideHd = {
37280                     "mouseout": function(e){
37281                         if(!e.within(this.el, true)){
37282                             st.delay(500);
37283                         }
37284                     },
37285                     "mouseover" : function(e){
37286                         st.cancel();
37287                     },
37288                     scope : this
37289                 };
37290             }
37291             this.el.on(this.autoHideHd);
37292         }
37293     },
37294
37295     clearAutoHide : function(){
37296         if(this.autoHide !== false){
37297             this.el.un("mouseout", this.autoHideHd.mouseout);
37298             this.el.un("mouseover", this.autoHideHd.mouseover);
37299         }
37300     },
37301
37302     clearMonitor : function(){
37303         Roo.get(document).un("click", this.slideInIf, this);
37304     },
37305
37306     // these names are backwards but not changed for compat
37307     slideOut : function(){
37308         if(this.isSlid || this.el.hasActiveFx()){
37309             return;
37310         }
37311         this.isSlid = true;
37312         if(this.collapseBtn){
37313             this.collapseBtn.hide();
37314         }
37315         this.closeBtnState = this.closeBtn.getStyle('display');
37316         this.closeBtn.hide();
37317         if(this.stickBtn){
37318             this.stickBtn.show();
37319         }
37320         this.el.show();
37321         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37322         this.beforeSlide();
37323         this.el.setStyle("z-index", 10001);
37324         this.el.slideIn(this.getSlideAnchor(), {
37325             callback: function(){
37326                 this.afterSlide();
37327                 this.initAutoHide();
37328                 Roo.get(document).on("click", this.slideInIf, this);
37329                 this.fireEvent("slideshow", this);
37330             },
37331             scope: this,
37332             block: true
37333         });
37334     },
37335
37336     afterSlideIn : function(){
37337         this.clearAutoHide();
37338         this.isSlid = false;
37339         this.clearMonitor();
37340         this.el.setStyle("z-index", "");
37341         if(this.collapseBtn){
37342             this.collapseBtn.show();
37343         }
37344         this.closeBtn.setStyle('display', this.closeBtnState);
37345         if(this.stickBtn){
37346             this.stickBtn.hide();
37347         }
37348         this.fireEvent("slidehide", this);
37349     },
37350
37351     slideIn : function(cb){
37352         if(!this.isSlid || this.el.hasActiveFx()){
37353             Roo.callback(cb);
37354             return;
37355         }
37356         this.isSlid = false;
37357         this.beforeSlide();
37358         this.el.slideOut(this.getSlideAnchor(), {
37359             callback: function(){
37360                 this.el.setLeftTop(-10000, -10000);
37361                 this.afterSlide();
37362                 this.afterSlideIn();
37363                 Roo.callback(cb);
37364             },
37365             scope: this,
37366             block: true
37367         });
37368     },
37369     
37370     slideInIf : function(e){
37371         if(!e.within(this.el)){
37372             this.slideIn();
37373         }
37374     },
37375
37376     animateCollapse : function(){
37377         this.beforeSlide();
37378         this.el.setStyle("z-index", 20000);
37379         var anchor = this.getSlideAnchor();
37380         this.el.slideOut(anchor, {
37381             callback : function(){
37382                 this.el.setStyle("z-index", "");
37383                 this.collapsedEl.slideIn(anchor, {duration:.3});
37384                 this.afterSlide();
37385                 this.el.setLocation(-10000,-10000);
37386                 this.el.hide();
37387                 this.fireEvent("collapsed", this);
37388             },
37389             scope: this,
37390             block: true
37391         });
37392     },
37393
37394     animateExpand : function(){
37395         this.beforeSlide();
37396         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37397         this.el.setStyle("z-index", 20000);
37398         this.collapsedEl.hide({
37399             duration:.1
37400         });
37401         this.el.slideIn(this.getSlideAnchor(), {
37402             callback : function(){
37403                 this.el.setStyle("z-index", "");
37404                 this.afterSlide();
37405                 if(this.split){
37406                     this.split.el.show();
37407                 }
37408                 this.fireEvent("invalidated", this);
37409                 this.fireEvent("expanded", this);
37410             },
37411             scope: this,
37412             block: true
37413         });
37414     },
37415
37416     anchors : {
37417         "west" : "left",
37418         "east" : "right",
37419         "north" : "top",
37420         "south" : "bottom"
37421     },
37422
37423     sanchors : {
37424         "west" : "l",
37425         "east" : "r",
37426         "north" : "t",
37427         "south" : "b"
37428     },
37429
37430     canchors : {
37431         "west" : "tl-tr",
37432         "east" : "tr-tl",
37433         "north" : "tl-bl",
37434         "south" : "bl-tl"
37435     },
37436
37437     getAnchor : function(){
37438         return this.anchors[this.position];
37439     },
37440
37441     getCollapseAnchor : function(){
37442         return this.canchors[this.position];
37443     },
37444
37445     getSlideAnchor : function(){
37446         return this.sanchors[this.position];
37447     },
37448
37449     getAlignAdj : function(){
37450         var cm = this.cmargins;
37451         switch(this.position){
37452             case "west":
37453                 return [0, 0];
37454             break;
37455             case "east":
37456                 return [0, 0];
37457             break;
37458             case "north":
37459                 return [0, 0];
37460             break;
37461             case "south":
37462                 return [0, 0];
37463             break;
37464         }
37465     },
37466
37467     getExpandAdj : function(){
37468         var c = this.collapsedEl, cm = this.cmargins;
37469         switch(this.position){
37470             case "west":
37471                 return [-(cm.right+c.getWidth()+cm.left), 0];
37472             break;
37473             case "east":
37474                 return [cm.right+c.getWidth()+cm.left, 0];
37475             break;
37476             case "north":
37477                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37478             break;
37479             case "south":
37480                 return [0, cm.top+cm.bottom+c.getHeight()];
37481             break;
37482         }
37483     }
37484 });/*
37485  * Based on:
37486  * Ext JS Library 1.1.1
37487  * Copyright(c) 2006-2007, Ext JS, LLC.
37488  *
37489  * Originally Released Under LGPL - original licence link has changed is not relivant.
37490  *
37491  * Fork - LGPL
37492  * <script type="text/javascript">
37493  */
37494 /*
37495  * These classes are private internal classes
37496  */
37497 Roo.bootstrap.layout.Center = function(config){
37498     config.region = "center";
37499     Roo.bootstrap.layout.Region.call(this, config);
37500     this.visible = true;
37501     this.minWidth = config.minWidth || 20;
37502     this.minHeight = config.minHeight || 20;
37503 };
37504
37505 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37506     hide : function(){
37507         // center panel can't be hidden
37508     },
37509     
37510     show : function(){
37511         // center panel can't be hidden
37512     },
37513     
37514     getMinWidth: function(){
37515         return this.minWidth;
37516     },
37517     
37518     getMinHeight: function(){
37519         return this.minHeight;
37520     }
37521 });
37522
37523
37524
37525
37526  
37527
37528
37529
37530
37531
37532
37533 Roo.bootstrap.layout.North = function(config)
37534 {
37535     config.region = 'north';
37536     config.cursor = 'n-resize';
37537     
37538     Roo.bootstrap.layout.Split.call(this, config);
37539     
37540     
37541     if(this.split){
37542         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37543         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37544         this.split.el.addClass("roo-layout-split-v");
37545     }
37546     var size = config.initialSize || config.height;
37547     if(typeof size != "undefined"){
37548         this.el.setHeight(size);
37549     }
37550 };
37551 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37552 {
37553     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37554     
37555     
37556     
37557     getBox : function(){
37558         if(this.collapsed){
37559             return this.collapsedEl.getBox();
37560         }
37561         var box = this.el.getBox();
37562         if(this.split){
37563             box.height += this.split.el.getHeight();
37564         }
37565         return box;
37566     },
37567     
37568     updateBox : function(box){
37569         if(this.split && !this.collapsed){
37570             box.height -= this.split.el.getHeight();
37571             this.split.el.setLeft(box.x);
37572             this.split.el.setTop(box.y+box.height);
37573             this.split.el.setWidth(box.width);
37574         }
37575         if(this.collapsed){
37576             this.updateBody(box.width, null);
37577         }
37578         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37579     }
37580 });
37581
37582
37583
37584
37585
37586 Roo.bootstrap.layout.South = function(config){
37587     config.region = 'south';
37588     config.cursor = 's-resize';
37589     Roo.bootstrap.layout.Split.call(this, config);
37590     if(this.split){
37591         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37592         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37593         this.split.el.addClass("roo-layout-split-v");
37594     }
37595     var size = config.initialSize || config.height;
37596     if(typeof size != "undefined"){
37597         this.el.setHeight(size);
37598     }
37599 };
37600
37601 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37602     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37603     getBox : function(){
37604         if(this.collapsed){
37605             return this.collapsedEl.getBox();
37606         }
37607         var box = this.el.getBox();
37608         if(this.split){
37609             var sh = this.split.el.getHeight();
37610             box.height += sh;
37611             box.y -= sh;
37612         }
37613         return box;
37614     },
37615     
37616     updateBox : function(box){
37617         if(this.split && !this.collapsed){
37618             var sh = this.split.el.getHeight();
37619             box.height -= sh;
37620             box.y += sh;
37621             this.split.el.setLeft(box.x);
37622             this.split.el.setTop(box.y-sh);
37623             this.split.el.setWidth(box.width);
37624         }
37625         if(this.collapsed){
37626             this.updateBody(box.width, null);
37627         }
37628         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37629     }
37630 });
37631
37632 Roo.bootstrap.layout.East = function(config){
37633     config.region = "east";
37634     config.cursor = "e-resize";
37635     Roo.bootstrap.layout.Split.call(this, config);
37636     if(this.split){
37637         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37638         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37639         this.split.el.addClass("roo-layout-split-h");
37640     }
37641     var size = config.initialSize || config.width;
37642     if(typeof size != "undefined"){
37643         this.el.setWidth(size);
37644     }
37645 };
37646 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37647     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37648     getBox : function(){
37649         if(this.collapsed){
37650             return this.collapsedEl.getBox();
37651         }
37652         var box = this.el.getBox();
37653         if(this.split){
37654             var sw = this.split.el.getWidth();
37655             box.width += sw;
37656             box.x -= sw;
37657         }
37658         return box;
37659     },
37660
37661     updateBox : function(box){
37662         if(this.split && !this.collapsed){
37663             var sw = this.split.el.getWidth();
37664             box.width -= sw;
37665             this.split.el.setLeft(box.x);
37666             this.split.el.setTop(box.y);
37667             this.split.el.setHeight(box.height);
37668             box.x += sw;
37669         }
37670         if(this.collapsed){
37671             this.updateBody(null, box.height);
37672         }
37673         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37674     }
37675 });
37676
37677 Roo.bootstrap.layout.West = function(config){
37678     config.region = "west";
37679     config.cursor = "w-resize";
37680     
37681     Roo.bootstrap.layout.Split.call(this, config);
37682     if(this.split){
37683         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37684         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37685         this.split.el.addClass("roo-layout-split-h");
37686     }
37687     
37688 };
37689 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37690     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37691     
37692     onRender: function(ctr, pos)
37693     {
37694         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37695         var size = this.config.initialSize || this.config.width;
37696         if(typeof size != "undefined"){
37697             this.el.setWidth(size);
37698         }
37699     },
37700     
37701     getBox : function(){
37702         if(this.collapsed){
37703             return this.collapsedEl.getBox();
37704         }
37705         var box = this.el.getBox();
37706         if(this.split){
37707             box.width += this.split.el.getWidth();
37708         }
37709         return box;
37710     },
37711     
37712     updateBox : function(box){
37713         if(this.split && !this.collapsed){
37714             var sw = this.split.el.getWidth();
37715             box.width -= sw;
37716             this.split.el.setLeft(box.x+box.width);
37717             this.split.el.setTop(box.y);
37718             this.split.el.setHeight(box.height);
37719         }
37720         if(this.collapsed){
37721             this.updateBody(null, box.height);
37722         }
37723         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37724     }
37725 });Roo.namespace("Roo.bootstrap.panel");/*
37726  * Based on:
37727  * Ext JS Library 1.1.1
37728  * Copyright(c) 2006-2007, Ext JS, LLC.
37729  *
37730  * Originally Released Under LGPL - original licence link has changed is not relivant.
37731  *
37732  * Fork - LGPL
37733  * <script type="text/javascript">
37734  */
37735 /**
37736  * @class Roo.ContentPanel
37737  * @extends Roo.util.Observable
37738  * A basic ContentPanel element.
37739  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37740  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37741  * @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
37742  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37743  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37744  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37745  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37746  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37747  * @cfg {String} title          The title for this panel
37748  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37749  * @cfg {String} url            Calls {@link #setUrl} with this value
37750  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37751  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37752  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37753  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37754  * @cfg {Boolean} badges render the badges
37755
37756  * @constructor
37757  * Create a new ContentPanel.
37758  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37759  * @param {String/Object} config A string to set only the title or a config object
37760  * @param {String} content (optional) Set the HTML content for this panel
37761  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37762  */
37763 Roo.bootstrap.panel.Content = function( config){
37764     
37765     this.tpl = config.tpl || false;
37766     
37767     var el = config.el;
37768     var content = config.content;
37769
37770     if(config.autoCreate){ // xtype is available if this is called from factory
37771         el = Roo.id();
37772     }
37773     this.el = Roo.get(el);
37774     if(!this.el && config && config.autoCreate){
37775         if(typeof config.autoCreate == "object"){
37776             if(!config.autoCreate.id){
37777                 config.autoCreate.id = config.id||el;
37778             }
37779             this.el = Roo.DomHelper.append(document.body,
37780                         config.autoCreate, true);
37781         }else{
37782             var elcfg =  {   tag: "div",
37783                             cls: "roo-layout-inactive-content",
37784                             id: config.id||el
37785                             };
37786             if (config.html) {
37787                 elcfg.html = config.html;
37788                 
37789             }
37790                         
37791             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37792         }
37793     } 
37794     this.closable = false;
37795     this.loaded = false;
37796     this.active = false;
37797    
37798       
37799     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37800         
37801         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37802         
37803         this.wrapEl = this.el; //this.el.wrap();
37804         var ti = [];
37805         if (config.toolbar.items) {
37806             ti = config.toolbar.items ;
37807             delete config.toolbar.items ;
37808         }
37809         
37810         var nitems = [];
37811         this.toolbar.render(this.wrapEl, 'before');
37812         for(var i =0;i < ti.length;i++) {
37813           //  Roo.log(['add child', items[i]]);
37814             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37815         }
37816         this.toolbar.items = nitems;
37817         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37818         delete config.toolbar;
37819         
37820     }
37821     /*
37822     // xtype created footer. - not sure if will work as we normally have to render first..
37823     if (this.footer && !this.footer.el && this.footer.xtype) {
37824         if (!this.wrapEl) {
37825             this.wrapEl = this.el.wrap();
37826         }
37827     
37828         this.footer.container = this.wrapEl.createChild();
37829          
37830         this.footer = Roo.factory(this.footer, Roo);
37831         
37832     }
37833     */
37834     
37835      if(typeof config == "string"){
37836         this.title = config;
37837     }else{
37838         Roo.apply(this, config);
37839     }
37840     
37841     if(this.resizeEl){
37842         this.resizeEl = Roo.get(this.resizeEl, true);
37843     }else{
37844         this.resizeEl = this.el;
37845     }
37846     // handle view.xtype
37847     
37848  
37849     
37850     
37851     this.addEvents({
37852         /**
37853          * @event activate
37854          * Fires when this panel is activated. 
37855          * @param {Roo.ContentPanel} this
37856          */
37857         "activate" : true,
37858         /**
37859          * @event deactivate
37860          * Fires when this panel is activated. 
37861          * @param {Roo.ContentPanel} this
37862          */
37863         "deactivate" : true,
37864
37865         /**
37866          * @event resize
37867          * Fires when this panel is resized if fitToFrame is true.
37868          * @param {Roo.ContentPanel} this
37869          * @param {Number} width The width after any component adjustments
37870          * @param {Number} height The height after any component adjustments
37871          */
37872         "resize" : true,
37873         
37874          /**
37875          * @event render
37876          * Fires when this tab is created
37877          * @param {Roo.ContentPanel} this
37878          */
37879         "render" : true
37880         
37881         
37882         
37883     });
37884     
37885
37886     
37887     
37888     if(this.autoScroll){
37889         this.resizeEl.setStyle("overflow", "auto");
37890     } else {
37891         // fix randome scrolling
37892         //this.el.on('scroll', function() {
37893         //    Roo.log('fix random scolling');
37894         //    this.scrollTo('top',0); 
37895         //});
37896     }
37897     content = content || this.content;
37898     if(content){
37899         this.setContent(content);
37900     }
37901     if(config && config.url){
37902         this.setUrl(this.url, this.params, this.loadOnce);
37903     }
37904     
37905     
37906     
37907     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37908     
37909     if (this.view && typeof(this.view.xtype) != 'undefined') {
37910         this.view.el = this.el.appendChild(document.createElement("div"));
37911         this.view = Roo.factory(this.view); 
37912         this.view.render  &&  this.view.render(false, '');  
37913     }
37914     
37915     
37916     this.fireEvent('render', this);
37917 };
37918
37919 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37920     
37921     tabTip : '',
37922     
37923     setRegion : function(region){
37924         this.region = region;
37925         this.setActiveClass(region && !this.background);
37926     },
37927     
37928     
37929     setActiveClass: function(state)
37930     {
37931         if(state){
37932            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37933            this.el.setStyle('position','relative');
37934         }else{
37935            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37936            this.el.setStyle('position', 'absolute');
37937         } 
37938     },
37939     
37940     /**
37941      * Returns the toolbar for this Panel if one was configured. 
37942      * @return {Roo.Toolbar} 
37943      */
37944     getToolbar : function(){
37945         return this.toolbar;
37946     },
37947     
37948     setActiveState : function(active)
37949     {
37950         this.active = active;
37951         this.setActiveClass(active);
37952         if(!active){
37953             if(this.fireEvent("deactivate", this) === false){
37954                 return false;
37955             }
37956             return true;
37957         }
37958         this.fireEvent("activate", this);
37959         return true;
37960     },
37961     /**
37962      * Updates this panel's element
37963      * @param {String} content The new content
37964      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37965     */
37966     setContent : function(content, loadScripts){
37967         this.el.update(content, loadScripts);
37968     },
37969
37970     ignoreResize : function(w, h){
37971         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37972             return true;
37973         }else{
37974             this.lastSize = {width: w, height: h};
37975             return false;
37976         }
37977     },
37978     /**
37979      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37980      * @return {Roo.UpdateManager} The UpdateManager
37981      */
37982     getUpdateManager : function(){
37983         return this.el.getUpdateManager();
37984     },
37985      /**
37986      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37987      * @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:
37988 <pre><code>
37989 panel.load({
37990     url: "your-url.php",
37991     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37992     callback: yourFunction,
37993     scope: yourObject, //(optional scope)
37994     discardUrl: false,
37995     nocache: false,
37996     text: "Loading...",
37997     timeout: 30,
37998     scripts: false
37999 });
38000 </code></pre>
38001      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38002      * 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.
38003      * @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}
38004      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38005      * @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.
38006      * @return {Roo.ContentPanel} this
38007      */
38008     load : function(){
38009         var um = this.el.getUpdateManager();
38010         um.update.apply(um, arguments);
38011         return this;
38012     },
38013
38014
38015     /**
38016      * 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.
38017      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38018      * @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)
38019      * @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)
38020      * @return {Roo.UpdateManager} The UpdateManager
38021      */
38022     setUrl : function(url, params, loadOnce){
38023         if(this.refreshDelegate){
38024             this.removeListener("activate", this.refreshDelegate);
38025         }
38026         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38027         this.on("activate", this.refreshDelegate);
38028         return this.el.getUpdateManager();
38029     },
38030     
38031     _handleRefresh : function(url, params, loadOnce){
38032         if(!loadOnce || !this.loaded){
38033             var updater = this.el.getUpdateManager();
38034             updater.update(url, params, this._setLoaded.createDelegate(this));
38035         }
38036     },
38037     
38038     _setLoaded : function(){
38039         this.loaded = true;
38040     }, 
38041     
38042     /**
38043      * Returns this panel's id
38044      * @return {String} 
38045      */
38046     getId : function(){
38047         return this.el.id;
38048     },
38049     
38050     /** 
38051      * Returns this panel's element - used by regiosn to add.
38052      * @return {Roo.Element} 
38053      */
38054     getEl : function(){
38055         return this.wrapEl || this.el;
38056     },
38057     
38058    
38059     
38060     adjustForComponents : function(width, height)
38061     {
38062         //Roo.log('adjustForComponents ');
38063         if(this.resizeEl != this.el){
38064             width -= this.el.getFrameWidth('lr');
38065             height -= this.el.getFrameWidth('tb');
38066         }
38067         if(this.toolbar){
38068             var te = this.toolbar.getEl();
38069             te.setWidth(width);
38070             height -= te.getHeight();
38071         }
38072         if(this.footer){
38073             var te = this.footer.getEl();
38074             te.setWidth(width);
38075             height -= te.getHeight();
38076         }
38077         
38078         
38079         if(this.adjustments){
38080             width += this.adjustments[0];
38081             height += this.adjustments[1];
38082         }
38083         return {"width": width, "height": height};
38084     },
38085     
38086     setSize : function(width, height){
38087         if(this.fitToFrame && !this.ignoreResize(width, height)){
38088             if(this.fitContainer && this.resizeEl != this.el){
38089                 this.el.setSize(width, height);
38090             }
38091             var size = this.adjustForComponents(width, height);
38092             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38093             this.fireEvent('resize', this, size.width, size.height);
38094         }
38095     },
38096     
38097     /**
38098      * Returns this panel's title
38099      * @return {String} 
38100      */
38101     getTitle : function(){
38102         
38103         if (typeof(this.title) != 'object') {
38104             return this.title;
38105         }
38106         
38107         var t = '';
38108         for (var k in this.title) {
38109             if (!this.title.hasOwnProperty(k)) {
38110                 continue;
38111             }
38112             
38113             if (k.indexOf('-') >= 0) {
38114                 var s = k.split('-');
38115                 for (var i = 0; i<s.length; i++) {
38116                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38117                 }
38118             } else {
38119                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38120             }
38121         }
38122         return t;
38123     },
38124     
38125     /**
38126      * Set this panel's title
38127      * @param {String} title
38128      */
38129     setTitle : function(title){
38130         this.title = title;
38131         if(this.region){
38132             this.region.updatePanelTitle(this, title);
38133         }
38134     },
38135     
38136     /**
38137      * Returns true is this panel was configured to be closable
38138      * @return {Boolean} 
38139      */
38140     isClosable : function(){
38141         return this.closable;
38142     },
38143     
38144     beforeSlide : function(){
38145         this.el.clip();
38146         this.resizeEl.clip();
38147     },
38148     
38149     afterSlide : function(){
38150         this.el.unclip();
38151         this.resizeEl.unclip();
38152     },
38153     
38154     /**
38155      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38156      *   Will fail silently if the {@link #setUrl} method has not been called.
38157      *   This does not activate the panel, just updates its content.
38158      */
38159     refresh : function(){
38160         if(this.refreshDelegate){
38161            this.loaded = false;
38162            this.refreshDelegate();
38163         }
38164     },
38165     
38166     /**
38167      * Destroys this panel
38168      */
38169     destroy : function(){
38170         this.el.removeAllListeners();
38171         var tempEl = document.createElement("span");
38172         tempEl.appendChild(this.el.dom);
38173         tempEl.innerHTML = "";
38174         this.el.remove();
38175         this.el = null;
38176     },
38177     
38178     /**
38179      * form - if the content panel contains a form - this is a reference to it.
38180      * @type {Roo.form.Form}
38181      */
38182     form : false,
38183     /**
38184      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38185      *    This contains a reference to it.
38186      * @type {Roo.View}
38187      */
38188     view : false,
38189     
38190       /**
38191      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38192      * <pre><code>
38193
38194 layout.addxtype({
38195        xtype : 'Form',
38196        items: [ .... ]
38197    }
38198 );
38199
38200 </code></pre>
38201      * @param {Object} cfg Xtype definition of item to add.
38202      */
38203     
38204     
38205     getChildContainer: function () {
38206         return this.getEl();
38207     }
38208     
38209     
38210     /*
38211         var  ret = new Roo.factory(cfg);
38212         return ret;
38213         
38214         
38215         // add form..
38216         if (cfg.xtype.match(/^Form$/)) {
38217             
38218             var el;
38219             //if (this.footer) {
38220             //    el = this.footer.container.insertSibling(false, 'before');
38221             //} else {
38222                 el = this.el.createChild();
38223             //}
38224
38225             this.form = new  Roo.form.Form(cfg);
38226             
38227             
38228             if ( this.form.allItems.length) {
38229                 this.form.render(el.dom);
38230             }
38231             return this.form;
38232         }
38233         // should only have one of theses..
38234         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38235             // views.. should not be just added - used named prop 'view''
38236             
38237             cfg.el = this.el.appendChild(document.createElement("div"));
38238             // factory?
38239             
38240             var ret = new Roo.factory(cfg);
38241              
38242              ret.render && ret.render(false, ''); // render blank..
38243             this.view = ret;
38244             return ret;
38245         }
38246         return false;
38247     }
38248     \*/
38249 });
38250  
38251 /**
38252  * @class Roo.bootstrap.panel.Grid
38253  * @extends Roo.bootstrap.panel.Content
38254  * @constructor
38255  * Create a new GridPanel.
38256  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38257  * @param {Object} config A the config object
38258   
38259  */
38260
38261
38262
38263 Roo.bootstrap.panel.Grid = function(config)
38264 {
38265     
38266       
38267     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38268         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38269
38270     config.el = this.wrapper;
38271     //this.el = this.wrapper;
38272     
38273       if (config.container) {
38274         // ctor'ed from a Border/panel.grid
38275         
38276         
38277         this.wrapper.setStyle("overflow", "hidden");
38278         this.wrapper.addClass('roo-grid-container');
38279
38280     }
38281     
38282     
38283     if(config.toolbar){
38284         var tool_el = this.wrapper.createChild();    
38285         this.toolbar = Roo.factory(config.toolbar);
38286         var ti = [];
38287         if (config.toolbar.items) {
38288             ti = config.toolbar.items ;
38289             delete config.toolbar.items ;
38290         }
38291         
38292         var nitems = [];
38293         this.toolbar.render(tool_el);
38294         for(var i =0;i < ti.length;i++) {
38295           //  Roo.log(['add child', items[i]]);
38296             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38297         }
38298         this.toolbar.items = nitems;
38299         
38300         delete config.toolbar;
38301     }
38302     
38303     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38304     config.grid.scrollBody = true;;
38305     config.grid.monitorWindowResize = false; // turn off autosizing
38306     config.grid.autoHeight = false;
38307     config.grid.autoWidth = false;
38308     
38309     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38310     
38311     if (config.background) {
38312         // render grid on panel activation (if panel background)
38313         this.on('activate', function(gp) {
38314             if (!gp.grid.rendered) {
38315                 gp.grid.render(this.wrapper);
38316                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38317             }
38318         });
38319             
38320     } else {
38321         this.grid.render(this.wrapper);
38322         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38323
38324     }
38325     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38326     // ??? needed ??? config.el = this.wrapper;
38327     
38328     
38329     
38330   
38331     // xtype created footer. - not sure if will work as we normally have to render first..
38332     if (this.footer && !this.footer.el && this.footer.xtype) {
38333         
38334         var ctr = this.grid.getView().getFooterPanel(true);
38335         this.footer.dataSource = this.grid.dataSource;
38336         this.footer = Roo.factory(this.footer, Roo);
38337         this.footer.render(ctr);
38338         
38339     }
38340     
38341     
38342     
38343     
38344      
38345 };
38346
38347 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38348     getId : function(){
38349         return this.grid.id;
38350     },
38351     
38352     /**
38353      * Returns the grid for this panel
38354      * @return {Roo.bootstrap.Table} 
38355      */
38356     getGrid : function(){
38357         return this.grid;    
38358     },
38359     
38360     setSize : function(width, height){
38361         if(!this.ignoreResize(width, height)){
38362             var grid = this.grid;
38363             var size = this.adjustForComponents(width, height);
38364             var gridel = grid.getGridEl();
38365             gridel.setSize(size.width, size.height);
38366             /*
38367             var thd = grid.getGridEl().select('thead',true).first();
38368             var tbd = grid.getGridEl().select('tbody', true).first();
38369             if (tbd) {
38370                 tbd.setSize(width, height - thd.getHeight());
38371             }
38372             */
38373             grid.autoSize();
38374         }
38375     },
38376      
38377     
38378     
38379     beforeSlide : function(){
38380         this.grid.getView().scroller.clip();
38381     },
38382     
38383     afterSlide : function(){
38384         this.grid.getView().scroller.unclip();
38385     },
38386     
38387     destroy : function(){
38388         this.grid.destroy();
38389         delete this.grid;
38390         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38391     }
38392 });
38393
38394 /**
38395  * @class Roo.bootstrap.panel.Nest
38396  * @extends Roo.bootstrap.panel.Content
38397  * @constructor
38398  * Create a new Panel, that can contain a layout.Border.
38399  * 
38400  * 
38401  * @param {Roo.BorderLayout} layout The layout for this panel
38402  * @param {String/Object} config A string to set only the title or a config object
38403  */
38404 Roo.bootstrap.panel.Nest = function(config)
38405 {
38406     // construct with only one argument..
38407     /* FIXME - implement nicer consturctors
38408     if (layout.layout) {
38409         config = layout;
38410         layout = config.layout;
38411         delete config.layout;
38412     }
38413     if (layout.xtype && !layout.getEl) {
38414         // then layout needs constructing..
38415         layout = Roo.factory(layout, Roo);
38416     }
38417     */
38418     
38419     config.el =  config.layout.getEl();
38420     
38421     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38422     
38423     config.layout.monitorWindowResize = false; // turn off autosizing
38424     this.layout = config.layout;
38425     this.layout.getEl().addClass("roo-layout-nested-layout");
38426     this.layout.parent = this;
38427     
38428     
38429     
38430     
38431 };
38432
38433 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38434
38435     setSize : function(width, height){
38436         if(!this.ignoreResize(width, height)){
38437             var size = this.adjustForComponents(width, height);
38438             var el = this.layout.getEl();
38439             if (size.height < 1) {
38440                 el.setWidth(size.width);   
38441             } else {
38442                 el.setSize(size.width, size.height);
38443             }
38444             var touch = el.dom.offsetWidth;
38445             this.layout.layout();
38446             // ie requires a double layout on the first pass
38447             if(Roo.isIE && !this.initialized){
38448                 this.initialized = true;
38449                 this.layout.layout();
38450             }
38451         }
38452     },
38453     
38454     // activate all subpanels if not currently active..
38455     
38456     setActiveState : function(active){
38457         this.active = active;
38458         this.setActiveClass(active);
38459         
38460         if(!active){
38461             this.fireEvent("deactivate", this);
38462             return;
38463         }
38464         
38465         this.fireEvent("activate", this);
38466         // not sure if this should happen before or after..
38467         if (!this.layout) {
38468             return; // should not happen..
38469         }
38470         var reg = false;
38471         for (var r in this.layout.regions) {
38472             reg = this.layout.getRegion(r);
38473             if (reg.getActivePanel()) {
38474                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38475                 reg.setActivePanel(reg.getActivePanel());
38476                 continue;
38477             }
38478             if (!reg.panels.length) {
38479                 continue;
38480             }
38481             reg.showPanel(reg.getPanel(0));
38482         }
38483         
38484         
38485         
38486         
38487     },
38488     
38489     /**
38490      * Returns the nested BorderLayout for this panel
38491      * @return {Roo.BorderLayout} 
38492      */
38493     getLayout : function(){
38494         return this.layout;
38495     },
38496     
38497      /**
38498      * Adds a xtype elements to the layout of the nested panel
38499      * <pre><code>
38500
38501 panel.addxtype({
38502        xtype : 'ContentPanel',
38503        region: 'west',
38504        items: [ .... ]
38505    }
38506 );
38507
38508 panel.addxtype({
38509         xtype : 'NestedLayoutPanel',
38510         region: 'west',
38511         layout: {
38512            center: { },
38513            west: { }   
38514         },
38515         items : [ ... list of content panels or nested layout panels.. ]
38516    }
38517 );
38518 </code></pre>
38519      * @param {Object} cfg Xtype definition of item to add.
38520      */
38521     addxtype : function(cfg) {
38522         return this.layout.addxtype(cfg);
38523     
38524     }
38525 });/*
38526  * Based on:
38527  * Ext JS Library 1.1.1
38528  * Copyright(c) 2006-2007, Ext JS, LLC.
38529  *
38530  * Originally Released Under LGPL - original licence link has changed is not relivant.
38531  *
38532  * Fork - LGPL
38533  * <script type="text/javascript">
38534  */
38535 /**
38536  * @class Roo.TabPanel
38537  * @extends Roo.util.Observable
38538  * A lightweight tab container.
38539  * <br><br>
38540  * Usage:
38541  * <pre><code>
38542 // basic tabs 1, built from existing content
38543 var tabs = new Roo.TabPanel("tabs1");
38544 tabs.addTab("script", "View Script");
38545 tabs.addTab("markup", "View Markup");
38546 tabs.activate("script");
38547
38548 // more advanced tabs, built from javascript
38549 var jtabs = new Roo.TabPanel("jtabs");
38550 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38551
38552 // set up the UpdateManager
38553 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38554 var updater = tab2.getUpdateManager();
38555 updater.setDefaultUrl("ajax1.htm");
38556 tab2.on('activate', updater.refresh, updater, true);
38557
38558 // Use setUrl for Ajax loading
38559 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38560 tab3.setUrl("ajax2.htm", null, true);
38561
38562 // Disabled tab
38563 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38564 tab4.disable();
38565
38566 jtabs.activate("jtabs-1");
38567  * </code></pre>
38568  * @constructor
38569  * Create a new TabPanel.
38570  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38571  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38572  */
38573 Roo.bootstrap.panel.Tabs = function(config){
38574     /**
38575     * The container element for this TabPanel.
38576     * @type Roo.Element
38577     */
38578     this.el = Roo.get(config.el);
38579     delete config.el;
38580     if(config){
38581         if(typeof config == "boolean"){
38582             this.tabPosition = config ? "bottom" : "top";
38583         }else{
38584             Roo.apply(this, config);
38585         }
38586     }
38587     
38588     if(this.tabPosition == "bottom"){
38589         // if tabs are at the bottom = create the body first.
38590         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38591         this.el.addClass("roo-tabs-bottom");
38592     }
38593     // next create the tabs holders
38594     
38595     if (this.tabPosition == "west"){
38596         
38597         var reg = this.region; // fake it..
38598         while (reg) {
38599             if (!reg.mgr.parent) {
38600                 break;
38601             }
38602             reg = reg.mgr.parent.region;
38603         }
38604         Roo.log("got nest?");
38605         Roo.log(reg);
38606         if (reg.mgr.getRegion('west')) {
38607             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38608             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38609             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38610             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38611             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38612         
38613             
38614         }
38615         
38616         
38617     } else {
38618      
38619         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38620         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38621         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38622         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38623     }
38624     
38625     
38626     if(Roo.isIE){
38627         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38628     }
38629     
38630     // finally - if tabs are at the top, then create the body last..
38631     if(this.tabPosition != "bottom"){
38632         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38633          * @type Roo.Element
38634          */
38635         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38636         this.el.addClass("roo-tabs-top");
38637     }
38638     this.items = [];
38639
38640     this.bodyEl.setStyle("position", "relative");
38641
38642     this.active = null;
38643     this.activateDelegate = this.activate.createDelegate(this);
38644
38645     this.addEvents({
38646         /**
38647          * @event tabchange
38648          * Fires when the active tab changes
38649          * @param {Roo.TabPanel} this
38650          * @param {Roo.TabPanelItem} activePanel The new active tab
38651          */
38652         "tabchange": true,
38653         /**
38654          * @event beforetabchange
38655          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38656          * @param {Roo.TabPanel} this
38657          * @param {Object} e Set cancel to true on this object to cancel the tab change
38658          * @param {Roo.TabPanelItem} tab The tab being changed to
38659          */
38660         "beforetabchange" : true
38661     });
38662
38663     Roo.EventManager.onWindowResize(this.onResize, this);
38664     this.cpad = this.el.getPadding("lr");
38665     this.hiddenCount = 0;
38666
38667
38668     // toolbar on the tabbar support...
38669     if (this.toolbar) {
38670         alert("no toolbar support yet");
38671         this.toolbar  = false;
38672         /*
38673         var tcfg = this.toolbar;
38674         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38675         this.toolbar = new Roo.Toolbar(tcfg);
38676         if (Roo.isSafari) {
38677             var tbl = tcfg.container.child('table', true);
38678             tbl.setAttribute('width', '100%');
38679         }
38680         */
38681         
38682     }
38683    
38684
38685
38686     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38687 };
38688
38689 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38690     /*
38691      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38692      */
38693     tabPosition : "top",
38694     /*
38695      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38696      */
38697     currentTabWidth : 0,
38698     /*
38699      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38700      */
38701     minTabWidth : 40,
38702     /*
38703      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38704      */
38705     maxTabWidth : 250,
38706     /*
38707      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38708      */
38709     preferredTabWidth : 175,
38710     /*
38711      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38712      */
38713     resizeTabs : false,
38714     /*
38715      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38716      */
38717     monitorResize : true,
38718     /*
38719      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38720      */
38721     toolbar : false,  // set by caller..
38722     
38723     region : false, /// set by caller
38724     
38725     disableTooltips : true, // not used yet...
38726
38727     /**
38728      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38729      * @param {String} id The id of the div to use <b>or create</b>
38730      * @param {String} text The text for the tab
38731      * @param {String} content (optional) Content to put in the TabPanelItem body
38732      * @param {Boolean} closable (optional) True to create a close icon on the tab
38733      * @return {Roo.TabPanelItem} The created TabPanelItem
38734      */
38735     addTab : function(id, text, content, closable, tpl)
38736     {
38737         var item = new Roo.bootstrap.panel.TabItem({
38738             panel: this,
38739             id : id,
38740             text : text,
38741             closable : closable,
38742             tpl : tpl
38743         });
38744         this.addTabItem(item);
38745         if(content){
38746             item.setContent(content);
38747         }
38748         return item;
38749     },
38750
38751     /**
38752      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38753      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38754      * @return {Roo.TabPanelItem}
38755      */
38756     getTab : function(id){
38757         return this.items[id];
38758     },
38759
38760     /**
38761      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38762      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38763      */
38764     hideTab : function(id){
38765         var t = this.items[id];
38766         if(!t.isHidden()){
38767            t.setHidden(true);
38768            this.hiddenCount++;
38769            this.autoSizeTabs();
38770         }
38771     },
38772
38773     /**
38774      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38775      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38776      */
38777     unhideTab : function(id){
38778         var t = this.items[id];
38779         if(t.isHidden()){
38780            t.setHidden(false);
38781            this.hiddenCount--;
38782            this.autoSizeTabs();
38783         }
38784     },
38785
38786     /**
38787      * Adds an existing {@link Roo.TabPanelItem}.
38788      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38789      */
38790     addTabItem : function(item)
38791     {
38792         this.items[item.id] = item;
38793         this.items.push(item);
38794         this.autoSizeTabs();
38795       //  if(this.resizeTabs){
38796     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38797   //         this.autoSizeTabs();
38798 //        }else{
38799 //            item.autoSize();
38800        // }
38801     },
38802
38803     /**
38804      * Removes a {@link Roo.TabPanelItem}.
38805      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38806      */
38807     removeTab : function(id){
38808         var items = this.items;
38809         var tab = items[id];
38810         if(!tab) { return; }
38811         var index = items.indexOf(tab);
38812         if(this.active == tab && items.length > 1){
38813             var newTab = this.getNextAvailable(index);
38814             if(newTab) {
38815                 newTab.activate();
38816             }
38817         }
38818         this.stripEl.dom.removeChild(tab.pnode.dom);
38819         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38820             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38821         }
38822         items.splice(index, 1);
38823         delete this.items[tab.id];
38824         tab.fireEvent("close", tab);
38825         tab.purgeListeners();
38826         this.autoSizeTabs();
38827     },
38828
38829     getNextAvailable : function(start){
38830         var items = this.items;
38831         var index = start;
38832         // look for a next tab that will slide over to
38833         // replace the one being removed
38834         while(index < items.length){
38835             var item = items[++index];
38836             if(item && !item.isHidden()){
38837                 return item;
38838             }
38839         }
38840         // if one isn't found select the previous tab (on the left)
38841         index = start;
38842         while(index >= 0){
38843             var item = items[--index];
38844             if(item && !item.isHidden()){
38845                 return item;
38846             }
38847         }
38848         return null;
38849     },
38850
38851     /**
38852      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38853      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38854      */
38855     disableTab : function(id){
38856         var tab = this.items[id];
38857         if(tab && this.active != tab){
38858             tab.disable();
38859         }
38860     },
38861
38862     /**
38863      * Enables a {@link Roo.TabPanelItem} that is disabled.
38864      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38865      */
38866     enableTab : function(id){
38867         var tab = this.items[id];
38868         tab.enable();
38869     },
38870
38871     /**
38872      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38873      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38874      * @return {Roo.TabPanelItem} The TabPanelItem.
38875      */
38876     activate : function(id)
38877     {
38878         //Roo.log('activite:'  + id);
38879         
38880         var tab = this.items[id];
38881         if(!tab){
38882             return null;
38883         }
38884         if(tab == this.active || tab.disabled){
38885             return tab;
38886         }
38887         var e = {};
38888         this.fireEvent("beforetabchange", this, e, tab);
38889         if(e.cancel !== true && !tab.disabled){
38890             if(this.active){
38891                 this.active.hide();
38892             }
38893             this.active = this.items[id];
38894             this.active.show();
38895             this.fireEvent("tabchange", this, this.active);
38896         }
38897         return tab;
38898     },
38899
38900     /**
38901      * Gets the active {@link Roo.TabPanelItem}.
38902      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38903      */
38904     getActiveTab : function(){
38905         return this.active;
38906     },
38907
38908     /**
38909      * Updates the tab body element to fit the height of the container element
38910      * for overflow scrolling
38911      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38912      */
38913     syncHeight : function(targetHeight){
38914         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38915         var bm = this.bodyEl.getMargins();
38916         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38917         this.bodyEl.setHeight(newHeight);
38918         return newHeight;
38919     },
38920
38921     onResize : function(){
38922         if(this.monitorResize){
38923             this.autoSizeTabs();
38924         }
38925     },
38926
38927     /**
38928      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38929      */
38930     beginUpdate : function(){
38931         this.updating = true;
38932     },
38933
38934     /**
38935      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38936      */
38937     endUpdate : function(){
38938         this.updating = false;
38939         this.autoSizeTabs();
38940     },
38941
38942     /**
38943      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38944      */
38945     autoSizeTabs : function()
38946     {
38947         var count = this.items.length;
38948         var vcount = count - this.hiddenCount;
38949         
38950         if (vcount < 2) {
38951             this.stripEl.hide();
38952         } else {
38953             this.stripEl.show();
38954         }
38955         
38956         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38957             return;
38958         }
38959         
38960         
38961         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38962         var availWidth = Math.floor(w / vcount);
38963         var b = this.stripBody;
38964         if(b.getWidth() > w){
38965             var tabs = this.items;
38966             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38967             if(availWidth < this.minTabWidth){
38968                 /*if(!this.sleft){    // incomplete scrolling code
38969                     this.createScrollButtons();
38970                 }
38971                 this.showScroll();
38972                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38973             }
38974         }else{
38975             if(this.currentTabWidth < this.preferredTabWidth){
38976                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38977             }
38978         }
38979     },
38980
38981     /**
38982      * Returns the number of tabs in this TabPanel.
38983      * @return {Number}
38984      */
38985      getCount : function(){
38986          return this.items.length;
38987      },
38988
38989     /**
38990      * Resizes all the tabs to the passed width
38991      * @param {Number} The new width
38992      */
38993     setTabWidth : function(width){
38994         this.currentTabWidth = width;
38995         for(var i = 0, len = this.items.length; i < len; i++) {
38996                 if(!this.items[i].isHidden()) {
38997                 this.items[i].setWidth(width);
38998             }
38999         }
39000     },
39001
39002     /**
39003      * Destroys this TabPanel
39004      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39005      */
39006     destroy : function(removeEl){
39007         Roo.EventManager.removeResizeListener(this.onResize, this);
39008         for(var i = 0, len = this.items.length; i < len; i++){
39009             this.items[i].purgeListeners();
39010         }
39011         if(removeEl === true){
39012             this.el.update("");
39013             this.el.remove();
39014         }
39015     },
39016     
39017     createStrip : function(container)
39018     {
39019         var strip = document.createElement("nav");
39020         strip.className = Roo.bootstrap.version == 4 ?
39021             "navbar-light bg-light" : 
39022             "navbar navbar-default"; //"x-tabs-wrap";
39023         container.appendChild(strip);
39024         return strip;
39025     },
39026     
39027     createStripList : function(strip)
39028     {
39029         // div wrapper for retard IE
39030         // returns the "tr" element.
39031         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39032         //'<div class="x-tabs-strip-wrap">'+
39033           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39034           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39035         return strip.firstChild; //.firstChild.firstChild.firstChild;
39036     },
39037     createBody : function(container)
39038     {
39039         var body = document.createElement("div");
39040         Roo.id(body, "tab-body");
39041         //Roo.fly(body).addClass("x-tabs-body");
39042         Roo.fly(body).addClass("tab-content");
39043         container.appendChild(body);
39044         return body;
39045     },
39046     createItemBody :function(bodyEl, id){
39047         var body = Roo.getDom(id);
39048         if(!body){
39049             body = document.createElement("div");
39050             body.id = id;
39051         }
39052         //Roo.fly(body).addClass("x-tabs-item-body");
39053         Roo.fly(body).addClass("tab-pane");
39054          bodyEl.insertBefore(body, bodyEl.firstChild);
39055         return body;
39056     },
39057     /** @private */
39058     createStripElements :  function(stripEl, text, closable, tpl)
39059     {
39060         var td = document.createElement("li"); // was td..
39061         td.className = 'nav-item';
39062         
39063         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39064         
39065         
39066         stripEl.appendChild(td);
39067         /*if(closable){
39068             td.className = "x-tabs-closable";
39069             if(!this.closeTpl){
39070                 this.closeTpl = new Roo.Template(
39071                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39072                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39073                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39074                 );
39075             }
39076             var el = this.closeTpl.overwrite(td, {"text": text});
39077             var close = el.getElementsByTagName("div")[0];
39078             var inner = el.getElementsByTagName("em")[0];
39079             return {"el": el, "close": close, "inner": inner};
39080         } else {
39081         */
39082         // not sure what this is..
39083 //            if(!this.tabTpl){
39084                 //this.tabTpl = new Roo.Template(
39085                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39086                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39087                 //);
39088 //                this.tabTpl = new Roo.Template(
39089 //                   '<a href="#">' +
39090 //                   '<span unselectable="on"' +
39091 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39092 //                            ' >{text}</span></a>'
39093 //                );
39094 //                
39095 //            }
39096
39097
39098             var template = tpl || this.tabTpl || false;
39099             
39100             if(!template){
39101                 template =  new Roo.Template(
39102                         Roo.bootstrap.version == 4 ? 
39103                             (
39104                                 '<a class="nav-link" href="#" unselectable="on"' +
39105                                      (this.disableTooltips ? '' : ' title="{text}"') +
39106                                      ' >{text}</a>'
39107                             ) : (
39108                                 '<a class="nav-link" href="#">' +
39109                                 '<span unselectable="on"' +
39110                                          (this.disableTooltips ? '' : ' title="{text}"') +
39111                                     ' >{text}</span></a>'
39112                             )
39113                 );
39114             }
39115             
39116             switch (typeof(template)) {
39117                 case 'object' :
39118                     break;
39119                 case 'string' :
39120                     template = new Roo.Template(template);
39121                     break;
39122                 default :
39123                     break;
39124             }
39125             
39126             var el = template.overwrite(td, {"text": text});
39127             
39128             var inner = el.getElementsByTagName("span")[0];
39129             
39130             return {"el": el, "inner": inner};
39131             
39132     }
39133         
39134     
39135 });
39136
39137 /**
39138  * @class Roo.TabPanelItem
39139  * @extends Roo.util.Observable
39140  * Represents an individual item (tab plus body) in a TabPanel.
39141  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39142  * @param {String} id The id of this TabPanelItem
39143  * @param {String} text The text for the tab of this TabPanelItem
39144  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39145  */
39146 Roo.bootstrap.panel.TabItem = function(config){
39147     /**
39148      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39149      * @type Roo.TabPanel
39150      */
39151     this.tabPanel = config.panel;
39152     /**
39153      * The id for this TabPanelItem
39154      * @type String
39155      */
39156     this.id = config.id;
39157     /** @private */
39158     this.disabled = false;
39159     /** @private */
39160     this.text = config.text;
39161     /** @private */
39162     this.loaded = false;
39163     this.closable = config.closable;
39164
39165     /**
39166      * The body element for this TabPanelItem.
39167      * @type Roo.Element
39168      */
39169     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39170     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39171     this.bodyEl.setStyle("display", "block");
39172     this.bodyEl.setStyle("zoom", "1");
39173     //this.hideAction();
39174
39175     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39176     /** @private */
39177     this.el = Roo.get(els.el);
39178     this.inner = Roo.get(els.inner, true);
39179      this.textEl = Roo.bootstrap.version == 4 ?
39180         this.el : Roo.get(this.el.dom.firstChild, true);
39181
39182     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39183     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39184
39185     
39186 //    this.el.on("mousedown", this.onTabMouseDown, this);
39187     this.el.on("click", this.onTabClick, this);
39188     /** @private */
39189     if(config.closable){
39190         var c = Roo.get(els.close, true);
39191         c.dom.title = this.closeText;
39192         c.addClassOnOver("close-over");
39193         c.on("click", this.closeClick, this);
39194      }
39195
39196     this.addEvents({
39197          /**
39198          * @event activate
39199          * Fires when this tab becomes the active tab.
39200          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39201          * @param {Roo.TabPanelItem} this
39202          */
39203         "activate": true,
39204         /**
39205          * @event beforeclose
39206          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39207          * @param {Roo.TabPanelItem} this
39208          * @param {Object} e Set cancel to true on this object to cancel the close.
39209          */
39210         "beforeclose": true,
39211         /**
39212          * @event close
39213          * Fires when this tab is closed.
39214          * @param {Roo.TabPanelItem} this
39215          */
39216          "close": true,
39217         /**
39218          * @event deactivate
39219          * Fires when this tab is no longer the active tab.
39220          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39221          * @param {Roo.TabPanelItem} this
39222          */
39223          "deactivate" : true
39224     });
39225     this.hidden = false;
39226
39227     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39228 };
39229
39230 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39231            {
39232     purgeListeners : function(){
39233        Roo.util.Observable.prototype.purgeListeners.call(this);
39234        this.el.removeAllListeners();
39235     },
39236     /**
39237      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39238      */
39239     show : function(){
39240         this.status_node.addClass("active");
39241         this.showAction();
39242         if(Roo.isOpera){
39243             this.tabPanel.stripWrap.repaint();
39244         }
39245         this.fireEvent("activate", this.tabPanel, this);
39246     },
39247
39248     /**
39249      * Returns true if this tab is the active tab.
39250      * @return {Boolean}
39251      */
39252     isActive : function(){
39253         return this.tabPanel.getActiveTab() == this;
39254     },
39255
39256     /**
39257      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39258      */
39259     hide : function(){
39260         this.status_node.removeClass("active");
39261         this.hideAction();
39262         this.fireEvent("deactivate", this.tabPanel, this);
39263     },
39264
39265     hideAction : function(){
39266         this.bodyEl.hide();
39267         this.bodyEl.setStyle("position", "absolute");
39268         this.bodyEl.setLeft("-20000px");
39269         this.bodyEl.setTop("-20000px");
39270     },
39271
39272     showAction : function(){
39273         this.bodyEl.setStyle("position", "relative");
39274         this.bodyEl.setTop("");
39275         this.bodyEl.setLeft("");
39276         this.bodyEl.show();
39277     },
39278
39279     /**
39280      * Set the tooltip for the tab.
39281      * @param {String} tooltip The tab's tooltip
39282      */
39283     setTooltip : function(text){
39284         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39285             this.textEl.dom.qtip = text;
39286             this.textEl.dom.removeAttribute('title');
39287         }else{
39288             this.textEl.dom.title = text;
39289         }
39290     },
39291
39292     onTabClick : function(e){
39293         e.preventDefault();
39294         this.tabPanel.activate(this.id);
39295     },
39296
39297     onTabMouseDown : function(e){
39298         e.preventDefault();
39299         this.tabPanel.activate(this.id);
39300     },
39301 /*
39302     getWidth : function(){
39303         return this.inner.getWidth();
39304     },
39305
39306     setWidth : function(width){
39307         var iwidth = width - this.linode.getPadding("lr");
39308         this.inner.setWidth(iwidth);
39309         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39310         this.linode.setWidth(width);
39311     },
39312 */
39313     /**
39314      * Show or hide the tab
39315      * @param {Boolean} hidden True to hide or false to show.
39316      */
39317     setHidden : function(hidden){
39318         this.hidden = hidden;
39319         this.linode.setStyle("display", hidden ? "none" : "");
39320     },
39321
39322     /**
39323      * Returns true if this tab is "hidden"
39324      * @return {Boolean}
39325      */
39326     isHidden : function(){
39327         return this.hidden;
39328     },
39329
39330     /**
39331      * Returns the text for this tab
39332      * @return {String}
39333      */
39334     getText : function(){
39335         return this.text;
39336     },
39337     /*
39338     autoSize : function(){
39339         //this.el.beginMeasure();
39340         this.textEl.setWidth(1);
39341         /*
39342          *  #2804 [new] Tabs in Roojs
39343          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39344          */
39345         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39346         //this.el.endMeasure();
39347     //},
39348
39349     /**
39350      * Sets the text for the tab (Note: this also sets the tooltip text)
39351      * @param {String} text The tab's text and tooltip
39352      */
39353     setText : function(text){
39354         this.text = text;
39355         this.textEl.update(text);
39356         this.setTooltip(text);
39357         //if(!this.tabPanel.resizeTabs){
39358         //    this.autoSize();
39359         //}
39360     },
39361     /**
39362      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39363      */
39364     activate : function(){
39365         this.tabPanel.activate(this.id);
39366     },
39367
39368     /**
39369      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39370      */
39371     disable : function(){
39372         if(this.tabPanel.active != this){
39373             this.disabled = true;
39374             this.status_node.addClass("disabled");
39375         }
39376     },
39377
39378     /**
39379      * Enables this TabPanelItem if it was previously disabled.
39380      */
39381     enable : function(){
39382         this.disabled = false;
39383         this.status_node.removeClass("disabled");
39384     },
39385
39386     /**
39387      * Sets the content for this TabPanelItem.
39388      * @param {String} content The content
39389      * @param {Boolean} loadScripts true to look for and load scripts
39390      */
39391     setContent : function(content, loadScripts){
39392         this.bodyEl.update(content, loadScripts);
39393     },
39394
39395     /**
39396      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39397      * @return {Roo.UpdateManager} The UpdateManager
39398      */
39399     getUpdateManager : function(){
39400         return this.bodyEl.getUpdateManager();
39401     },
39402
39403     /**
39404      * Set a URL to be used to load the content for this TabPanelItem.
39405      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39406      * @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)
39407      * @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)
39408      * @return {Roo.UpdateManager} The UpdateManager
39409      */
39410     setUrl : function(url, params, loadOnce){
39411         if(this.refreshDelegate){
39412             this.un('activate', this.refreshDelegate);
39413         }
39414         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39415         this.on("activate", this.refreshDelegate);
39416         return this.bodyEl.getUpdateManager();
39417     },
39418
39419     /** @private */
39420     _handleRefresh : function(url, params, loadOnce){
39421         if(!loadOnce || !this.loaded){
39422             var updater = this.bodyEl.getUpdateManager();
39423             updater.update(url, params, this._setLoaded.createDelegate(this));
39424         }
39425     },
39426
39427     /**
39428      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39429      *   Will fail silently if the setUrl method has not been called.
39430      *   This does not activate the panel, just updates its content.
39431      */
39432     refresh : function(){
39433         if(this.refreshDelegate){
39434            this.loaded = false;
39435            this.refreshDelegate();
39436         }
39437     },
39438
39439     /** @private */
39440     _setLoaded : function(){
39441         this.loaded = true;
39442     },
39443
39444     /** @private */
39445     closeClick : function(e){
39446         var o = {};
39447         e.stopEvent();
39448         this.fireEvent("beforeclose", this, o);
39449         if(o.cancel !== true){
39450             this.tabPanel.removeTab(this.id);
39451         }
39452     },
39453     /**
39454      * The text displayed in the tooltip for the close icon.
39455      * @type String
39456      */
39457     closeText : "Close this tab"
39458 });
39459 /**
39460 *    This script refer to:
39461 *    Title: International Telephone Input
39462 *    Author: Jack O'Connor
39463 *    Code version:  v12.1.12
39464 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39465 **/
39466
39467 Roo.bootstrap.PhoneInputData = function() {
39468     var d = [
39469       [
39470         "Afghanistan (‫افغانستان‬‎)",
39471         "af",
39472         "93"
39473       ],
39474       [
39475         "Albania (Shqipëri)",
39476         "al",
39477         "355"
39478       ],
39479       [
39480         "Algeria (‫الجزائر‬‎)",
39481         "dz",
39482         "213"
39483       ],
39484       [
39485         "American Samoa",
39486         "as",
39487         "1684"
39488       ],
39489       [
39490         "Andorra",
39491         "ad",
39492         "376"
39493       ],
39494       [
39495         "Angola",
39496         "ao",
39497         "244"
39498       ],
39499       [
39500         "Anguilla",
39501         "ai",
39502         "1264"
39503       ],
39504       [
39505         "Antigua and Barbuda",
39506         "ag",
39507         "1268"
39508       ],
39509       [
39510         "Argentina",
39511         "ar",
39512         "54"
39513       ],
39514       [
39515         "Armenia (Հայաստան)",
39516         "am",
39517         "374"
39518       ],
39519       [
39520         "Aruba",
39521         "aw",
39522         "297"
39523       ],
39524       [
39525         "Australia",
39526         "au",
39527         "61",
39528         0
39529       ],
39530       [
39531         "Austria (Österreich)",
39532         "at",
39533         "43"
39534       ],
39535       [
39536         "Azerbaijan (Azərbaycan)",
39537         "az",
39538         "994"
39539       ],
39540       [
39541         "Bahamas",
39542         "bs",
39543         "1242"
39544       ],
39545       [
39546         "Bahrain (‫البحرين‬‎)",
39547         "bh",
39548         "973"
39549       ],
39550       [
39551         "Bangladesh (বাংলাদেশ)",
39552         "bd",
39553         "880"
39554       ],
39555       [
39556         "Barbados",
39557         "bb",
39558         "1246"
39559       ],
39560       [
39561         "Belarus (Беларусь)",
39562         "by",
39563         "375"
39564       ],
39565       [
39566         "Belgium (België)",
39567         "be",
39568         "32"
39569       ],
39570       [
39571         "Belize",
39572         "bz",
39573         "501"
39574       ],
39575       [
39576         "Benin (Bénin)",
39577         "bj",
39578         "229"
39579       ],
39580       [
39581         "Bermuda",
39582         "bm",
39583         "1441"
39584       ],
39585       [
39586         "Bhutan (འབྲུག)",
39587         "bt",
39588         "975"
39589       ],
39590       [
39591         "Bolivia",
39592         "bo",
39593         "591"
39594       ],
39595       [
39596         "Bosnia and Herzegovina (Босна и Херцеговина)",
39597         "ba",
39598         "387"
39599       ],
39600       [
39601         "Botswana",
39602         "bw",
39603         "267"
39604       ],
39605       [
39606         "Brazil (Brasil)",
39607         "br",
39608         "55"
39609       ],
39610       [
39611         "British Indian Ocean Territory",
39612         "io",
39613         "246"
39614       ],
39615       [
39616         "British Virgin Islands",
39617         "vg",
39618         "1284"
39619       ],
39620       [
39621         "Brunei",
39622         "bn",
39623         "673"
39624       ],
39625       [
39626         "Bulgaria (България)",
39627         "bg",
39628         "359"
39629       ],
39630       [
39631         "Burkina Faso",
39632         "bf",
39633         "226"
39634       ],
39635       [
39636         "Burundi (Uburundi)",
39637         "bi",
39638         "257"
39639       ],
39640       [
39641         "Cambodia (កម្ពុជា)",
39642         "kh",
39643         "855"
39644       ],
39645       [
39646         "Cameroon (Cameroun)",
39647         "cm",
39648         "237"
39649       ],
39650       [
39651         "Canada",
39652         "ca",
39653         "1",
39654         1,
39655         ["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"]
39656       ],
39657       [
39658         "Cape Verde (Kabu Verdi)",
39659         "cv",
39660         "238"
39661       ],
39662       [
39663         "Caribbean Netherlands",
39664         "bq",
39665         "599",
39666         1
39667       ],
39668       [
39669         "Cayman Islands",
39670         "ky",
39671         "1345"
39672       ],
39673       [
39674         "Central African Republic (République centrafricaine)",
39675         "cf",
39676         "236"
39677       ],
39678       [
39679         "Chad (Tchad)",
39680         "td",
39681         "235"
39682       ],
39683       [
39684         "Chile",
39685         "cl",
39686         "56"
39687       ],
39688       [
39689         "China (中国)",
39690         "cn",
39691         "86"
39692       ],
39693       [
39694         "Christmas Island",
39695         "cx",
39696         "61",
39697         2
39698       ],
39699       [
39700         "Cocos (Keeling) Islands",
39701         "cc",
39702         "61",
39703         1
39704       ],
39705       [
39706         "Colombia",
39707         "co",
39708         "57"
39709       ],
39710       [
39711         "Comoros (‫جزر القمر‬‎)",
39712         "km",
39713         "269"
39714       ],
39715       [
39716         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39717         "cd",
39718         "243"
39719       ],
39720       [
39721         "Congo (Republic) (Congo-Brazzaville)",
39722         "cg",
39723         "242"
39724       ],
39725       [
39726         "Cook Islands",
39727         "ck",
39728         "682"
39729       ],
39730       [
39731         "Costa Rica",
39732         "cr",
39733         "506"
39734       ],
39735       [
39736         "Côte d’Ivoire",
39737         "ci",
39738         "225"
39739       ],
39740       [
39741         "Croatia (Hrvatska)",
39742         "hr",
39743         "385"
39744       ],
39745       [
39746         "Cuba",
39747         "cu",
39748         "53"
39749       ],
39750       [
39751         "Curaçao",
39752         "cw",
39753         "599",
39754         0
39755       ],
39756       [
39757         "Cyprus (Κύπρος)",
39758         "cy",
39759         "357"
39760       ],
39761       [
39762         "Czech Republic (Česká republika)",
39763         "cz",
39764         "420"
39765       ],
39766       [
39767         "Denmark (Danmark)",
39768         "dk",
39769         "45"
39770       ],
39771       [
39772         "Djibouti",
39773         "dj",
39774         "253"
39775       ],
39776       [
39777         "Dominica",
39778         "dm",
39779         "1767"
39780       ],
39781       [
39782         "Dominican Republic (República Dominicana)",
39783         "do",
39784         "1",
39785         2,
39786         ["809", "829", "849"]
39787       ],
39788       [
39789         "Ecuador",
39790         "ec",
39791         "593"
39792       ],
39793       [
39794         "Egypt (‫مصر‬‎)",
39795         "eg",
39796         "20"
39797       ],
39798       [
39799         "El Salvador",
39800         "sv",
39801         "503"
39802       ],
39803       [
39804         "Equatorial Guinea (Guinea Ecuatorial)",
39805         "gq",
39806         "240"
39807       ],
39808       [
39809         "Eritrea",
39810         "er",
39811         "291"
39812       ],
39813       [
39814         "Estonia (Eesti)",
39815         "ee",
39816         "372"
39817       ],
39818       [
39819         "Ethiopia",
39820         "et",
39821         "251"
39822       ],
39823       [
39824         "Falkland Islands (Islas Malvinas)",
39825         "fk",
39826         "500"
39827       ],
39828       [
39829         "Faroe Islands (Føroyar)",
39830         "fo",
39831         "298"
39832       ],
39833       [
39834         "Fiji",
39835         "fj",
39836         "679"
39837       ],
39838       [
39839         "Finland (Suomi)",
39840         "fi",
39841         "358",
39842         0
39843       ],
39844       [
39845         "France",
39846         "fr",
39847         "33"
39848       ],
39849       [
39850         "French Guiana (Guyane française)",
39851         "gf",
39852         "594"
39853       ],
39854       [
39855         "French Polynesia (Polynésie française)",
39856         "pf",
39857         "689"
39858       ],
39859       [
39860         "Gabon",
39861         "ga",
39862         "241"
39863       ],
39864       [
39865         "Gambia",
39866         "gm",
39867         "220"
39868       ],
39869       [
39870         "Georgia (საქართველო)",
39871         "ge",
39872         "995"
39873       ],
39874       [
39875         "Germany (Deutschland)",
39876         "de",
39877         "49"
39878       ],
39879       [
39880         "Ghana (Gaana)",
39881         "gh",
39882         "233"
39883       ],
39884       [
39885         "Gibraltar",
39886         "gi",
39887         "350"
39888       ],
39889       [
39890         "Greece (Ελλάδα)",
39891         "gr",
39892         "30"
39893       ],
39894       [
39895         "Greenland (Kalaallit Nunaat)",
39896         "gl",
39897         "299"
39898       ],
39899       [
39900         "Grenada",
39901         "gd",
39902         "1473"
39903       ],
39904       [
39905         "Guadeloupe",
39906         "gp",
39907         "590",
39908         0
39909       ],
39910       [
39911         "Guam",
39912         "gu",
39913         "1671"
39914       ],
39915       [
39916         "Guatemala",
39917         "gt",
39918         "502"
39919       ],
39920       [
39921         "Guernsey",
39922         "gg",
39923         "44",
39924         1
39925       ],
39926       [
39927         "Guinea (Guinée)",
39928         "gn",
39929         "224"
39930       ],
39931       [
39932         "Guinea-Bissau (Guiné Bissau)",
39933         "gw",
39934         "245"
39935       ],
39936       [
39937         "Guyana",
39938         "gy",
39939         "592"
39940       ],
39941       [
39942         "Haiti",
39943         "ht",
39944         "509"
39945       ],
39946       [
39947         "Honduras",
39948         "hn",
39949         "504"
39950       ],
39951       [
39952         "Hong Kong (香港)",
39953         "hk",
39954         "852"
39955       ],
39956       [
39957         "Hungary (Magyarország)",
39958         "hu",
39959         "36"
39960       ],
39961       [
39962         "Iceland (Ísland)",
39963         "is",
39964         "354"
39965       ],
39966       [
39967         "India (भारत)",
39968         "in",
39969         "91"
39970       ],
39971       [
39972         "Indonesia",
39973         "id",
39974         "62"
39975       ],
39976       [
39977         "Iran (‫ایران‬‎)",
39978         "ir",
39979         "98"
39980       ],
39981       [
39982         "Iraq (‫العراق‬‎)",
39983         "iq",
39984         "964"
39985       ],
39986       [
39987         "Ireland",
39988         "ie",
39989         "353"
39990       ],
39991       [
39992         "Isle of Man",
39993         "im",
39994         "44",
39995         2
39996       ],
39997       [
39998         "Israel (‫ישראל‬‎)",
39999         "il",
40000         "972"
40001       ],
40002       [
40003         "Italy (Italia)",
40004         "it",
40005         "39",
40006         0
40007       ],
40008       [
40009         "Jamaica",
40010         "jm",
40011         "1876"
40012       ],
40013       [
40014         "Japan (日本)",
40015         "jp",
40016         "81"
40017       ],
40018       [
40019         "Jersey",
40020         "je",
40021         "44",
40022         3
40023       ],
40024       [
40025         "Jordan (‫الأردن‬‎)",
40026         "jo",
40027         "962"
40028       ],
40029       [
40030         "Kazakhstan (Казахстан)",
40031         "kz",
40032         "7",
40033         1
40034       ],
40035       [
40036         "Kenya",
40037         "ke",
40038         "254"
40039       ],
40040       [
40041         "Kiribati",
40042         "ki",
40043         "686"
40044       ],
40045       [
40046         "Kosovo",
40047         "xk",
40048         "383"
40049       ],
40050       [
40051         "Kuwait (‫الكويت‬‎)",
40052         "kw",
40053         "965"
40054       ],
40055       [
40056         "Kyrgyzstan (Кыргызстан)",
40057         "kg",
40058         "996"
40059       ],
40060       [
40061         "Laos (ລາວ)",
40062         "la",
40063         "856"
40064       ],
40065       [
40066         "Latvia (Latvija)",
40067         "lv",
40068         "371"
40069       ],
40070       [
40071         "Lebanon (‫لبنان‬‎)",
40072         "lb",
40073         "961"
40074       ],
40075       [
40076         "Lesotho",
40077         "ls",
40078         "266"
40079       ],
40080       [
40081         "Liberia",
40082         "lr",
40083         "231"
40084       ],
40085       [
40086         "Libya (‫ليبيا‬‎)",
40087         "ly",
40088         "218"
40089       ],
40090       [
40091         "Liechtenstein",
40092         "li",
40093         "423"
40094       ],
40095       [
40096         "Lithuania (Lietuva)",
40097         "lt",
40098         "370"
40099       ],
40100       [
40101         "Luxembourg",
40102         "lu",
40103         "352"
40104       ],
40105       [
40106         "Macau (澳門)",
40107         "mo",
40108         "853"
40109       ],
40110       [
40111         "Macedonia (FYROM) (Македонија)",
40112         "mk",
40113         "389"
40114       ],
40115       [
40116         "Madagascar (Madagasikara)",
40117         "mg",
40118         "261"
40119       ],
40120       [
40121         "Malawi",
40122         "mw",
40123         "265"
40124       ],
40125       [
40126         "Malaysia",
40127         "my",
40128         "60"
40129       ],
40130       [
40131         "Maldives",
40132         "mv",
40133         "960"
40134       ],
40135       [
40136         "Mali",
40137         "ml",
40138         "223"
40139       ],
40140       [
40141         "Malta",
40142         "mt",
40143         "356"
40144       ],
40145       [
40146         "Marshall Islands",
40147         "mh",
40148         "692"
40149       ],
40150       [
40151         "Martinique",
40152         "mq",
40153         "596"
40154       ],
40155       [
40156         "Mauritania (‫موريتانيا‬‎)",
40157         "mr",
40158         "222"
40159       ],
40160       [
40161         "Mauritius (Moris)",
40162         "mu",
40163         "230"
40164       ],
40165       [
40166         "Mayotte",
40167         "yt",
40168         "262",
40169         1
40170       ],
40171       [
40172         "Mexico (México)",
40173         "mx",
40174         "52"
40175       ],
40176       [
40177         "Micronesia",
40178         "fm",
40179         "691"
40180       ],
40181       [
40182         "Moldova (Republica Moldova)",
40183         "md",
40184         "373"
40185       ],
40186       [
40187         "Monaco",
40188         "mc",
40189         "377"
40190       ],
40191       [
40192         "Mongolia (Монгол)",
40193         "mn",
40194         "976"
40195       ],
40196       [
40197         "Montenegro (Crna Gora)",
40198         "me",
40199         "382"
40200       ],
40201       [
40202         "Montserrat",
40203         "ms",
40204         "1664"
40205       ],
40206       [
40207         "Morocco (‫المغرب‬‎)",
40208         "ma",
40209         "212",
40210         0
40211       ],
40212       [
40213         "Mozambique (Moçambique)",
40214         "mz",
40215         "258"
40216       ],
40217       [
40218         "Myanmar (Burma) (မြန်မာ)",
40219         "mm",
40220         "95"
40221       ],
40222       [
40223         "Namibia (Namibië)",
40224         "na",
40225         "264"
40226       ],
40227       [
40228         "Nauru",
40229         "nr",
40230         "674"
40231       ],
40232       [
40233         "Nepal (नेपाल)",
40234         "np",
40235         "977"
40236       ],
40237       [
40238         "Netherlands (Nederland)",
40239         "nl",
40240         "31"
40241       ],
40242       [
40243         "New Caledonia (Nouvelle-Calédonie)",
40244         "nc",
40245         "687"
40246       ],
40247       [
40248         "New Zealand",
40249         "nz",
40250         "64"
40251       ],
40252       [
40253         "Nicaragua",
40254         "ni",
40255         "505"
40256       ],
40257       [
40258         "Niger (Nijar)",
40259         "ne",
40260         "227"
40261       ],
40262       [
40263         "Nigeria",
40264         "ng",
40265         "234"
40266       ],
40267       [
40268         "Niue",
40269         "nu",
40270         "683"
40271       ],
40272       [
40273         "Norfolk Island",
40274         "nf",
40275         "672"
40276       ],
40277       [
40278         "North Korea (조선 민주주의 인민 공화국)",
40279         "kp",
40280         "850"
40281       ],
40282       [
40283         "Northern Mariana Islands",
40284         "mp",
40285         "1670"
40286       ],
40287       [
40288         "Norway (Norge)",
40289         "no",
40290         "47",
40291         0
40292       ],
40293       [
40294         "Oman (‫عُمان‬‎)",
40295         "om",
40296         "968"
40297       ],
40298       [
40299         "Pakistan (‫پاکستان‬‎)",
40300         "pk",
40301         "92"
40302       ],
40303       [
40304         "Palau",
40305         "pw",
40306         "680"
40307       ],
40308       [
40309         "Palestine (‫فلسطين‬‎)",
40310         "ps",
40311         "970"
40312       ],
40313       [
40314         "Panama (Panamá)",
40315         "pa",
40316         "507"
40317       ],
40318       [
40319         "Papua New Guinea",
40320         "pg",
40321         "675"
40322       ],
40323       [
40324         "Paraguay",
40325         "py",
40326         "595"
40327       ],
40328       [
40329         "Peru (Perú)",
40330         "pe",
40331         "51"
40332       ],
40333       [
40334         "Philippines",
40335         "ph",
40336         "63"
40337       ],
40338       [
40339         "Poland (Polska)",
40340         "pl",
40341         "48"
40342       ],
40343       [
40344         "Portugal",
40345         "pt",
40346         "351"
40347       ],
40348       [
40349         "Puerto Rico",
40350         "pr",
40351         "1",
40352         3,
40353         ["787", "939"]
40354       ],
40355       [
40356         "Qatar (‫قطر‬‎)",
40357         "qa",
40358         "974"
40359       ],
40360       [
40361         "Réunion (La Réunion)",
40362         "re",
40363         "262",
40364         0
40365       ],
40366       [
40367         "Romania (România)",
40368         "ro",
40369         "40"
40370       ],
40371       [
40372         "Russia (Россия)",
40373         "ru",
40374         "7",
40375         0
40376       ],
40377       [
40378         "Rwanda",
40379         "rw",
40380         "250"
40381       ],
40382       [
40383         "Saint Barthélemy",
40384         "bl",
40385         "590",
40386         1
40387       ],
40388       [
40389         "Saint Helena",
40390         "sh",
40391         "290"
40392       ],
40393       [
40394         "Saint Kitts and Nevis",
40395         "kn",
40396         "1869"
40397       ],
40398       [
40399         "Saint Lucia",
40400         "lc",
40401         "1758"
40402       ],
40403       [
40404         "Saint Martin (Saint-Martin (partie française))",
40405         "mf",
40406         "590",
40407         2
40408       ],
40409       [
40410         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40411         "pm",
40412         "508"
40413       ],
40414       [
40415         "Saint Vincent and the Grenadines",
40416         "vc",
40417         "1784"
40418       ],
40419       [
40420         "Samoa",
40421         "ws",
40422         "685"
40423       ],
40424       [
40425         "San Marino",
40426         "sm",
40427         "378"
40428       ],
40429       [
40430         "São Tomé and Príncipe (São Tomé e Príncipe)",
40431         "st",
40432         "239"
40433       ],
40434       [
40435         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40436         "sa",
40437         "966"
40438       ],
40439       [
40440         "Senegal (Sénégal)",
40441         "sn",
40442         "221"
40443       ],
40444       [
40445         "Serbia (Србија)",
40446         "rs",
40447         "381"
40448       ],
40449       [
40450         "Seychelles",
40451         "sc",
40452         "248"
40453       ],
40454       [
40455         "Sierra Leone",
40456         "sl",
40457         "232"
40458       ],
40459       [
40460         "Singapore",
40461         "sg",
40462         "65"
40463       ],
40464       [
40465         "Sint Maarten",
40466         "sx",
40467         "1721"
40468       ],
40469       [
40470         "Slovakia (Slovensko)",
40471         "sk",
40472         "421"
40473       ],
40474       [
40475         "Slovenia (Slovenija)",
40476         "si",
40477         "386"
40478       ],
40479       [
40480         "Solomon Islands",
40481         "sb",
40482         "677"
40483       ],
40484       [
40485         "Somalia (Soomaaliya)",
40486         "so",
40487         "252"
40488       ],
40489       [
40490         "South Africa",
40491         "za",
40492         "27"
40493       ],
40494       [
40495         "South Korea (대한민국)",
40496         "kr",
40497         "82"
40498       ],
40499       [
40500         "South Sudan (‫جنوب السودان‬‎)",
40501         "ss",
40502         "211"
40503       ],
40504       [
40505         "Spain (España)",
40506         "es",
40507         "34"
40508       ],
40509       [
40510         "Sri Lanka (ශ්‍රී ලංකාව)",
40511         "lk",
40512         "94"
40513       ],
40514       [
40515         "Sudan (‫السودان‬‎)",
40516         "sd",
40517         "249"
40518       ],
40519       [
40520         "Suriname",
40521         "sr",
40522         "597"
40523       ],
40524       [
40525         "Svalbard and Jan Mayen",
40526         "sj",
40527         "47",
40528         1
40529       ],
40530       [
40531         "Swaziland",
40532         "sz",
40533         "268"
40534       ],
40535       [
40536         "Sweden (Sverige)",
40537         "se",
40538         "46"
40539       ],
40540       [
40541         "Switzerland (Schweiz)",
40542         "ch",
40543         "41"
40544       ],
40545       [
40546         "Syria (‫سوريا‬‎)",
40547         "sy",
40548         "963"
40549       ],
40550       [
40551         "Taiwan (台灣)",
40552         "tw",
40553         "886"
40554       ],
40555       [
40556         "Tajikistan",
40557         "tj",
40558         "992"
40559       ],
40560       [
40561         "Tanzania",
40562         "tz",
40563         "255"
40564       ],
40565       [
40566         "Thailand (ไทย)",
40567         "th",
40568         "66"
40569       ],
40570       [
40571         "Timor-Leste",
40572         "tl",
40573         "670"
40574       ],
40575       [
40576         "Togo",
40577         "tg",
40578         "228"
40579       ],
40580       [
40581         "Tokelau",
40582         "tk",
40583         "690"
40584       ],
40585       [
40586         "Tonga",
40587         "to",
40588         "676"
40589       ],
40590       [
40591         "Trinidad and Tobago",
40592         "tt",
40593         "1868"
40594       ],
40595       [
40596         "Tunisia (‫تونس‬‎)",
40597         "tn",
40598         "216"
40599       ],
40600       [
40601         "Turkey (Türkiye)",
40602         "tr",
40603         "90"
40604       ],
40605       [
40606         "Turkmenistan",
40607         "tm",
40608         "993"
40609       ],
40610       [
40611         "Turks and Caicos Islands",
40612         "tc",
40613         "1649"
40614       ],
40615       [
40616         "Tuvalu",
40617         "tv",
40618         "688"
40619       ],
40620       [
40621         "U.S. Virgin Islands",
40622         "vi",
40623         "1340"
40624       ],
40625       [
40626         "Uganda",
40627         "ug",
40628         "256"
40629       ],
40630       [
40631         "Ukraine (Україна)",
40632         "ua",
40633         "380"
40634       ],
40635       [
40636         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40637         "ae",
40638         "971"
40639       ],
40640       [
40641         "United Kingdom",
40642         "gb",
40643         "44",
40644         0
40645       ],
40646       [
40647         "United States",
40648         "us",
40649         "1",
40650         0
40651       ],
40652       [
40653         "Uruguay",
40654         "uy",
40655         "598"
40656       ],
40657       [
40658         "Uzbekistan (Oʻzbekiston)",
40659         "uz",
40660         "998"
40661       ],
40662       [
40663         "Vanuatu",
40664         "vu",
40665         "678"
40666       ],
40667       [
40668         "Vatican City (Città del Vaticano)",
40669         "va",
40670         "39",
40671         1
40672       ],
40673       [
40674         "Venezuela",
40675         "ve",
40676         "58"
40677       ],
40678       [
40679         "Vietnam (Việt Nam)",
40680         "vn",
40681         "84"
40682       ],
40683       [
40684         "Wallis and Futuna (Wallis-et-Futuna)",
40685         "wf",
40686         "681"
40687       ],
40688       [
40689         "Western Sahara (‫الصحراء الغربية‬‎)",
40690         "eh",
40691         "212",
40692         1
40693       ],
40694       [
40695         "Yemen (‫اليمن‬‎)",
40696         "ye",
40697         "967"
40698       ],
40699       [
40700         "Zambia",
40701         "zm",
40702         "260"
40703       ],
40704       [
40705         "Zimbabwe",
40706         "zw",
40707         "263"
40708       ],
40709       [
40710         "Åland Islands",
40711         "ax",
40712         "358",
40713         1
40714       ]
40715   ];
40716   
40717   return d;
40718 }/**
40719 *    This script refer to:
40720 *    Title: International Telephone Input
40721 *    Author: Jack O'Connor
40722 *    Code version:  v12.1.12
40723 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40724 **/
40725
40726 /**
40727  * @class Roo.bootstrap.PhoneInput
40728  * @extends Roo.bootstrap.TriggerField
40729  * An input with International dial-code selection
40730  
40731  * @cfg {String} defaultDialCode default '+852'
40732  * @cfg {Array} preferedCountries default []
40733   
40734  * @constructor
40735  * Create a new PhoneInput.
40736  * @param {Object} config Configuration options
40737  */
40738
40739 Roo.bootstrap.PhoneInput = function(config) {
40740     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40741 };
40742
40743 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40744         
40745         listWidth: undefined,
40746         
40747         selectedClass: 'active',
40748         
40749         invalidClass : "has-warning",
40750         
40751         validClass: 'has-success',
40752         
40753         allowed: '0123456789',
40754         
40755         max_length: 15,
40756         
40757         /**
40758          * @cfg {String} defaultDialCode The default dial code when initializing the input
40759          */
40760         defaultDialCode: '+852',
40761         
40762         /**
40763          * @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
40764          */
40765         preferedCountries: false,
40766         
40767         getAutoCreate : function()
40768         {
40769             var data = Roo.bootstrap.PhoneInputData();
40770             var align = this.labelAlign || this.parentLabelAlign();
40771             var id = Roo.id();
40772             
40773             this.allCountries = [];
40774             this.dialCodeMapping = [];
40775             
40776             for (var i = 0; i < data.length; i++) {
40777               var c = data[i];
40778               this.allCountries[i] = {
40779                 name: c[0],
40780                 iso2: c[1],
40781                 dialCode: c[2],
40782                 priority: c[3] || 0,
40783                 areaCodes: c[4] || null
40784               };
40785               this.dialCodeMapping[c[2]] = {
40786                   name: c[0],
40787                   iso2: c[1],
40788                   priority: c[3] || 0,
40789                   areaCodes: c[4] || null
40790               };
40791             }
40792             
40793             var cfg = {
40794                 cls: 'form-group',
40795                 cn: []
40796             };
40797             
40798             var input =  {
40799                 tag: 'input',
40800                 id : id,
40801                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40802                 maxlength: this.max_length,
40803                 cls : 'form-control tel-input',
40804                 autocomplete: 'new-password'
40805             };
40806             
40807             var hiddenInput = {
40808                 tag: 'input',
40809                 type: 'hidden',
40810                 cls: 'hidden-tel-input'
40811             };
40812             
40813             if (this.name) {
40814                 hiddenInput.name = this.name;
40815             }
40816             
40817             if (this.disabled) {
40818                 input.disabled = true;
40819             }
40820             
40821             var flag_container = {
40822                 tag: 'div',
40823                 cls: 'flag-box',
40824                 cn: [
40825                     {
40826                         tag: 'div',
40827                         cls: 'flag'
40828                     },
40829                     {
40830                         tag: 'div',
40831                         cls: 'caret'
40832                     }
40833                 ]
40834             };
40835             
40836             var box = {
40837                 tag: 'div',
40838                 cls: this.hasFeedback ? 'has-feedback' : '',
40839                 cn: [
40840                     hiddenInput,
40841                     input,
40842                     {
40843                         tag: 'input',
40844                         cls: 'dial-code-holder',
40845                         disabled: true
40846                     }
40847                 ]
40848             };
40849             
40850             var container = {
40851                 cls: 'roo-select2-container input-group',
40852                 cn: [
40853                     flag_container,
40854                     box
40855                 ]
40856             };
40857             
40858             if (this.fieldLabel.length) {
40859                 var indicator = {
40860                     tag: 'i',
40861                     tooltip: 'This field is required'
40862                 };
40863                 
40864                 var label = {
40865                     tag: 'label',
40866                     'for':  id,
40867                     cls: 'control-label',
40868                     cn: []
40869                 };
40870                 
40871                 var label_text = {
40872                     tag: 'span',
40873                     html: this.fieldLabel
40874                 };
40875                 
40876                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40877                 label.cn = [
40878                     indicator,
40879                     label_text
40880                 ];
40881                 
40882                 if(this.indicatorpos == 'right') {
40883                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40884                     label.cn = [
40885                         label_text,
40886                         indicator
40887                     ];
40888                 }
40889                 
40890                 if(align == 'left') {
40891                     container = {
40892                         tag: 'div',
40893                         cn: [
40894                             container
40895                         ]
40896                     };
40897                     
40898                     if(this.labelWidth > 12){
40899                         label.style = "width: " + this.labelWidth + 'px';
40900                     }
40901                     if(this.labelWidth < 13 && this.labelmd == 0){
40902                         this.labelmd = this.labelWidth;
40903                     }
40904                     if(this.labellg > 0){
40905                         label.cls += ' col-lg-' + this.labellg;
40906                         input.cls += ' col-lg-' + (12 - this.labellg);
40907                     }
40908                     if(this.labelmd > 0){
40909                         label.cls += ' col-md-' + this.labelmd;
40910                         container.cls += ' col-md-' + (12 - this.labelmd);
40911                     }
40912                     if(this.labelsm > 0){
40913                         label.cls += ' col-sm-' + this.labelsm;
40914                         container.cls += ' col-sm-' + (12 - this.labelsm);
40915                     }
40916                     if(this.labelxs > 0){
40917                         label.cls += ' col-xs-' + this.labelxs;
40918                         container.cls += ' col-xs-' + (12 - this.labelxs);
40919                     }
40920                 }
40921             }
40922             
40923             cfg.cn = [
40924                 label,
40925                 container
40926             ];
40927             
40928             var settings = this;
40929             
40930             ['xs','sm','md','lg'].map(function(size){
40931                 if (settings[size]) {
40932                     cfg.cls += ' col-' + size + '-' + settings[size];
40933                 }
40934             });
40935             
40936             this.store = new Roo.data.Store({
40937                 proxy : new Roo.data.MemoryProxy({}),
40938                 reader : new Roo.data.JsonReader({
40939                     fields : [
40940                         {
40941                             'name' : 'name',
40942                             'type' : 'string'
40943                         },
40944                         {
40945                             'name' : 'iso2',
40946                             'type' : 'string'
40947                         },
40948                         {
40949                             'name' : 'dialCode',
40950                             'type' : 'string'
40951                         },
40952                         {
40953                             'name' : 'priority',
40954                             'type' : 'string'
40955                         },
40956                         {
40957                             'name' : 'areaCodes',
40958                             'type' : 'string'
40959                         }
40960                     ]
40961                 })
40962             });
40963             
40964             if(!this.preferedCountries) {
40965                 this.preferedCountries = [
40966                     'hk',
40967                     'gb',
40968                     'us'
40969                 ];
40970             }
40971             
40972             var p = this.preferedCountries.reverse();
40973             
40974             if(p) {
40975                 for (var i = 0; i < p.length; i++) {
40976                     for (var j = 0; j < this.allCountries.length; j++) {
40977                         if(this.allCountries[j].iso2 == p[i]) {
40978                             var t = this.allCountries[j];
40979                             this.allCountries.splice(j,1);
40980                             this.allCountries.unshift(t);
40981                         }
40982                     } 
40983                 }
40984             }
40985             
40986             this.store.proxy.data = {
40987                 success: true,
40988                 data: this.allCountries
40989             };
40990             
40991             return cfg;
40992         },
40993         
40994         initEvents : function()
40995         {
40996             this.createList();
40997             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40998             
40999             this.indicator = this.indicatorEl();
41000             this.flag = this.flagEl();
41001             this.dialCodeHolder = this.dialCodeHolderEl();
41002             
41003             this.trigger = this.el.select('div.flag-box',true).first();
41004             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41005             
41006             var _this = this;
41007             
41008             (function(){
41009                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41010                 _this.list.setWidth(lw);
41011             }).defer(100);
41012             
41013             this.list.on('mouseover', this.onViewOver, this);
41014             this.list.on('mousemove', this.onViewMove, this);
41015             this.inputEl().on("keyup", this.onKeyUp, this);
41016             this.inputEl().on("keypress", this.onKeyPress, this);
41017             
41018             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41019
41020             this.view = new Roo.View(this.list, this.tpl, {
41021                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41022             });
41023             
41024             this.view.on('click', this.onViewClick, this);
41025             this.setValue(this.defaultDialCode);
41026         },
41027         
41028         onTriggerClick : function(e)
41029         {
41030             Roo.log('trigger click');
41031             if(this.disabled){
41032                 return;
41033             }
41034             
41035             if(this.isExpanded()){
41036                 this.collapse();
41037                 this.hasFocus = false;
41038             }else {
41039                 this.store.load({});
41040                 this.hasFocus = true;
41041                 this.expand();
41042             }
41043         },
41044         
41045         isExpanded : function()
41046         {
41047             return this.list.isVisible();
41048         },
41049         
41050         collapse : function()
41051         {
41052             if(!this.isExpanded()){
41053                 return;
41054             }
41055             this.list.hide();
41056             Roo.get(document).un('mousedown', this.collapseIf, this);
41057             Roo.get(document).un('mousewheel', this.collapseIf, this);
41058             this.fireEvent('collapse', this);
41059             this.validate();
41060         },
41061         
41062         expand : function()
41063         {
41064             Roo.log('expand');
41065
41066             if(this.isExpanded() || !this.hasFocus){
41067                 return;
41068             }
41069             
41070             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41071             this.list.setWidth(lw);
41072             
41073             this.list.show();
41074             this.restrictHeight();
41075             
41076             Roo.get(document).on('mousedown', this.collapseIf, this);
41077             Roo.get(document).on('mousewheel', this.collapseIf, this);
41078             
41079             this.fireEvent('expand', this);
41080         },
41081         
41082         restrictHeight : function()
41083         {
41084             this.list.alignTo(this.inputEl(), this.listAlign);
41085             this.list.alignTo(this.inputEl(), this.listAlign);
41086         },
41087         
41088         onViewOver : function(e, t)
41089         {
41090             if(this.inKeyMode){
41091                 return;
41092             }
41093             var item = this.view.findItemFromChild(t);
41094             
41095             if(item){
41096                 var index = this.view.indexOf(item);
41097                 this.select(index, false);
41098             }
41099         },
41100
41101         // private
41102         onViewClick : function(view, doFocus, el, e)
41103         {
41104             var index = this.view.getSelectedIndexes()[0];
41105             
41106             var r = this.store.getAt(index);
41107             
41108             if(r){
41109                 this.onSelect(r, index);
41110             }
41111             if(doFocus !== false && !this.blockFocus){
41112                 this.inputEl().focus();
41113             }
41114         },
41115         
41116         onViewMove : function(e, t)
41117         {
41118             this.inKeyMode = false;
41119         },
41120         
41121         select : function(index, scrollIntoView)
41122         {
41123             this.selectedIndex = index;
41124             this.view.select(index);
41125             if(scrollIntoView !== false){
41126                 var el = this.view.getNode(index);
41127                 if(el){
41128                     this.list.scrollChildIntoView(el, false);
41129                 }
41130             }
41131         },
41132         
41133         createList : function()
41134         {
41135             this.list = Roo.get(document.body).createChild({
41136                 tag: 'ul',
41137                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41138                 style: 'display:none'
41139             });
41140             
41141             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41142         },
41143         
41144         collapseIf : function(e)
41145         {
41146             var in_combo  = e.within(this.el);
41147             var in_list =  e.within(this.list);
41148             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41149             
41150             if (in_combo || in_list || is_list) {
41151                 return;
41152             }
41153             this.collapse();
41154         },
41155         
41156         onSelect : function(record, index)
41157         {
41158             if(this.fireEvent('beforeselect', this, record, index) !== false){
41159                 
41160                 this.setFlagClass(record.data.iso2);
41161                 this.setDialCode(record.data.dialCode);
41162                 this.hasFocus = false;
41163                 this.collapse();
41164                 this.fireEvent('select', this, record, index);
41165             }
41166         },
41167         
41168         flagEl : function()
41169         {
41170             var flag = this.el.select('div.flag',true).first();
41171             if(!flag){
41172                 return false;
41173             }
41174             return flag;
41175         },
41176         
41177         dialCodeHolderEl : function()
41178         {
41179             var d = this.el.select('input.dial-code-holder',true).first();
41180             if(!d){
41181                 return false;
41182             }
41183             return d;
41184         },
41185         
41186         setDialCode : function(v)
41187         {
41188             this.dialCodeHolder.dom.value = '+'+v;
41189         },
41190         
41191         setFlagClass : function(n)
41192         {
41193             this.flag.dom.className = 'flag '+n;
41194         },
41195         
41196         getValue : function()
41197         {
41198             var v = this.inputEl().getValue();
41199             if(this.dialCodeHolder) {
41200                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41201             }
41202             return v;
41203         },
41204         
41205         setValue : function(v)
41206         {
41207             var d = this.getDialCode(v);
41208             
41209             //invalid dial code
41210             if(v.length == 0 || !d || d.length == 0) {
41211                 if(this.rendered){
41212                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41213                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41214                 }
41215                 return;
41216             }
41217             
41218             //valid dial code
41219             this.setFlagClass(this.dialCodeMapping[d].iso2);
41220             this.setDialCode(d);
41221             this.inputEl().dom.value = v.replace('+'+d,'');
41222             this.hiddenEl().dom.value = this.getValue();
41223             
41224             this.validate();
41225         },
41226         
41227         getDialCode : function(v)
41228         {
41229             v = v ||  '';
41230             
41231             if (v.length == 0) {
41232                 return this.dialCodeHolder.dom.value;
41233             }
41234             
41235             var dialCode = "";
41236             if (v.charAt(0) != "+") {
41237                 return false;
41238             }
41239             var numericChars = "";
41240             for (var i = 1; i < v.length; i++) {
41241               var c = v.charAt(i);
41242               if (!isNaN(c)) {
41243                 numericChars += c;
41244                 if (this.dialCodeMapping[numericChars]) {
41245                   dialCode = v.substr(1, i);
41246                 }
41247                 if (numericChars.length == 4) {
41248                   break;
41249                 }
41250               }
41251             }
41252             return dialCode;
41253         },
41254         
41255         reset : function()
41256         {
41257             this.setValue(this.defaultDialCode);
41258             this.validate();
41259         },
41260         
41261         hiddenEl : function()
41262         {
41263             return this.el.select('input.hidden-tel-input',true).first();
41264         },
41265         
41266         // after setting val
41267         onKeyUp : function(e){
41268             this.setValue(this.getValue());
41269         },
41270         
41271         onKeyPress : function(e){
41272             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41273                 e.stopEvent();
41274             }
41275         }
41276         
41277 });
41278 /**
41279  * @class Roo.bootstrap.MoneyField
41280  * @extends Roo.bootstrap.ComboBox
41281  * Bootstrap MoneyField class
41282  * 
41283  * @constructor
41284  * Create a new MoneyField.
41285  * @param {Object} config Configuration options
41286  */
41287
41288 Roo.bootstrap.MoneyField = function(config) {
41289     
41290     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41291     
41292 };
41293
41294 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41295     
41296     /**
41297      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41298      */
41299     allowDecimals : true,
41300     /**
41301      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41302      */
41303     decimalSeparator : ".",
41304     /**
41305      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41306      */
41307     decimalPrecision : 0,
41308     /**
41309      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41310      */
41311     allowNegative : true,
41312     /**
41313      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41314      */
41315     allowZero: true,
41316     /**
41317      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41318      */
41319     minValue : Number.NEGATIVE_INFINITY,
41320     /**
41321      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41322      */
41323     maxValue : Number.MAX_VALUE,
41324     /**
41325      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41326      */
41327     minText : "The minimum value for this field is {0}",
41328     /**
41329      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41330      */
41331     maxText : "The maximum value for this field is {0}",
41332     /**
41333      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41334      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41335      */
41336     nanText : "{0} is not a valid number",
41337     /**
41338      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41339      */
41340     castInt : true,
41341     /**
41342      * @cfg {String} defaults currency of the MoneyField
41343      * value should be in lkey
41344      */
41345     defaultCurrency : false,
41346     /**
41347      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41348      */
41349     thousandsDelimiter : false,
41350     /**
41351      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41352      */
41353     max_length: false,
41354     
41355     inputlg : 9,
41356     inputmd : 9,
41357     inputsm : 9,
41358     inputxs : 6,
41359     
41360     store : false,
41361     
41362     getAutoCreate : function()
41363     {
41364         var align = this.labelAlign || this.parentLabelAlign();
41365         
41366         var id = Roo.id();
41367
41368         var cfg = {
41369             cls: 'form-group',
41370             cn: []
41371         };
41372
41373         var input =  {
41374             tag: 'input',
41375             id : id,
41376             cls : 'form-control roo-money-amount-input',
41377             autocomplete: 'new-password'
41378         };
41379         
41380         var hiddenInput = {
41381             tag: 'input',
41382             type: 'hidden',
41383             id: Roo.id(),
41384             cls: 'hidden-number-input'
41385         };
41386         
41387         if(this.max_length) {
41388             input.maxlength = this.max_length; 
41389         }
41390         
41391         if (this.name) {
41392             hiddenInput.name = this.name;
41393         }
41394
41395         if (this.disabled) {
41396             input.disabled = true;
41397         }
41398
41399         var clg = 12 - this.inputlg;
41400         var cmd = 12 - this.inputmd;
41401         var csm = 12 - this.inputsm;
41402         var cxs = 12 - this.inputxs;
41403         
41404         var container = {
41405             tag : 'div',
41406             cls : 'row roo-money-field',
41407             cn : [
41408                 {
41409                     tag : 'div',
41410                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41411                     cn : [
41412                         {
41413                             tag : 'div',
41414                             cls: 'roo-select2-container input-group',
41415                             cn: [
41416                                 {
41417                                     tag : 'input',
41418                                     cls : 'form-control roo-money-currency-input',
41419                                     autocomplete: 'new-password',
41420                                     readOnly : 1,
41421                                     name : this.currencyName
41422                                 },
41423                                 {
41424                                     tag :'span',
41425                                     cls : 'input-group-addon',
41426                                     cn : [
41427                                         {
41428                                             tag: 'span',
41429                                             cls: 'caret'
41430                                         }
41431                                     ]
41432                                 }
41433                             ]
41434                         }
41435                     ]
41436                 },
41437                 {
41438                     tag : 'div',
41439                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41440                     cn : [
41441                         {
41442                             tag: 'div',
41443                             cls: this.hasFeedback ? 'has-feedback' : '',
41444                             cn: [
41445                                 input
41446                             ]
41447                         }
41448                     ]
41449                 }
41450             ]
41451             
41452         };
41453         
41454         if (this.fieldLabel.length) {
41455             var indicator = {
41456                 tag: 'i',
41457                 tooltip: 'This field is required'
41458             };
41459
41460             var label = {
41461                 tag: 'label',
41462                 'for':  id,
41463                 cls: 'control-label',
41464                 cn: []
41465             };
41466
41467             var label_text = {
41468                 tag: 'span',
41469                 html: this.fieldLabel
41470             };
41471
41472             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41473             label.cn = [
41474                 indicator,
41475                 label_text
41476             ];
41477
41478             if(this.indicatorpos == 'right') {
41479                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41480                 label.cn = [
41481                     label_text,
41482                     indicator
41483                 ];
41484             }
41485
41486             if(align == 'left') {
41487                 container = {
41488                     tag: 'div',
41489                     cn: [
41490                         container
41491                     ]
41492                 };
41493
41494                 if(this.labelWidth > 12){
41495                     label.style = "width: " + this.labelWidth + 'px';
41496                 }
41497                 if(this.labelWidth < 13 && this.labelmd == 0){
41498                     this.labelmd = this.labelWidth;
41499                 }
41500                 if(this.labellg > 0){
41501                     label.cls += ' col-lg-' + this.labellg;
41502                     input.cls += ' col-lg-' + (12 - this.labellg);
41503                 }
41504                 if(this.labelmd > 0){
41505                     label.cls += ' col-md-' + this.labelmd;
41506                     container.cls += ' col-md-' + (12 - this.labelmd);
41507                 }
41508                 if(this.labelsm > 0){
41509                     label.cls += ' col-sm-' + this.labelsm;
41510                     container.cls += ' col-sm-' + (12 - this.labelsm);
41511                 }
41512                 if(this.labelxs > 0){
41513                     label.cls += ' col-xs-' + this.labelxs;
41514                     container.cls += ' col-xs-' + (12 - this.labelxs);
41515                 }
41516             }
41517         }
41518
41519         cfg.cn = [
41520             label,
41521             container,
41522             hiddenInput
41523         ];
41524         
41525         var settings = this;
41526
41527         ['xs','sm','md','lg'].map(function(size){
41528             if (settings[size]) {
41529                 cfg.cls += ' col-' + size + '-' + settings[size];
41530             }
41531         });
41532         
41533         return cfg;
41534     },
41535     
41536     initEvents : function()
41537     {
41538         this.indicator = this.indicatorEl();
41539         
41540         this.initCurrencyEvent();
41541         
41542         this.initNumberEvent();
41543     },
41544     
41545     initCurrencyEvent : function()
41546     {
41547         if (!this.store) {
41548             throw "can not find store for combo";
41549         }
41550         
41551         this.store = Roo.factory(this.store, Roo.data);
41552         this.store.parent = this;
41553         
41554         this.createList();
41555         
41556         this.triggerEl = this.el.select('.input-group-addon', true).first();
41557         
41558         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41559         
41560         var _this = this;
41561         
41562         (function(){
41563             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41564             _this.list.setWidth(lw);
41565         }).defer(100);
41566         
41567         this.list.on('mouseover', this.onViewOver, this);
41568         this.list.on('mousemove', this.onViewMove, this);
41569         this.list.on('scroll', this.onViewScroll, this);
41570         
41571         if(!this.tpl){
41572             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41573         }
41574         
41575         this.view = new Roo.View(this.list, this.tpl, {
41576             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41577         });
41578         
41579         this.view.on('click', this.onViewClick, this);
41580         
41581         this.store.on('beforeload', this.onBeforeLoad, this);
41582         this.store.on('load', this.onLoad, this);
41583         this.store.on('loadexception', this.onLoadException, this);
41584         
41585         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41586             "up" : function(e){
41587                 this.inKeyMode = true;
41588                 this.selectPrev();
41589             },
41590
41591             "down" : function(e){
41592                 if(!this.isExpanded()){
41593                     this.onTriggerClick();
41594                 }else{
41595                     this.inKeyMode = true;
41596                     this.selectNext();
41597                 }
41598             },
41599
41600             "enter" : function(e){
41601                 this.collapse();
41602                 
41603                 if(this.fireEvent("specialkey", this, e)){
41604                     this.onViewClick(false);
41605                 }
41606                 
41607                 return true;
41608             },
41609
41610             "esc" : function(e){
41611                 this.collapse();
41612             },
41613
41614             "tab" : function(e){
41615                 this.collapse();
41616                 
41617                 if(this.fireEvent("specialkey", this, e)){
41618                     this.onViewClick(false);
41619                 }
41620                 
41621                 return true;
41622             },
41623
41624             scope : this,
41625
41626             doRelay : function(foo, bar, hname){
41627                 if(hname == 'down' || this.scope.isExpanded()){
41628                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41629                 }
41630                 return true;
41631             },
41632
41633             forceKeyDown: true
41634         });
41635         
41636         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41637         
41638     },
41639     
41640     initNumberEvent : function(e)
41641     {
41642         this.inputEl().on("keydown" , this.fireKey,  this);
41643         this.inputEl().on("focus", this.onFocus,  this);
41644         this.inputEl().on("blur", this.onBlur,  this);
41645         
41646         this.inputEl().relayEvent('keyup', this);
41647         
41648         if(this.indicator){
41649             this.indicator.addClass('invisible');
41650         }
41651  
41652         this.originalValue = this.getValue();
41653         
41654         if(this.validationEvent == 'keyup'){
41655             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41656             this.inputEl().on('keyup', this.filterValidation, this);
41657         }
41658         else if(this.validationEvent !== false){
41659             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41660         }
41661         
41662         if(this.selectOnFocus){
41663             this.on("focus", this.preFocus, this);
41664             
41665         }
41666         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41667             this.inputEl().on("keypress", this.filterKeys, this);
41668         } else {
41669             this.inputEl().relayEvent('keypress', this);
41670         }
41671         
41672         var allowed = "0123456789";
41673         
41674         if(this.allowDecimals){
41675             allowed += this.decimalSeparator;
41676         }
41677         
41678         if(this.allowNegative){
41679             allowed += "-";
41680         }
41681         
41682         if(this.thousandsDelimiter) {
41683             allowed += ",";
41684         }
41685         
41686         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41687         
41688         var keyPress = function(e){
41689             
41690             var k = e.getKey();
41691             
41692             var c = e.getCharCode();
41693             
41694             if(
41695                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41696                     allowed.indexOf(String.fromCharCode(c)) === -1
41697             ){
41698                 e.stopEvent();
41699                 return;
41700             }
41701             
41702             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41703                 return;
41704             }
41705             
41706             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41707                 e.stopEvent();
41708             }
41709         };
41710         
41711         this.inputEl().on("keypress", keyPress, this);
41712         
41713     },
41714     
41715     onTriggerClick : function(e)
41716     {   
41717         if(this.disabled){
41718             return;
41719         }
41720         
41721         this.page = 0;
41722         this.loadNext = false;
41723         
41724         if(this.isExpanded()){
41725             this.collapse();
41726             return;
41727         }
41728         
41729         this.hasFocus = true;
41730         
41731         if(this.triggerAction == 'all') {
41732             this.doQuery(this.allQuery, true);
41733             return;
41734         }
41735         
41736         this.doQuery(this.getRawValue());
41737     },
41738     
41739     getCurrency : function()
41740     {   
41741         var v = this.currencyEl().getValue();
41742         
41743         return v;
41744     },
41745     
41746     restrictHeight : function()
41747     {
41748         this.list.alignTo(this.currencyEl(), this.listAlign);
41749         this.list.alignTo(this.currencyEl(), this.listAlign);
41750     },
41751     
41752     onViewClick : function(view, doFocus, el, e)
41753     {
41754         var index = this.view.getSelectedIndexes()[0];
41755         
41756         var r = this.store.getAt(index);
41757         
41758         if(r){
41759             this.onSelect(r, index);
41760         }
41761     },
41762     
41763     onSelect : function(record, index){
41764         
41765         if(this.fireEvent('beforeselect', this, record, index) !== false){
41766         
41767             this.setFromCurrencyData(index > -1 ? record.data : false);
41768             
41769             this.collapse();
41770             
41771             this.fireEvent('select', this, record, index);
41772         }
41773     },
41774     
41775     setFromCurrencyData : function(o)
41776     {
41777         var currency = '';
41778         
41779         this.lastCurrency = o;
41780         
41781         if (this.currencyField) {
41782             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41783         } else {
41784             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41785         }
41786         
41787         this.lastSelectionText = currency;
41788         
41789         //setting default currency
41790         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41791             this.setCurrency(this.defaultCurrency);
41792             return;
41793         }
41794         
41795         this.setCurrency(currency);
41796     },
41797     
41798     setFromData : function(o)
41799     {
41800         var c = {};
41801         
41802         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41803         
41804         this.setFromCurrencyData(c);
41805         
41806         var value = '';
41807         
41808         if (this.name) {
41809             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41810         } else {
41811             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41812         }
41813         
41814         this.setValue(value);
41815         
41816     },
41817     
41818     setCurrency : function(v)
41819     {   
41820         this.currencyValue = v;
41821         
41822         if(this.rendered){
41823             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41824             this.validate();
41825         }
41826     },
41827     
41828     setValue : function(v)
41829     {
41830         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41831         
41832         this.value = v;
41833         
41834         if(this.rendered){
41835             
41836             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41837             
41838             this.inputEl().dom.value = (v == '') ? '' :
41839                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41840             
41841             if(!this.allowZero && v === '0') {
41842                 this.hiddenEl().dom.value = '';
41843                 this.inputEl().dom.value = '';
41844             }
41845             
41846             this.validate();
41847         }
41848     },
41849     
41850     getRawValue : function()
41851     {
41852         var v = this.inputEl().getValue();
41853         
41854         return v;
41855     },
41856     
41857     getValue : function()
41858     {
41859         return this.fixPrecision(this.parseValue(this.getRawValue()));
41860     },
41861     
41862     parseValue : function(value)
41863     {
41864         if(this.thousandsDelimiter) {
41865             value += "";
41866             r = new RegExp(",", "g");
41867             value = value.replace(r, "");
41868         }
41869         
41870         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41871         return isNaN(value) ? '' : value;
41872         
41873     },
41874     
41875     fixPrecision : function(value)
41876     {
41877         if(this.thousandsDelimiter) {
41878             value += "";
41879             r = new RegExp(",", "g");
41880             value = value.replace(r, "");
41881         }
41882         
41883         var nan = isNaN(value);
41884         
41885         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41886             return nan ? '' : value;
41887         }
41888         return parseFloat(value).toFixed(this.decimalPrecision);
41889     },
41890     
41891     decimalPrecisionFcn : function(v)
41892     {
41893         return Math.floor(v);
41894     },
41895     
41896     validateValue : function(value)
41897     {
41898         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41899             return false;
41900         }
41901         
41902         var num = this.parseValue(value);
41903         
41904         if(isNaN(num)){
41905             this.markInvalid(String.format(this.nanText, value));
41906             return false;
41907         }
41908         
41909         if(num < this.minValue){
41910             this.markInvalid(String.format(this.minText, this.minValue));
41911             return false;
41912         }
41913         
41914         if(num > this.maxValue){
41915             this.markInvalid(String.format(this.maxText, this.maxValue));
41916             return false;
41917         }
41918         
41919         return true;
41920     },
41921     
41922     validate : function()
41923     {
41924         if(this.disabled || this.allowBlank){
41925             this.markValid();
41926             return true;
41927         }
41928         
41929         var currency = this.getCurrency();
41930         
41931         if(this.validateValue(this.getRawValue()) && currency.length){
41932             this.markValid();
41933             return true;
41934         }
41935         
41936         this.markInvalid();
41937         return false;
41938     },
41939     
41940     getName: function()
41941     {
41942         return this.name;
41943     },
41944     
41945     beforeBlur : function()
41946     {
41947         if(!this.castInt){
41948             return;
41949         }
41950         
41951         var v = this.parseValue(this.getRawValue());
41952         
41953         if(v || v == 0){
41954             this.setValue(v);
41955         }
41956     },
41957     
41958     onBlur : function()
41959     {
41960         this.beforeBlur();
41961         
41962         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41963             //this.el.removeClass(this.focusClass);
41964         }
41965         
41966         this.hasFocus = false;
41967         
41968         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41969             this.validate();
41970         }
41971         
41972         var v = this.getValue();
41973         
41974         if(String(v) !== String(this.startValue)){
41975             this.fireEvent('change', this, v, this.startValue);
41976         }
41977         
41978         this.fireEvent("blur", this);
41979     },
41980     
41981     inputEl : function()
41982     {
41983         return this.el.select('.roo-money-amount-input', true).first();
41984     },
41985     
41986     currencyEl : function()
41987     {
41988         return this.el.select('.roo-money-currency-input', true).first();
41989     },
41990     
41991     hiddenEl : function()
41992     {
41993         return this.el.select('input.hidden-number-input',true).first();
41994     }
41995     
41996 });/**
41997  * @class Roo.bootstrap.BezierSignature
41998  * @extends Roo.bootstrap.Component
41999  * Bootstrap BezierSignature class
42000  * This script refer to:
42001  *    Title: Signature Pad
42002  *    Author: szimek
42003  *    Availability: https://github.com/szimek/signature_pad
42004  *
42005  * @constructor
42006  * Create a new BezierSignature
42007  * @param {Object} config The config object
42008  */
42009
42010 Roo.bootstrap.BezierSignature = function(config){
42011     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42012     this.addEvents({
42013         "resize" : true
42014     });
42015 };
42016
42017 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42018 {
42019      
42020     curve_data: [],
42021     
42022     is_empty: true,
42023     
42024     mouse_btn_down: true,
42025     
42026     /**
42027      * @cfg {int} canvas height
42028      */
42029     canvas_height: '200px',
42030     
42031     /**
42032      * @cfg {float|function} Radius of a single dot.
42033      */ 
42034     dot_size: false,
42035     
42036     /**
42037      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42038      */
42039     min_width: 0.5,
42040     
42041     /**
42042      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42043      */
42044     max_width: 2.5,
42045     
42046     /**
42047      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42048      */
42049     throttle: 16,
42050     
42051     /**
42052      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42053      */
42054     min_distance: 5,
42055     
42056     /**
42057      * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
42058      */
42059     bg_color: 'rgba(0, 0, 0, 0)',
42060     
42061     /**
42062      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42063      */
42064     dot_color: 'black',
42065     
42066     /**
42067      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42068      */ 
42069     velocity_filter_weight: 0.7,
42070     
42071     /**
42072      * @cfg {function} Callback when stroke begin. 
42073      */
42074     onBegin: false,
42075     
42076     /**
42077      * @cfg {function} Callback when stroke end.
42078      */
42079     onEnd: false,
42080     
42081     getAutoCreate : function()
42082     {
42083         var cls = 'roo-signature column';
42084         
42085         if(this.cls){
42086             cls += ' ' + this.cls;
42087         }
42088         
42089         var col_sizes = [
42090             'lg',
42091             'md',
42092             'sm',
42093             'xs'
42094         ];
42095         
42096         for(var i = 0; i < col_sizes.length; i++) {
42097             if(this[col_sizes[i]]) {
42098                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42099             }
42100         }
42101         
42102         var cfg = {
42103             tag: 'div',
42104             cls: cls,
42105             cn: [
42106                 {
42107                     tag: 'div',
42108                     cls: 'roo-signature-body',
42109                     cn: [
42110                         {
42111                             tag: 'canvas',
42112                             cls: 'roo-signature-body-canvas',
42113                             height: this.canvas_height,
42114                             width: this.canvas_width
42115                         }
42116                     ]
42117                 },
42118                 {
42119                     tag: 'input',
42120                     type: 'file',
42121                     style: 'display: none'
42122                 }
42123             ]
42124         };
42125         
42126         return cfg;
42127     },
42128     
42129     initEvents: function() 
42130     {
42131         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42132         
42133         var canvas = this.canvasEl();
42134         
42135         // mouse && touch event swapping...
42136         canvas.dom.style.touchAction = 'none';
42137         canvas.dom.style.msTouchAction = 'none';
42138         
42139         this.mouse_btn_down = false;
42140         canvas.on('mousedown', this._handleMouseDown, this);
42141         canvas.on('mousemove', this._handleMouseMove, this);
42142         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42143         
42144         if (window.PointerEvent) {
42145             canvas.on('pointerdown', this._handleMouseDown, this);
42146             canvas.on('pointermove', this._handleMouseMove, this);
42147             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42148         }
42149         
42150         if ('ontouchstart' in window) {
42151             canvas.on('touchstart', this._handleTouchStart, this);
42152             canvas.on('touchmove', this._handleTouchMove, this);
42153             canvas.on('touchend', this._handleTouchEnd, this);
42154         }
42155         
42156         Roo.EventManager.onWindowResize(this.resize, this, true);
42157         
42158         // file input event
42159         this.fileEl().on('change', this.uploadImage, this);
42160         
42161         this.clear();
42162         
42163         this.resize();
42164     },
42165     
42166     resize: function(){
42167         
42168         var canvas = this.canvasEl().dom;
42169         var ctx = this.canvasElCtx();
42170         var img_data = false;
42171         
42172         if(canvas.width > 0) {
42173             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42174         }
42175         // setting canvas width will clean img data
42176         canvas.width = 0;
42177         
42178         var style = window.getComputedStyle ? 
42179             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42180             
42181         var padding_left = parseInt(style.paddingLeft) || 0;
42182         var padding_right = parseInt(style.paddingRight) || 0;
42183         
42184         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42185         
42186         if(img_data) {
42187             ctx.putImageData(img_data, 0, 0);
42188         }
42189     },
42190     
42191     _handleMouseDown: function(e)
42192     {
42193         if (e.browserEvent.which === 1) {
42194             this.mouse_btn_down = true;
42195             this.strokeBegin(e);
42196         }
42197     },
42198     
42199     _handleMouseMove: function (e)
42200     {
42201         if (this.mouse_btn_down) {
42202             this.strokeMoveUpdate(e);
42203         }
42204     },
42205     
42206     _handleMouseUp: function (e)
42207     {
42208         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42209             this.mouse_btn_down = false;
42210             this.strokeEnd(e);
42211         }
42212     },
42213     
42214     _handleTouchStart: function (e) {
42215         
42216         e.preventDefault();
42217         if (e.browserEvent.targetTouches.length === 1) {
42218             // var touch = e.browserEvent.changedTouches[0];
42219             // this.strokeBegin(touch);
42220             
42221              this.strokeBegin(e); // assume e catching the correct xy...
42222         }
42223     },
42224     
42225     _handleTouchMove: function (e) {
42226         e.preventDefault();
42227         // var touch = event.targetTouches[0];
42228         // _this._strokeMoveUpdate(touch);
42229         this.strokeMoveUpdate(e);
42230     },
42231     
42232     _handleTouchEnd: function (e) {
42233         var wasCanvasTouched = e.target === this.canvasEl().dom;
42234         if (wasCanvasTouched) {
42235             e.preventDefault();
42236             // var touch = event.changedTouches[0];
42237             // _this._strokeEnd(touch);
42238             this.strokeEnd(e);
42239         }
42240     },
42241     
42242     reset: function () {
42243         this._lastPoints = [];
42244         this._lastVelocity = 0;
42245         this._lastWidth = (this.min_width + this.max_width) / 2;
42246         this.canvasElCtx().fillStyle = this.dot_color;
42247     },
42248     
42249     strokeMoveUpdate: function(e)
42250     {
42251         this.strokeUpdate(e);
42252         
42253         if (this.throttle) {
42254             this.throttleStroke(this.strokeUpdate, this.throttle);
42255         }
42256         else {
42257             this.strokeUpdate(e);
42258         }
42259     },
42260     
42261     strokeBegin: function(e)
42262     {
42263         var newPointGroup = {
42264             color: this.dot_color,
42265             points: []
42266         };
42267         
42268         if (typeof this.onBegin === 'function') {
42269             this.onBegin(e);
42270         }
42271         
42272         this.curve_data.push(newPointGroup);
42273         this.reset();
42274         this.strokeUpdate(e);
42275     },
42276     
42277     strokeUpdate: function(e)
42278     {
42279         var rect = this.canvasEl().dom.getBoundingClientRect();
42280         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42281         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42282         var lastPoints = lastPointGroup.points;
42283         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42284         var isLastPointTooClose = lastPoint
42285             ? point.distanceTo(lastPoint) <= this.min_distance
42286             : false;
42287         var color = lastPointGroup.color;
42288         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42289             var curve = this.addPoint(point);
42290             if (!lastPoint) {
42291                 this.drawDot({color: color, point: point});
42292             }
42293             else if (curve) {
42294                 this.drawCurve({color: color, curve: curve});
42295             }
42296             lastPoints.push({
42297                 time: point.time,
42298                 x: point.x,
42299                 y: point.y
42300             });
42301         }
42302     },
42303     
42304     strokeEnd: function(e)
42305     {
42306         this.strokeUpdate(e);
42307         if (typeof this.onEnd === 'function') {
42308             this.onEnd(e);
42309         }
42310     },
42311     
42312     addPoint:  function (point) {
42313         var _lastPoints = this._lastPoints;
42314         _lastPoints.push(point);
42315         if (_lastPoints.length > 2) {
42316             if (_lastPoints.length === 3) {
42317                 _lastPoints.unshift(_lastPoints[0]);
42318             }
42319             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42320             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42321             _lastPoints.shift();
42322             return curve;
42323         }
42324         return null;
42325     },
42326     
42327     calculateCurveWidths: function (startPoint, endPoint) {
42328         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42329             (1 - this.velocity_filter_weight) * this._lastVelocity;
42330
42331         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42332         var widths = {
42333             end: newWidth,
42334             start: this._lastWidth
42335         };
42336         
42337         this._lastVelocity = velocity;
42338         this._lastWidth = newWidth;
42339         return widths;
42340     },
42341     
42342     drawDot: function (_a) {
42343         var color = _a.color, point = _a.point;
42344         var ctx = this.canvasElCtx();
42345         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42346         ctx.beginPath();
42347         this.drawCurveSegment(point.x, point.y, width);
42348         ctx.closePath();
42349         ctx.fillStyle = color;
42350         ctx.fill();
42351     },
42352     
42353     drawCurve: function (_a) {
42354         var color = _a.color, curve = _a.curve;
42355         var ctx = this.canvasElCtx();
42356         var widthDelta = curve.endWidth - curve.startWidth;
42357         var drawSteps = Math.floor(curve.length()) * 2;
42358         ctx.beginPath();
42359         ctx.fillStyle = color;
42360         for (var i = 0; i < drawSteps; i += 1) {
42361         var t = i / drawSteps;
42362         var tt = t * t;
42363         var ttt = tt * t;
42364         var u = 1 - t;
42365         var uu = u * u;
42366         var uuu = uu * u;
42367         var x = uuu * curve.startPoint.x;
42368         x += 3 * uu * t * curve.control1.x;
42369         x += 3 * u * tt * curve.control2.x;
42370         x += ttt * curve.endPoint.x;
42371         var y = uuu * curve.startPoint.y;
42372         y += 3 * uu * t * curve.control1.y;
42373         y += 3 * u * tt * curve.control2.y;
42374         y += ttt * curve.endPoint.y;
42375         var width = curve.startWidth + ttt * widthDelta;
42376         this.drawCurveSegment(x, y, width);
42377         }
42378         ctx.closePath();
42379         ctx.fill();
42380     },
42381     
42382     drawCurveSegment: function (x, y, width) {
42383         var ctx = this.canvasElCtx();
42384         ctx.moveTo(x, y);
42385         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42386         this.is_empty = false;
42387     },
42388     
42389     clear: function()
42390     {
42391         var ctx = this.canvasElCtx();
42392         var canvas = this.canvasEl().dom;
42393         ctx.fillStyle = this.bg_color;
42394         ctx.clearRect(0, 0, canvas.width, canvas.height);
42395         ctx.fillRect(0, 0, canvas.width, canvas.height);
42396         this.curve_data = [];
42397         this.reset();
42398         this.is_empty = true;
42399     },
42400     
42401     fileEl: function()
42402     {
42403         return  this.el.select('input',true).first();
42404     },
42405     
42406     canvasEl: function()
42407     {
42408         return this.el.select('canvas',true).first();
42409     },
42410     
42411     canvasElCtx: function()
42412     {
42413         return this.el.select('canvas',true).first().dom.getContext('2d');
42414     },
42415     
42416     getImage: function(type)
42417     {
42418         if(this.is_empty) {
42419             return false;
42420         }
42421         
42422         // encryption ?
42423         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42424     },
42425     
42426     drawFromImage: function(img_src)
42427     {
42428         var img = new Image();
42429         
42430         img.onload = function(){
42431             this.canvasElCtx().drawImage(img, 0, 0);
42432         }.bind(this);
42433         
42434         img.src = img_src;
42435         
42436         this.is_empty = false;
42437     },
42438     
42439     selectImage: function()
42440     {
42441         this.fileEl().dom.click();
42442     },
42443     
42444     uploadImage: function(e)
42445     {
42446         var reader = new FileReader();
42447         
42448         reader.onload = function(e){
42449             var img = new Image();
42450             img.onload = function(){
42451                 this.reset();
42452                 this.canvasElCtx().drawImage(img, 0, 0);
42453             }.bind(this);
42454             img.src = e.target.result;
42455         }.bind(this);
42456         
42457         reader.readAsDataURL(e.target.files[0]);
42458     },
42459     
42460     // Bezier Point Constructor
42461     Point: (function () {
42462         function Point(x, y, time) {
42463             this.x = x;
42464             this.y = y;
42465             this.time = time || Date.now();
42466         }
42467         Point.prototype.distanceTo = function (start) {
42468             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42469         };
42470         Point.prototype.equals = function (other) {
42471             return this.x === other.x && this.y === other.y && this.time === other.time;
42472         };
42473         Point.prototype.velocityFrom = function (start) {
42474             return this.time !== start.time
42475             ? this.distanceTo(start) / (this.time - start.time)
42476             : 0;
42477         };
42478         return Point;
42479     }()),
42480     
42481     
42482     // Bezier Constructor
42483     Bezier: (function () {
42484         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42485             this.startPoint = startPoint;
42486             this.control2 = control2;
42487             this.control1 = control1;
42488             this.endPoint = endPoint;
42489             this.startWidth = startWidth;
42490             this.endWidth = endWidth;
42491         }
42492         Bezier.fromPoints = function (points, widths, scope) {
42493             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42494             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42495             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42496         };
42497         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42498             var dx1 = s1.x - s2.x;
42499             var dy1 = s1.y - s2.y;
42500             var dx2 = s2.x - s3.x;
42501             var dy2 = s2.y - s3.y;
42502             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42503             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42504             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42505             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42506             var dxm = m1.x - m2.x;
42507             var dym = m1.y - m2.y;
42508             var k = l2 / (l1 + l2);
42509             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42510             var tx = s2.x - cm.x;
42511             var ty = s2.y - cm.y;
42512             return {
42513                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42514                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42515             };
42516         };
42517         Bezier.prototype.length = function () {
42518             var steps = 10;
42519             var length = 0;
42520             var px;
42521             var py;
42522             for (var i = 0; i <= steps; i += 1) {
42523                 var t = i / steps;
42524                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42525                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42526                 if (i > 0) {
42527                     var xdiff = cx - px;
42528                     var ydiff = cy - py;
42529                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42530                 }
42531                 px = cx;
42532                 py = cy;
42533             }
42534             return length;
42535         };
42536         Bezier.prototype.point = function (t, start, c1, c2, end) {
42537             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42538             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42539             + (3.0 * c2 * (1.0 - t) * t * t)
42540             + (end * t * t * t);
42541         };
42542         return Bezier;
42543     }()),
42544     
42545     throttleStroke: function(fn, wait) {
42546       if (wait === void 0) { wait = 250; }
42547       var previous = 0;
42548       var timeout = null;
42549       var result;
42550       var storedContext;
42551       var storedArgs;
42552       var later = function () {
42553           previous = Date.now();
42554           timeout = null;
42555           result = fn.apply(storedContext, storedArgs);
42556           if (!timeout) {
42557               storedContext = null;
42558               storedArgs = [];
42559           }
42560       };
42561       return function wrapper() {
42562           var args = [];
42563           for (var _i = 0; _i < arguments.length; _i++) {
42564               args[_i] = arguments[_i];
42565           }
42566           var now = Date.now();
42567           var remaining = wait - (now - previous);
42568           storedContext = this;
42569           storedArgs = args;
42570           if (remaining <= 0 || remaining > wait) {
42571               if (timeout) {
42572                   clearTimeout(timeout);
42573                   timeout = null;
42574               }
42575               previous = now;
42576               result = fn.apply(storedContext, storedArgs);
42577               if (!timeout) {
42578                   storedContext = null;
42579                   storedArgs = [];
42580               }
42581           }
42582           else if (!timeout) {
42583               timeout = window.setTimeout(later, remaining);
42584           }
42585           return result;
42586       };
42587   }
42588   
42589 });
42590
42591  
42592
42593