e47078400588da7f4a4fc4caffa376092743e37f
[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} 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         }
1681         if (this.header_image.length) {
1682             cfg.cn.push({
1683                 tag : 'img',
1684                 cls : 'card-img-top',
1685                 src: this.header_image // escape?
1686             });
1687         }
1688         
1689         var body = {
1690             tag : 'div',
1691             cls : 'card-body',
1692             cn : []
1693         };
1694         cfg.cn.push(body);
1695         
1696         if (this.title.length) {
1697             body.cn.push({
1698                 tag : 'div',
1699                 cls : 'card-title',
1700                 src: this.title // escape?
1701             });
1702         }
1703         
1704         if (this.subtitle.length) {
1705             body.cn.push({
1706                 tag : 'div',
1707                 cls : 'card-title',
1708                 src: this.subtitle // escape?
1709             });
1710         }
1711         
1712         body.cn.push({
1713             tag : 'div',
1714             cls : 'roo-card-body-ctr'
1715         });
1716         
1717         if (this.html.length) {
1718             body.cn.push({
1719                 tag: 'div',
1720                 html : this.html
1721             });
1722         }
1723         // fixme ? handle objects?
1724         if (this.footer.length) {
1725             cfg.cn.push({
1726                 tag : 'div',
1727                 cls : 'card-footer',
1728                 html: this.footer // escape?
1729             });
1730         }
1731         // footer...
1732         
1733         return cfg;
1734     },
1735     
1736     
1737     getChildContainer : function()
1738     {
1739         
1740         if(!this.el){
1741             return false;
1742         }
1743         return this.el.select('.roo-card-body-ctr',true).first();    
1744     },
1745     
1746     initEvents: function() 
1747     {
1748         if(this.dragable){
1749              this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1750                     containerScroll: true,
1751                     ddGroup: this.drag_group || 'default_card_drag_group'
1752             });
1753             this.dragZone.getDragData = this.getDragData.createDelegate(this);
1754         }
1755         
1756         
1757         
1758     },
1759     getDragData : function(e) {
1760         var target = this.getEl();
1761         if (target) {
1762             //this.handleSelection(e);
1763             
1764             var dragData = {
1765                 source: this,
1766                 copy: false,
1767                 nodes: this.getEl(),
1768                 records: []
1769             };
1770             
1771             
1772             dragData.ddel = target.dom ;        // the div element
1773             Roo.log(target.getWidth( ));
1774              dragData.ddel.style.width = target.getWidth() + 'px';
1775             
1776             return dragData;
1777         }
1778         return false;
1779     }
1780     
1781 });
1782
1783 /*
1784  * - LGPL
1785  *
1786  * image
1787  * 
1788  */
1789
1790
1791 /**
1792  * @class Roo.bootstrap.Img
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Img class
1795  * @cfg {Boolean} imgResponsive false | true
1796  * @cfg {String} border rounded | circle | thumbnail
1797  * @cfg {String} src image source
1798  * @cfg {String} alt image alternative text
1799  * @cfg {String} href a tag href
1800  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1801  * @cfg {String} xsUrl xs image source
1802  * @cfg {String} smUrl sm image source
1803  * @cfg {String} mdUrl md image source
1804  * @cfg {String} lgUrl lg image source
1805  * 
1806  * @constructor
1807  * Create a new Input
1808  * @param {Object} config The config object
1809  */
1810
1811 Roo.bootstrap.Img = function(config){
1812     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1813     
1814     this.addEvents({
1815         // img events
1816         /**
1817          * @event click
1818          * The img click event for the img.
1819          * @param {Roo.EventObject} e
1820          */
1821         "click" : true
1822     });
1823 };
1824
1825 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1826     
1827     imgResponsive: true,
1828     border: '',
1829     src: 'about:blank',
1830     href: false,
1831     target: false,
1832     xsUrl: '',
1833     smUrl: '',
1834     mdUrl: '',
1835     lgUrl: '',
1836
1837     getAutoCreate : function()
1838     {   
1839         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1840             return this.createSingleImg();
1841         }
1842         
1843         var cfg = {
1844             tag: 'div',
1845             cls: 'roo-image-responsive-group',
1846             cn: []
1847         };
1848         var _this = this;
1849         
1850         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1851             
1852             if(!_this[size + 'Url']){
1853                 return;
1854             }
1855             
1856             var img = {
1857                 tag: 'img',
1858                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1859                 html: _this.html || cfg.html,
1860                 src: _this[size + 'Url']
1861             };
1862             
1863             img.cls += ' roo-image-responsive-' + size;
1864             
1865             var s = ['xs', 'sm', 'md', 'lg'];
1866             
1867             s.splice(s.indexOf(size), 1);
1868             
1869             Roo.each(s, function(ss){
1870                 img.cls += ' hidden-' + ss;
1871             });
1872             
1873             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1874                 cfg.cls += ' img-' + _this.border;
1875             }
1876             
1877             if(_this.alt){
1878                 cfg.alt = _this.alt;
1879             }
1880             
1881             if(_this.href){
1882                 var a = {
1883                     tag: 'a',
1884                     href: _this.href,
1885                     cn: [
1886                         img
1887                     ]
1888                 };
1889
1890                 if(this.target){
1891                     a.target = _this.target;
1892                 }
1893             }
1894             
1895             cfg.cn.push((_this.href) ? a : img);
1896             
1897         });
1898         
1899         return cfg;
1900     },
1901     
1902     createSingleImg : function()
1903     {
1904         var cfg = {
1905             tag: 'img',
1906             cls: (this.imgResponsive) ? 'img-responsive' : '',
1907             html : null,
1908             src : 'about:blank'  // just incase src get's set to undefined?!?
1909         };
1910         
1911         cfg.html = this.html || cfg.html;
1912         
1913         cfg.src = this.src || cfg.src;
1914         
1915         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1916             cfg.cls += ' img-' + this.border;
1917         }
1918         
1919         if(this.alt){
1920             cfg.alt = this.alt;
1921         }
1922         
1923         if(this.href){
1924             var a = {
1925                 tag: 'a',
1926                 href: this.href,
1927                 cn: [
1928                     cfg
1929                 ]
1930             };
1931             
1932             if(this.target){
1933                 a.target = this.target;
1934             }
1935             
1936         }
1937         
1938         return (this.href) ? a : cfg;
1939     },
1940     
1941     initEvents: function() 
1942     {
1943         if(!this.href){
1944             this.el.on('click', this.onClick, this);
1945         }
1946         
1947     },
1948     
1949     onClick : function(e)
1950     {
1951         Roo.log('img onclick');
1952         this.fireEvent('click', this, e);
1953     },
1954     /**
1955      * Sets the url of the image - used to update it
1956      * @param {String} url the url of the image
1957      */
1958     
1959     setSrc : function(url)
1960     {
1961         this.src =  url;
1962         
1963         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1964             this.el.dom.src =  url;
1965             return;
1966         }
1967         
1968         this.el.select('img', true).first().dom.src =  url;
1969     }
1970     
1971     
1972    
1973 });
1974
1975  /*
1976  * - LGPL
1977  *
1978  * image
1979  * 
1980  */
1981
1982
1983 /**
1984  * @class Roo.bootstrap.Link
1985  * @extends Roo.bootstrap.Component
1986  * Bootstrap Link Class
1987  * @cfg {String} alt image alternative text
1988  * @cfg {String} href a tag href
1989  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1990  * @cfg {String} html the content of the link.
1991  * @cfg {String} anchor name for the anchor link
1992  * @cfg {String} fa - favicon
1993
1994  * @cfg {Boolean} preventDefault (true | false) default false
1995
1996  * 
1997  * @constructor
1998  * Create a new Input
1999  * @param {Object} config The config object
2000  */
2001
2002 Roo.bootstrap.Link = function(config){
2003     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2004     
2005     this.addEvents({
2006         // img events
2007         /**
2008          * @event click
2009          * The img click event for the img.
2010          * @param {Roo.EventObject} e
2011          */
2012         "click" : true
2013     });
2014 };
2015
2016 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2017     
2018     href: false,
2019     target: false,
2020     preventDefault: false,
2021     anchor : false,
2022     alt : false,
2023     fa: false,
2024
2025
2026     getAutoCreate : function()
2027     {
2028         var html = this.html || '';
2029         
2030         if (this.fa !== false) {
2031             html = '<i class="fa fa-' + this.fa + '"></i>';
2032         }
2033         var cfg = {
2034             tag: 'a'
2035         };
2036         // anchor's do not require html/href...
2037         if (this.anchor === false) {
2038             cfg.html = html;
2039             cfg.href = this.href || '#';
2040         } else {
2041             cfg.name = this.anchor;
2042             if (this.html !== false || this.fa !== false) {
2043                 cfg.html = html;
2044             }
2045             if (this.href !== false) {
2046                 cfg.href = this.href;
2047             }
2048         }
2049         
2050         if(this.alt !== false){
2051             cfg.alt = this.alt;
2052         }
2053         
2054         
2055         if(this.target !== false) {
2056             cfg.target = this.target;
2057         }
2058         
2059         return cfg;
2060     },
2061     
2062     initEvents: function() {
2063         
2064         if(!this.href || this.preventDefault){
2065             this.el.on('click', this.onClick, this);
2066         }
2067     },
2068     
2069     onClick : function(e)
2070     {
2071         if(this.preventDefault){
2072             e.preventDefault();
2073         }
2074         //Roo.log('img onclick');
2075         this.fireEvent('click', this, e);
2076     }
2077    
2078 });
2079
2080  /*
2081  * - LGPL
2082  *
2083  * header
2084  * 
2085  */
2086
2087 /**
2088  * @class Roo.bootstrap.Header
2089  * @extends Roo.bootstrap.Component
2090  * Bootstrap Header class
2091  * @cfg {String} html content of header
2092  * @cfg {Number} level (1|2|3|4|5|6) default 1
2093  * 
2094  * @constructor
2095  * Create a new Header
2096  * @param {Object} config The config object
2097  */
2098
2099
2100 Roo.bootstrap.Header  = function(config){
2101     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2102 };
2103
2104 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2105     
2106     //href : false,
2107     html : false,
2108     level : 1,
2109     
2110     
2111     
2112     getAutoCreate : function(){
2113         
2114         
2115         
2116         var cfg = {
2117             tag: 'h' + (1 *this.level),
2118             html: this.html || ''
2119         } ;
2120         
2121         return cfg;
2122     }
2123    
2124 });
2125
2126  
2127
2128  /*
2129  * Based on:
2130  * Ext JS Library 1.1.1
2131  * Copyright(c) 2006-2007, Ext JS, LLC.
2132  *
2133  * Originally Released Under LGPL - original licence link has changed is not relivant.
2134  *
2135  * Fork - LGPL
2136  * <script type="text/javascript">
2137  */
2138  
2139 /**
2140  * @class Roo.bootstrap.MenuMgr
2141  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2142  * @singleton
2143  */
2144 Roo.bootstrap.MenuMgr = function(){
2145    var menus, active, groups = {}, attached = false, lastShow = new Date();
2146
2147    // private - called when first menu is created
2148    function init(){
2149        menus = {};
2150        active = new Roo.util.MixedCollection();
2151        Roo.get(document).addKeyListener(27, function(){
2152            if(active.length > 0){
2153                hideAll();
2154            }
2155        });
2156    }
2157
2158    // private
2159    function hideAll(){
2160        if(active && active.length > 0){
2161            var c = active.clone();
2162            c.each(function(m){
2163                m.hide();
2164            });
2165        }
2166    }
2167
2168    // private
2169    function onHide(m){
2170        active.remove(m);
2171        if(active.length < 1){
2172            Roo.get(document).un("mouseup", onMouseDown);
2173             
2174            attached = false;
2175        }
2176    }
2177
2178    // private
2179    function onShow(m){
2180        var last = active.last();
2181        lastShow = new Date();
2182        active.add(m);
2183        if(!attached){
2184           Roo.get(document).on("mouseup", onMouseDown);
2185            
2186            attached = true;
2187        }
2188        if(m.parentMenu){
2189           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2190           m.parentMenu.activeChild = m;
2191        }else if(last && last.isVisible()){
2192           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2193        }
2194    }
2195
2196    // private
2197    function onBeforeHide(m){
2198        if(m.activeChild){
2199            m.activeChild.hide();
2200        }
2201        if(m.autoHideTimer){
2202            clearTimeout(m.autoHideTimer);
2203            delete m.autoHideTimer;
2204        }
2205    }
2206
2207    // private
2208    function onBeforeShow(m){
2209        var pm = m.parentMenu;
2210        if(!pm && !m.allowOtherMenus){
2211            hideAll();
2212        }else if(pm && pm.activeChild && active != m){
2213            pm.activeChild.hide();
2214        }
2215    }
2216
2217    // private this should really trigger on mouseup..
2218    function onMouseDown(e){
2219         Roo.log("on Mouse Up");
2220         
2221         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2222             Roo.log("MenuManager hideAll");
2223             hideAll();
2224             e.stopEvent();
2225         }
2226         
2227         
2228    }
2229
2230    // private
2231    function onBeforeCheck(mi, state){
2232        if(state){
2233            var g = groups[mi.group];
2234            for(var i = 0, l = g.length; i < l; i++){
2235                if(g[i] != mi){
2236                    g[i].setChecked(false);
2237                }
2238            }
2239        }
2240    }
2241
2242    return {
2243
2244        /**
2245         * Hides all menus that are currently visible
2246         */
2247        hideAll : function(){
2248             hideAll();  
2249        },
2250
2251        // private
2252        register : function(menu){
2253            if(!menus){
2254                init();
2255            }
2256            menus[menu.id] = menu;
2257            menu.on("beforehide", onBeforeHide);
2258            menu.on("hide", onHide);
2259            menu.on("beforeshow", onBeforeShow);
2260            menu.on("show", onShow);
2261            var g = menu.group;
2262            if(g && menu.events["checkchange"]){
2263                if(!groups[g]){
2264                    groups[g] = [];
2265                }
2266                groups[g].push(menu);
2267                menu.on("checkchange", onCheck);
2268            }
2269        },
2270
2271         /**
2272          * Returns a {@link Roo.menu.Menu} object
2273          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2274          * be used to generate and return a new Menu instance.
2275          */
2276        get : function(menu){
2277            if(typeof menu == "string"){ // menu id
2278                return menus[menu];
2279            }else if(menu.events){  // menu instance
2280                return menu;
2281            }
2282            /*else if(typeof menu.length == 'number'){ // array of menu items?
2283                return new Roo.bootstrap.Menu({items:menu});
2284            }else{ // otherwise, must be a config
2285                return new Roo.bootstrap.Menu(menu);
2286            }
2287            */
2288            return false;
2289        },
2290
2291        // private
2292        unregister : function(menu){
2293            delete menus[menu.id];
2294            menu.un("beforehide", onBeforeHide);
2295            menu.un("hide", onHide);
2296            menu.un("beforeshow", onBeforeShow);
2297            menu.un("show", onShow);
2298            var g = menu.group;
2299            if(g && menu.events["checkchange"]){
2300                groups[g].remove(menu);
2301                menu.un("checkchange", onCheck);
2302            }
2303        },
2304
2305        // private
2306        registerCheckable : function(menuItem){
2307            var g = menuItem.group;
2308            if(g){
2309                if(!groups[g]){
2310                    groups[g] = [];
2311                }
2312                groups[g].push(menuItem);
2313                menuItem.on("beforecheckchange", onBeforeCheck);
2314            }
2315        },
2316
2317        // private
2318        unregisterCheckable : function(menuItem){
2319            var g = menuItem.group;
2320            if(g){
2321                groups[g].remove(menuItem);
2322                menuItem.un("beforecheckchange", onBeforeCheck);
2323            }
2324        }
2325    };
2326 }();/*
2327  * - LGPL
2328  *
2329  * menu
2330  * 
2331  */
2332
2333 /**
2334  * @class Roo.bootstrap.Menu
2335  * @extends Roo.bootstrap.Component
2336  * Bootstrap Menu class - container for MenuItems
2337  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2338  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2339  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2340  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2341  * 
2342  * @constructor
2343  * Create a new Menu
2344  * @param {Object} config The config object
2345  */
2346
2347
2348 Roo.bootstrap.Menu = function(config){
2349     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2350     if (this.registerMenu && this.type != 'treeview')  {
2351         Roo.bootstrap.MenuMgr.register(this);
2352     }
2353     
2354     
2355     this.addEvents({
2356         /**
2357          * @event beforeshow
2358          * Fires before this menu is displayed (return false to block)
2359          * @param {Roo.menu.Menu} this
2360          */
2361         beforeshow : true,
2362         /**
2363          * @event beforehide
2364          * Fires before this menu is hidden (return false to block)
2365          * @param {Roo.menu.Menu} this
2366          */
2367         beforehide : true,
2368         /**
2369          * @event show
2370          * Fires after this menu is displayed
2371          * @param {Roo.menu.Menu} this
2372          */
2373         show : true,
2374         /**
2375          * @event hide
2376          * Fires after this menu is hidden
2377          * @param {Roo.menu.Menu} this
2378          */
2379         hide : true,
2380         /**
2381          * @event click
2382          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2383          * @param {Roo.menu.Menu} this
2384          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2385          * @param {Roo.EventObject} e
2386          */
2387         click : true,
2388         /**
2389          * @event mouseover
2390          * Fires when the mouse is hovering over this menu
2391          * @param {Roo.menu.Menu} this
2392          * @param {Roo.EventObject} e
2393          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2394          */
2395         mouseover : true,
2396         /**
2397          * @event mouseout
2398          * Fires when the mouse exits this menu
2399          * @param {Roo.menu.Menu} this
2400          * @param {Roo.EventObject} e
2401          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2402          */
2403         mouseout : true,
2404         /**
2405          * @event itemclick
2406          * Fires when a menu item contained in this menu is clicked
2407          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2408          * @param {Roo.EventObject} e
2409          */
2410         itemclick: true
2411     });
2412     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2413 };
2414
2415 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2416     
2417    /// html : false,
2418     //align : '',
2419     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2420     type: false,
2421     /**
2422      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2423      */
2424     registerMenu : true,
2425     
2426     menuItems :false, // stores the menu items..
2427     
2428     hidden:true,
2429         
2430     parentMenu : false,
2431     
2432     stopEvent : true,
2433     
2434     isLink : false,
2435     
2436     getChildContainer : function() {
2437         return this.el;  
2438     },
2439     
2440     getAutoCreate : function(){
2441          
2442         //if (['right'].indexOf(this.align)!==-1) {
2443         //    cfg.cn[1].cls += ' pull-right'
2444         //}
2445         
2446         
2447         var cfg = {
2448             tag : 'ul',
2449             cls : 'dropdown-menu' ,
2450             style : 'z-index:1000'
2451             
2452         };
2453         
2454         if (this.type === 'submenu') {
2455             cfg.cls = 'submenu active';
2456         }
2457         if (this.type === 'treeview') {
2458             cfg.cls = 'treeview-menu';
2459         }
2460         
2461         return cfg;
2462     },
2463     initEvents : function() {
2464         
2465        // Roo.log("ADD event");
2466        // Roo.log(this.triggerEl.dom);
2467         
2468         this.triggerEl.on('click', this.onTriggerClick, this);
2469         
2470         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2471         
2472         
2473         if (this.triggerEl.hasClass('nav-item')) {
2474             // dropdown toggle on the 'a' in BS4?
2475             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2476         } else {
2477             this.triggerEl.addClass('dropdown-toggle');
2478         }
2479         if (Roo.isTouch) {
2480             this.el.on('touchstart'  , this.onTouch, this);
2481         }
2482         this.el.on('click' , this.onClick, this);
2483
2484         this.el.on("mouseover", this.onMouseOver, this);
2485         this.el.on("mouseout", this.onMouseOut, this);
2486         
2487     },
2488     
2489     findTargetItem : function(e)
2490     {
2491         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2492         if(!t){
2493             return false;
2494         }
2495         //Roo.log(t);         Roo.log(t.id);
2496         if(t && t.id){
2497             //Roo.log(this.menuitems);
2498             return this.menuitems.get(t.id);
2499             
2500             //return this.items.get(t.menuItemId);
2501         }
2502         
2503         return false;
2504     },
2505     
2506     onTouch : function(e) 
2507     {
2508         Roo.log("menu.onTouch");
2509         //e.stopEvent(); this make the user popdown broken
2510         this.onClick(e);
2511     },
2512     
2513     onClick : function(e)
2514     {
2515         Roo.log("menu.onClick");
2516         
2517         var t = this.findTargetItem(e);
2518         if(!t || t.isContainer){
2519             return;
2520         }
2521         Roo.log(e);
2522         /*
2523         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2524             if(t == this.activeItem && t.shouldDeactivate(e)){
2525                 this.activeItem.deactivate();
2526                 delete this.activeItem;
2527                 return;
2528             }
2529             if(t.canActivate){
2530                 this.setActiveItem(t, true);
2531             }
2532             return;
2533             
2534             
2535         }
2536         */
2537        
2538         Roo.log('pass click event');
2539         
2540         t.onClick(e);
2541         
2542         this.fireEvent("click", this, t, e);
2543         
2544         var _this = this;
2545         
2546         if(!t.href.length || t.href == '#'){
2547             (function() { _this.hide(); }).defer(100);
2548         }
2549         
2550     },
2551     
2552     onMouseOver : function(e){
2553         var t  = this.findTargetItem(e);
2554         //Roo.log(t);
2555         //if(t){
2556         //    if(t.canActivate && !t.disabled){
2557         //        this.setActiveItem(t, true);
2558         //    }
2559         //}
2560         
2561         this.fireEvent("mouseover", this, e, t);
2562     },
2563     isVisible : function(){
2564         return !this.hidden;
2565     },
2566     onMouseOut : function(e){
2567         var t  = this.findTargetItem(e);
2568         
2569         //if(t ){
2570         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2571         //        this.activeItem.deactivate();
2572         //        delete this.activeItem;
2573         //    }
2574         //}
2575         this.fireEvent("mouseout", this, e, t);
2576     },
2577     
2578     
2579     /**
2580      * Displays this menu relative to another element
2581      * @param {String/HTMLElement/Roo.Element} element The element to align to
2582      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2583      * the element (defaults to this.defaultAlign)
2584      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2585      */
2586     show : function(el, pos, parentMenu)
2587     {
2588         if (false === this.fireEvent("beforeshow", this)) {
2589             Roo.log("show canceled");
2590             return;
2591         }
2592         this.parentMenu = parentMenu;
2593         if(!this.el){
2594             this.render();
2595         }
2596         
2597         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2598     },
2599      /**
2600      * Displays this menu at a specific xy position
2601      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2602      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2603      */
2604     showAt : function(xy, parentMenu, /* private: */_e){
2605         this.parentMenu = parentMenu;
2606         if(!this.el){
2607             this.render();
2608         }
2609         if(_e !== false){
2610             this.fireEvent("beforeshow", this);
2611             //xy = this.el.adjustForConstraints(xy);
2612         }
2613         
2614         //this.el.show();
2615         this.hideMenuItems();
2616         this.hidden = false;
2617         this.triggerEl.addClass('open');
2618         this.el.addClass('show');
2619         
2620         // reassign x when hitting right
2621         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2622             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2623         }
2624         
2625         // reassign y when hitting bottom
2626         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2627             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2628         }
2629         
2630         // but the list may align on trigger left or trigger top... should it be a properity?
2631         
2632         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2633             this.el.setXY(xy);
2634         }
2635         
2636         this.focus();
2637         this.fireEvent("show", this);
2638     },
2639     
2640     focus : function(){
2641         return;
2642         if(!this.hidden){
2643             this.doFocus.defer(50, this);
2644         }
2645     },
2646
2647     doFocus : function(){
2648         if(!this.hidden){
2649             this.focusEl.focus();
2650         }
2651     },
2652
2653     /**
2654      * Hides this menu and optionally all parent menus
2655      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2656      */
2657     hide : function(deep)
2658     {
2659         if (false === this.fireEvent("beforehide", this)) {
2660             Roo.log("hide canceled");
2661             return;
2662         }
2663         this.hideMenuItems();
2664         if(this.el && this.isVisible()){
2665            
2666             if(this.activeItem){
2667                 this.activeItem.deactivate();
2668                 this.activeItem = null;
2669             }
2670             this.triggerEl.removeClass('open');;
2671             this.el.removeClass('show');
2672             this.hidden = true;
2673             this.fireEvent("hide", this);
2674         }
2675         if(deep === true && this.parentMenu){
2676             this.parentMenu.hide(true);
2677         }
2678     },
2679     
2680     onTriggerClick : function(e)
2681     {
2682         Roo.log('trigger click');
2683         
2684         var target = e.getTarget();
2685         
2686         Roo.log(target.nodeName.toLowerCase());
2687         
2688         if(target.nodeName.toLowerCase() === 'i'){
2689             e.preventDefault();
2690         }
2691         
2692     },
2693     
2694     onTriggerPress  : function(e)
2695     {
2696         Roo.log('trigger press');
2697         //Roo.log(e.getTarget());
2698        // Roo.log(this.triggerEl.dom);
2699        
2700         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2701         var pel = Roo.get(e.getTarget());
2702         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2703             Roo.log('is treeview or dropdown?');
2704             return;
2705         }
2706         
2707         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2708             return;
2709         }
2710         
2711         if (this.isVisible()) {
2712             Roo.log('hide');
2713             this.hide();
2714         } else {
2715             Roo.log('show');
2716             this.show(this.triggerEl, '?', false);
2717         }
2718         
2719         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2720             e.stopEvent();
2721         }
2722         
2723     },
2724        
2725     
2726     hideMenuItems : function()
2727     {
2728         Roo.log("hide Menu Items");
2729         if (!this.el) { 
2730             return;
2731         }
2732         
2733         this.el.select('.open',true).each(function(aa) {
2734             
2735             aa.removeClass('open');
2736          
2737         });
2738     },
2739     addxtypeChild : function (tree, cntr) {
2740         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2741           
2742         this.menuitems.add(comp);
2743         return comp;
2744
2745     },
2746     getEl : function()
2747     {
2748         Roo.log(this.el);
2749         return this.el;
2750     },
2751     
2752     clear : function()
2753     {
2754         this.getEl().dom.innerHTML = '';
2755         this.menuitems.clear();
2756     }
2757 });
2758
2759  
2760  /*
2761  * - LGPL
2762  *
2763  * menu item
2764  * 
2765  */
2766
2767
2768 /**
2769  * @class Roo.bootstrap.MenuItem
2770  * @extends Roo.bootstrap.Component
2771  * Bootstrap MenuItem class
2772  * @cfg {String} html the menu label
2773  * @cfg {String} href the link
2774  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2775  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2776  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2777  * @cfg {String} fa favicon to show on left of menu item.
2778  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2779  * 
2780  * 
2781  * @constructor
2782  * Create a new MenuItem
2783  * @param {Object} config The config object
2784  */
2785
2786
2787 Roo.bootstrap.MenuItem = function(config){
2788     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2789     this.addEvents({
2790         // raw events
2791         /**
2792          * @event click
2793          * The raw click event for the entire grid.
2794          * @param {Roo.bootstrap.MenuItem} this
2795          * @param {Roo.EventObject} e
2796          */
2797         "click" : true
2798     });
2799 };
2800
2801 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2802     
2803     href : false,
2804     html : false,
2805     preventDefault: false,
2806     isContainer : false,
2807     active : false,
2808     fa: false,
2809     
2810     getAutoCreate : function(){
2811         
2812         if(this.isContainer){
2813             return {
2814                 tag: 'li',
2815                 cls: 'dropdown-menu-item '
2816             };
2817         }
2818         var ctag = {
2819             tag: 'span',
2820             html: 'Link'
2821         };
2822         
2823         var anc = {
2824             tag : 'a',
2825             cls : 'dropdown-item',
2826             href : '#',
2827             cn : [  ]
2828         };
2829         
2830         if (this.fa !== false) {
2831             anc.cn.push({
2832                 tag : 'i',
2833                 cls : 'fa fa-' + this.fa
2834             });
2835         }
2836         
2837         anc.cn.push(ctag);
2838         
2839         
2840         var cfg= {
2841             tag: 'li',
2842             cls: 'dropdown-menu-item',
2843             cn: [ anc ]
2844         };
2845         if (this.parent().type == 'treeview') {
2846             cfg.cls = 'treeview-menu';
2847         }
2848         if (this.active) {
2849             cfg.cls += ' active';
2850         }
2851         
2852         
2853         
2854         anc.href = this.href || cfg.cn[0].href ;
2855         ctag.html = this.html || cfg.cn[0].html ;
2856         return cfg;
2857     },
2858     
2859     initEvents: function()
2860     {
2861         if (this.parent().type == 'treeview') {
2862             this.el.select('a').on('click', this.onClick, this);
2863         }
2864         
2865         if (this.menu) {
2866             this.menu.parentType = this.xtype;
2867             this.menu.triggerEl = this.el;
2868             this.menu = this.addxtype(Roo.apply({}, this.menu));
2869         }
2870         
2871     },
2872     onClick : function(e)
2873     {
2874         Roo.log('item on click ');
2875         
2876         if(this.preventDefault){
2877             e.preventDefault();
2878         }
2879         //this.parent().hideMenuItems();
2880         
2881         this.fireEvent('click', this, e);
2882     },
2883     getEl : function()
2884     {
2885         return this.el;
2886     } 
2887 });
2888
2889  
2890
2891  /*
2892  * - LGPL
2893  *
2894  * menu separator
2895  * 
2896  */
2897
2898
2899 /**
2900  * @class Roo.bootstrap.MenuSeparator
2901  * @extends Roo.bootstrap.Component
2902  * Bootstrap MenuSeparator class
2903  * 
2904  * @constructor
2905  * Create a new MenuItem
2906  * @param {Object} config The config object
2907  */
2908
2909
2910 Roo.bootstrap.MenuSeparator = function(config){
2911     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2912 };
2913
2914 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2915     
2916     getAutoCreate : function(){
2917         var cfg = {
2918             cls: 'divider',
2919             tag : 'li'
2920         };
2921         
2922         return cfg;
2923     }
2924    
2925 });
2926
2927  
2928
2929  
2930 /*
2931 * Licence: LGPL
2932 */
2933
2934 /**
2935  * @class Roo.bootstrap.Modal
2936  * @extends Roo.bootstrap.Component
2937  * Bootstrap Modal class
2938  * @cfg {String} title Title of dialog
2939  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2940  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2941  * @cfg {Boolean} specificTitle default false
2942  * @cfg {Array} buttons Array of buttons or standard button set..
2943  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2944  * @cfg {Boolean} animate default true
2945  * @cfg {Boolean} allow_close default true
2946  * @cfg {Boolean} fitwindow default false
2947  * @cfg {Number} width fixed width - usefull for chrome extension only really.
2948  * @cfg {Number} height fixed height - usefull for chrome extension only really.
2949  * @cfg {String} size (sm|lg) default empty
2950  * @cfg {Number} max_width set the max width of modal
2951  *
2952  *
2953  * @constructor
2954  * Create a new Modal Dialog
2955  * @param {Object} config The config object
2956  */
2957
2958 Roo.bootstrap.Modal = function(config){
2959     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2960     this.addEvents({
2961         // raw events
2962         /**
2963          * @event btnclick
2964          * The raw btnclick event for the button
2965          * @param {Roo.EventObject} e
2966          */
2967         "btnclick" : true,
2968         /**
2969          * @event resize
2970          * Fire when dialog resize
2971          * @param {Roo.bootstrap.Modal} this
2972          * @param {Roo.EventObject} e
2973          */
2974         "resize" : true
2975     });
2976     this.buttons = this.buttons || [];
2977
2978     if (this.tmpl) {
2979         this.tmpl = Roo.factory(this.tmpl);
2980     }
2981
2982 };
2983
2984 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2985
2986     title : 'test dialog',
2987
2988     buttons : false,
2989
2990     // set on load...
2991
2992     html: false,
2993
2994     tmp: false,
2995
2996     specificTitle: false,
2997
2998     buttonPosition: 'right',
2999
3000     allow_close : true,
3001
3002     animate : true,
3003
3004     fitwindow: false,
3005     
3006      // private
3007     dialogEl: false,
3008     bodyEl:  false,
3009     footerEl:  false,
3010     titleEl:  false,
3011     closeEl:  false,
3012
3013     size: '',
3014     
3015     max_width: 0,
3016     
3017     max_height: 0,
3018     
3019     fit_content: false,
3020
3021     onRender : function(ct, position)
3022     {
3023         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3024
3025         if(!this.el){
3026             var cfg = Roo.apply({},  this.getAutoCreate());
3027             cfg.id = Roo.id();
3028             //if(!cfg.name){
3029             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3030             //}
3031             //if (!cfg.name.length) {
3032             //    delete cfg.name;
3033            // }
3034             if (this.cls) {
3035                 cfg.cls += ' ' + this.cls;
3036             }
3037             if (this.style) {
3038                 cfg.style = this.style;
3039             }
3040             this.el = Roo.get(document.body).createChild(cfg, position);
3041         }
3042         //var type = this.el.dom.type;
3043
3044
3045         if(this.tabIndex !== undefined){
3046             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3047         }
3048
3049         this.dialogEl = this.el.select('.modal-dialog',true).first();
3050         this.bodyEl = this.el.select('.modal-body',true).first();
3051         this.closeEl = this.el.select('.modal-header .close', true).first();
3052         this.headerEl = this.el.select('.modal-header',true).first();
3053         this.titleEl = this.el.select('.modal-title',true).first();
3054         this.footerEl = this.el.select('.modal-footer',true).first();
3055
3056         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3057         
3058         //this.el.addClass("x-dlg-modal");
3059
3060         if (this.buttons.length) {
3061             Roo.each(this.buttons, function(bb) {
3062                 var b = Roo.apply({}, bb);
3063                 b.xns = b.xns || Roo.bootstrap;
3064                 b.xtype = b.xtype || 'Button';
3065                 if (typeof(b.listeners) == 'undefined') {
3066                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3067                 }
3068
3069                 var btn = Roo.factory(b);
3070
3071                 btn.render(this.getButtonContainer());
3072
3073             },this);
3074         }
3075         // render the children.
3076         var nitems = [];
3077
3078         if(typeof(this.items) != 'undefined'){
3079             var items = this.items;
3080             delete this.items;
3081
3082             for(var i =0;i < items.length;i++) {
3083                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3084             }
3085         }
3086
3087         this.items = nitems;
3088
3089         // where are these used - they used to be body/close/footer
3090
3091
3092         this.initEvents();
3093         //this.el.addClass([this.fieldClass, this.cls]);
3094
3095     },
3096
3097     getAutoCreate : function()
3098     {
3099         // we will default to modal-body-overflow - might need to remove or make optional later.
3100         var bdy = {
3101                 cls : 'modal-body enable-modal-body-overflow ', 
3102                 html : this.html || ''
3103         };
3104
3105         var title = {
3106             tag: 'h4',
3107             cls : 'modal-title',
3108             html : this.title
3109         };
3110
3111         if(this.specificTitle){
3112             title = this.title;
3113
3114         }
3115
3116         var header = [];
3117         if (this.allow_close && Roo.bootstrap.version == 3) {
3118             header.push({
3119                 tag: 'button',
3120                 cls : 'close',
3121                 html : '&times'
3122             });
3123         }
3124
3125         header.push(title);
3126
3127         if (this.allow_close && Roo.bootstrap.version == 4) {
3128             header.push({
3129                 tag: 'button',
3130                 cls : 'close',
3131                 html : '&times'
3132             });
3133         }
3134         
3135         var size = '';
3136
3137         if(this.size.length){
3138             size = 'modal-' + this.size;
3139         }
3140         
3141         var footer = Roo.bootstrap.version == 3 ?
3142             {
3143                 cls : 'modal-footer',
3144                 cn : [
3145                     {
3146                         tag: 'div',
3147                         cls: 'btn-' + this.buttonPosition
3148                     }
3149                 ]
3150
3151             } :
3152             {  // BS4 uses mr-auto on left buttons....
3153                 cls : 'modal-footer'
3154             };
3155
3156             
3157
3158         
3159         
3160         var modal = {
3161             cls: "modal",
3162              cn : [
3163                 {
3164                     cls: "modal-dialog " + size,
3165                     cn : [
3166                         {
3167                             cls : "modal-content",
3168                             cn : [
3169                                 {
3170                                     cls : 'modal-header',
3171                                     cn : header
3172                                 },
3173                                 bdy,
3174                                 footer
3175                             ]
3176
3177                         }
3178                     ]
3179
3180                 }
3181             ]
3182         };
3183
3184         if(this.animate){
3185             modal.cls += ' fade';
3186         }
3187
3188         return modal;
3189
3190     },
3191     getChildContainer : function() {
3192
3193          return this.bodyEl;
3194
3195     },
3196     getButtonContainer : function() {
3197         
3198          return Roo.bootstrap.version == 4 ?
3199             this.el.select('.modal-footer',true).first()
3200             : this.el.select('.modal-footer div',true).first();
3201
3202     },
3203     initEvents : function()
3204     {
3205         if (this.allow_close) {
3206             this.closeEl.on('click', this.hide, this);
3207         }
3208         Roo.EventManager.onWindowResize(this.resize, this, true);
3209
3210
3211     },
3212   
3213
3214     resize : function()
3215     {
3216         this.maskEl.setSize(
3217             Roo.lib.Dom.getViewWidth(true),
3218             Roo.lib.Dom.getViewHeight(true)
3219         );
3220         
3221         if (this.fitwindow) {
3222             
3223            
3224             this.setSize(
3225                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3226                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3227             );
3228             return;
3229         }
3230         
3231         if(this.max_width !== 0) {
3232             
3233             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3234             
3235             if(this.height) {
3236                 this.setSize(w, this.height);
3237                 return;
3238             }
3239             
3240             if(this.max_height) {
3241                 this.setSize(w,Math.min(
3242                     this.max_height,
3243                     Roo.lib.Dom.getViewportHeight(true) - 60
3244                 ));
3245                 
3246                 return;
3247             }
3248             
3249             if(!this.fit_content) {
3250                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3251                 return;
3252             }
3253             
3254             this.setSize(w, Math.min(
3255                 60 +
3256                 this.headerEl.getHeight() + 
3257                 this.footerEl.getHeight() + 
3258                 this.getChildHeight(this.bodyEl.dom.childNodes),
3259                 Roo.lib.Dom.getViewportHeight(true) - 60)
3260             );
3261         }
3262         
3263     },
3264
3265     setSize : function(w,h)
3266     {
3267         if (!w && !h) {
3268             return;
3269         }
3270         
3271         this.resizeTo(w,h);
3272     },
3273
3274     show : function() {
3275
3276         if (!this.rendered) {
3277             this.render();
3278         }
3279
3280         //this.el.setStyle('display', 'block');
3281         this.el.removeClass('hideing');
3282         this.el.dom.style.display='block';
3283         
3284         Roo.get(document.body).addClass('modal-open');
3285  
3286         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3287             
3288             (function(){
3289                 this.el.addClass('show');
3290                 this.el.addClass('in');
3291             }).defer(50, this);
3292         }else{
3293             this.el.addClass('show');
3294             this.el.addClass('in');
3295         }
3296
3297         // not sure how we can show data in here..
3298         //if (this.tmpl) {
3299         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3300         //}
3301
3302         Roo.get(document.body).addClass("x-body-masked");
3303         
3304         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3305         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3306         this.maskEl.dom.style.display = 'block';
3307         this.maskEl.addClass('show');
3308         
3309         
3310         this.resize();
3311         
3312         this.fireEvent('show', this);
3313
3314         // set zindex here - otherwise it appears to be ignored...
3315         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3316
3317         (function () {
3318             this.items.forEach( function(e) {
3319                 e.layout ? e.layout() : false;
3320
3321             });
3322         }).defer(100,this);
3323
3324     },
3325     hide : function()
3326     {
3327         if(this.fireEvent("beforehide", this) !== false){
3328             
3329             this.maskEl.removeClass('show');
3330             
3331             this.maskEl.dom.style.display = '';
3332             Roo.get(document.body).removeClass("x-body-masked");
3333             this.el.removeClass('in');
3334             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3335
3336             if(this.animate){ // why
3337                 this.el.addClass('hideing');
3338                 this.el.removeClass('show');
3339                 (function(){
3340                     if (!this.el.hasClass('hideing')) {
3341                         return; // it's been shown again...
3342                     }
3343                     
3344                     this.el.dom.style.display='';
3345
3346                     Roo.get(document.body).removeClass('modal-open');
3347                     this.el.removeClass('hideing');
3348                 }).defer(150,this);
3349                 
3350             }else{
3351                 this.el.removeClass('show');
3352                 this.el.dom.style.display='';
3353                 Roo.get(document.body).removeClass('modal-open');
3354
3355             }
3356             this.fireEvent('hide', this);
3357         }
3358     },
3359     isVisible : function()
3360     {
3361         
3362         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3363         
3364     },
3365
3366     addButton : function(str, cb)
3367     {
3368
3369
3370         var b = Roo.apply({}, { html : str } );
3371         b.xns = b.xns || Roo.bootstrap;
3372         b.xtype = b.xtype || 'Button';
3373         if (typeof(b.listeners) == 'undefined') {
3374             b.listeners = { click : cb.createDelegate(this)  };
3375         }
3376
3377         var btn = Roo.factory(b);
3378
3379         btn.render(this.getButtonContainer());
3380
3381         return btn;
3382
3383     },
3384
3385     setDefaultButton : function(btn)
3386     {
3387         //this.el.select('.modal-footer').()
3388     },
3389
3390     resizeTo: function(w,h)
3391     {
3392         this.dialogEl.setWidth(w);
3393         
3394         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3395
3396         this.bodyEl.setHeight(h - diff);
3397         
3398         this.fireEvent('resize', this);
3399     },
3400     
3401     setContentSize  : function(w, h)
3402     {
3403
3404     },
3405     onButtonClick: function(btn,e)
3406     {
3407         //Roo.log([a,b,c]);
3408         this.fireEvent('btnclick', btn.name, e);
3409     },
3410      /**
3411      * Set the title of the Dialog
3412      * @param {String} str new Title
3413      */
3414     setTitle: function(str) {
3415         this.titleEl.dom.innerHTML = str;
3416     },
3417     /**
3418      * Set the body of the Dialog
3419      * @param {String} str new Title
3420      */
3421     setBody: function(str) {
3422         this.bodyEl.dom.innerHTML = str;
3423     },
3424     /**
3425      * Set the body of the Dialog using the template
3426      * @param {Obj} data - apply this data to the template and replace the body contents.
3427      */
3428     applyBody: function(obj)
3429     {
3430         if (!this.tmpl) {
3431             Roo.log("Error - using apply Body without a template");
3432             //code
3433         }
3434         this.tmpl.overwrite(this.bodyEl, obj);
3435     },
3436     
3437     getChildHeight : function(child_nodes)
3438     {
3439         if(
3440             !child_nodes ||
3441             child_nodes.length == 0
3442         ) {
3443             return;
3444         }
3445         
3446         var child_height = 0;
3447         
3448         for(var i = 0; i < child_nodes.length; i++) {
3449             
3450             /*
3451             * for modal with tabs...
3452             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3453                 
3454                 var layout_childs = child_nodes[i].childNodes;
3455                 
3456                 for(var j = 0; j < layout_childs.length; j++) {
3457                     
3458                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3459                         
3460                         var layout_body_childs = layout_childs[j].childNodes;
3461                         
3462                         for(var k = 0; k < layout_body_childs.length; k++) {
3463                             
3464                             if(layout_body_childs[k].classList.contains('navbar')) {
3465                                 child_height += layout_body_childs[k].offsetHeight;
3466                                 continue;
3467                             }
3468                             
3469                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3470                                 
3471                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3472                                 
3473                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3474                                     
3475                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3476                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3477                                         continue;
3478                                     }
3479                                     
3480                                 }
3481                                 
3482                             }
3483                             
3484                         }
3485                     }
3486                 }
3487                 continue;
3488             }
3489             */
3490             
3491             child_height += child_nodes[i].offsetHeight;
3492             // Roo.log(child_nodes[i].offsetHeight);
3493         }
3494         
3495         return child_height;
3496     }
3497
3498 });
3499
3500
3501 Roo.apply(Roo.bootstrap.Modal,  {
3502     /**
3503          * Button config that displays a single OK button
3504          * @type Object
3505          */
3506         OK :  [{
3507             name : 'ok',
3508             weight : 'primary',
3509             html : 'OK'
3510         }],
3511         /**
3512          * Button config that displays Yes and No buttons
3513          * @type Object
3514          */
3515         YESNO : [
3516             {
3517                 name  : 'no',
3518                 html : 'No'
3519             },
3520             {
3521                 name  :'yes',
3522                 weight : 'primary',
3523                 html : 'Yes'
3524             }
3525         ],
3526
3527         /**
3528          * Button config that displays OK and Cancel buttons
3529          * @type Object
3530          */
3531         OKCANCEL : [
3532             {
3533                name : 'cancel',
3534                 html : 'Cancel'
3535             },
3536             {
3537                 name : 'ok',
3538                 weight : 'primary',
3539                 html : 'OK'
3540             }
3541         ],
3542         /**
3543          * Button config that displays Yes, No and Cancel buttons
3544          * @type Object
3545          */
3546         YESNOCANCEL : [
3547             {
3548                 name : 'yes',
3549                 weight : 'primary',
3550                 html : 'Yes'
3551             },
3552             {
3553                 name : 'no',
3554                 html : 'No'
3555             },
3556             {
3557                 name : 'cancel',
3558                 html : 'Cancel'
3559             }
3560         ],
3561         
3562         zIndex : 10001
3563 });
3564 /*
3565  * - LGPL
3566  *
3567  * messagebox - can be used as a replace
3568  * 
3569  */
3570 /**
3571  * @class Roo.MessageBox
3572  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3573  * Example usage:
3574  *<pre><code>
3575 // Basic alert:
3576 Roo.Msg.alert('Status', 'Changes saved successfully.');
3577
3578 // Prompt for user data:
3579 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3580     if (btn == 'ok'){
3581         // process text value...
3582     }
3583 });
3584
3585 // Show a dialog using config options:
3586 Roo.Msg.show({
3587    title:'Save Changes?',
3588    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3589    buttons: Roo.Msg.YESNOCANCEL,
3590    fn: processResult,
3591    animEl: 'elId'
3592 });
3593 </code></pre>
3594  * @singleton
3595  */
3596 Roo.bootstrap.MessageBox = function(){
3597     var dlg, opt, mask, waitTimer;
3598     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3599     var buttons, activeTextEl, bwidth;
3600
3601     
3602     // private
3603     var handleButton = function(button){
3604         dlg.hide();
3605         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3606     };
3607
3608     // private
3609     var handleHide = function(){
3610         if(opt && opt.cls){
3611             dlg.el.removeClass(opt.cls);
3612         }
3613         //if(waitTimer){
3614         //    Roo.TaskMgr.stop(waitTimer);
3615         //    waitTimer = null;
3616         //}
3617     };
3618
3619     // private
3620     var updateButtons = function(b){
3621         var width = 0;
3622         if(!b){
3623             buttons["ok"].hide();
3624             buttons["cancel"].hide();
3625             buttons["yes"].hide();
3626             buttons["no"].hide();
3627             dlg.footerEl.hide();
3628             
3629             return width;
3630         }
3631         dlg.footerEl.show();
3632         for(var k in buttons){
3633             if(typeof buttons[k] != "function"){
3634                 if(b[k]){
3635                     buttons[k].show();
3636                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3637                     width += buttons[k].el.getWidth()+15;
3638                 }else{
3639                     buttons[k].hide();
3640                 }
3641             }
3642         }
3643         return width;
3644     };
3645
3646     // private
3647     var handleEsc = function(d, k, e){
3648         if(opt && opt.closable !== false){
3649             dlg.hide();
3650         }
3651         if(e){
3652             e.stopEvent();
3653         }
3654     };
3655
3656     return {
3657         /**
3658          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3659          * @return {Roo.BasicDialog} The BasicDialog element
3660          */
3661         getDialog : function(){
3662            if(!dlg){
3663                 dlg = new Roo.bootstrap.Modal( {
3664                     //draggable: true,
3665                     //resizable:false,
3666                     //constraintoviewport:false,
3667                     //fixedcenter:true,
3668                     //collapsible : false,
3669                     //shim:true,
3670                     //modal: true,
3671                 //    width: 'auto',
3672                   //  height:100,
3673                     //buttonAlign:"center",
3674                     closeClick : function(){
3675                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3676                             handleButton("no");
3677                         }else{
3678                             handleButton("cancel");
3679                         }
3680                     }
3681                 });
3682                 dlg.render();
3683                 dlg.on("hide", handleHide);
3684                 mask = dlg.mask;
3685                 //dlg.addKeyListener(27, handleEsc);
3686                 buttons = {};
3687                 this.buttons = buttons;
3688                 var bt = this.buttonText;
3689                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3690                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3691                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3692                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3693                 //Roo.log(buttons);
3694                 bodyEl = dlg.bodyEl.createChild({
3695
3696                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3697                         '<textarea class="roo-mb-textarea"></textarea>' +
3698                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3699                 });
3700                 msgEl = bodyEl.dom.firstChild;
3701                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3702                 textboxEl.enableDisplayMode();
3703                 textboxEl.addKeyListener([10,13], function(){
3704                     if(dlg.isVisible() && opt && opt.buttons){
3705                         if(opt.buttons.ok){
3706                             handleButton("ok");
3707                         }else if(opt.buttons.yes){
3708                             handleButton("yes");
3709                         }
3710                     }
3711                 });
3712                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3713                 textareaEl.enableDisplayMode();
3714                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3715                 progressEl.enableDisplayMode();
3716                 
3717                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3718                 var pf = progressEl.dom.firstChild;
3719                 if (pf) {
3720                     pp = Roo.get(pf.firstChild);
3721                     pp.setHeight(pf.offsetHeight);
3722                 }
3723                 
3724             }
3725             return dlg;
3726         },
3727
3728         /**
3729          * Updates the message box body text
3730          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3731          * the XHTML-compliant non-breaking space character '&amp;#160;')
3732          * @return {Roo.MessageBox} This message box
3733          */
3734         updateText : function(text)
3735         {
3736             if(!dlg.isVisible() && !opt.width){
3737                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3738                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3739             }
3740             msgEl.innerHTML = text || '&#160;';
3741       
3742             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3743             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3744             var w = Math.max(
3745                     Math.min(opt.width || cw , this.maxWidth), 
3746                     Math.max(opt.minWidth || this.minWidth, bwidth)
3747             );
3748             if(opt.prompt){
3749                 activeTextEl.setWidth(w);
3750             }
3751             if(dlg.isVisible()){
3752                 dlg.fixedcenter = false;
3753             }
3754             // to big, make it scroll. = But as usual stupid IE does not support
3755             // !important..
3756             
3757             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3758                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3759                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3760             } else {
3761                 bodyEl.dom.style.height = '';
3762                 bodyEl.dom.style.overflowY = '';
3763             }
3764             if (cw > w) {
3765                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3766             } else {
3767                 bodyEl.dom.style.overflowX = '';
3768             }
3769             
3770             dlg.setContentSize(w, bodyEl.getHeight());
3771             if(dlg.isVisible()){
3772                 dlg.fixedcenter = true;
3773             }
3774             return this;
3775         },
3776
3777         /**
3778          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3779          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3780          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3781          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3782          * @return {Roo.MessageBox} This message box
3783          */
3784         updateProgress : function(value, text){
3785             if(text){
3786                 this.updateText(text);
3787             }
3788             
3789             if (pp) { // weird bug on my firefox - for some reason this is not defined
3790                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3791                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3792             }
3793             return this;
3794         },        
3795
3796         /**
3797          * Returns true if the message box is currently displayed
3798          * @return {Boolean} True if the message box is visible, else false
3799          */
3800         isVisible : function(){
3801             return dlg && dlg.isVisible();  
3802         },
3803
3804         /**
3805          * Hides the message box if it is displayed
3806          */
3807         hide : function(){
3808             if(this.isVisible()){
3809                 dlg.hide();
3810             }  
3811         },
3812
3813         /**
3814          * Displays a new message box, or reinitializes an existing message box, based on the config options
3815          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3816          * The following config object properties are supported:
3817          * <pre>
3818 Property    Type             Description
3819 ----------  ---------------  ------------------------------------------------------------------------------------
3820 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3821                                    closes (defaults to undefined)
3822 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3823                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3824 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3825                                    progress and wait dialogs will ignore this property and always hide the
3826                                    close button as they can only be closed programmatically.
3827 cls               String           A custom CSS class to apply to the message box element
3828 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3829                                    displayed (defaults to 75)
3830 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3831                                    function will be btn (the name of the button that was clicked, if applicable,
3832                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3833                                    Progress and wait dialogs will ignore this option since they do not respond to
3834                                    user actions and can only be closed programmatically, so any required function
3835                                    should be called by the same code after it closes the dialog.
3836 icon              String           A CSS class that provides a background image to be used as an icon for
3837                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3838 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3839 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3840 modal             Boolean          False to allow user interaction with the page while the message box is
3841                                    displayed (defaults to true)
3842 msg               String           A string that will replace the existing message box body text (defaults
3843                                    to the XHTML-compliant non-breaking space character '&#160;')
3844 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3845 progress          Boolean          True to display a progress bar (defaults to false)
3846 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3847 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3848 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3849 title             String           The title text
3850 value             String           The string value to set into the active textbox element if displayed
3851 wait              Boolean          True to display a progress bar (defaults to false)
3852 width             Number           The width of the dialog in pixels
3853 </pre>
3854          *
3855          * Example usage:
3856          * <pre><code>
3857 Roo.Msg.show({
3858    title: 'Address',
3859    msg: 'Please enter your address:',
3860    width: 300,
3861    buttons: Roo.MessageBox.OKCANCEL,
3862    multiline: true,
3863    fn: saveAddress,
3864    animEl: 'addAddressBtn'
3865 });
3866 </code></pre>
3867          * @param {Object} config Configuration options
3868          * @return {Roo.MessageBox} This message box
3869          */
3870         show : function(options)
3871         {
3872             
3873             // this causes nightmares if you show one dialog after another
3874             // especially on callbacks..
3875              
3876             if(this.isVisible()){
3877                 
3878                 this.hide();
3879                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3880                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3881                 Roo.log("New Dialog Message:" +  options.msg )
3882                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3883                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3884                 
3885             }
3886             var d = this.getDialog();
3887             opt = options;
3888             d.setTitle(opt.title || "&#160;");
3889             d.closeEl.setDisplayed(opt.closable !== false);
3890             activeTextEl = textboxEl;
3891             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3892             if(opt.prompt){
3893                 if(opt.multiline){
3894                     textboxEl.hide();
3895                     textareaEl.show();
3896                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3897                         opt.multiline : this.defaultTextHeight);
3898                     activeTextEl = textareaEl;
3899                 }else{
3900                     textboxEl.show();
3901                     textareaEl.hide();
3902                 }
3903             }else{
3904                 textboxEl.hide();
3905                 textareaEl.hide();
3906             }
3907             progressEl.setDisplayed(opt.progress === true);
3908             if (opt.progress) {
3909                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3910             }
3911             this.updateProgress(0);
3912             activeTextEl.dom.value = opt.value || "";
3913             if(opt.prompt){
3914                 dlg.setDefaultButton(activeTextEl);
3915             }else{
3916                 var bs = opt.buttons;
3917                 var db = null;
3918                 if(bs && bs.ok){
3919                     db = buttons["ok"];
3920                 }else if(bs && bs.yes){
3921                     db = buttons["yes"];
3922                 }
3923                 dlg.setDefaultButton(db);
3924             }
3925             bwidth = updateButtons(opt.buttons);
3926             this.updateText(opt.msg);
3927             if(opt.cls){
3928                 d.el.addClass(opt.cls);
3929             }
3930             d.proxyDrag = opt.proxyDrag === true;
3931             d.modal = opt.modal !== false;
3932             d.mask = opt.modal !== false ? mask : false;
3933             if(!d.isVisible()){
3934                 // force it to the end of the z-index stack so it gets a cursor in FF
3935                 document.body.appendChild(dlg.el.dom);
3936                 d.animateTarget = null;
3937                 d.show(options.animEl);
3938             }
3939             return this;
3940         },
3941
3942         /**
3943          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3944          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3945          * and closing the message box when the process is complete.
3946          * @param {String} title The title bar text
3947          * @param {String} msg The message box body text
3948          * @return {Roo.MessageBox} This message box
3949          */
3950         progress : function(title, msg){
3951             this.show({
3952                 title : title,
3953                 msg : msg,
3954                 buttons: false,
3955                 progress:true,
3956                 closable:false,
3957                 minWidth: this.minProgressWidth,
3958                 modal : true
3959             });
3960             return this;
3961         },
3962
3963         /**
3964          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3965          * If a callback function is passed it will be called after the user clicks the button, and the
3966          * id of the button that was clicked will be passed as the only parameter to the callback
3967          * (could also be the top-right close button).
3968          * @param {String} title The title bar text
3969          * @param {String} msg The message box body text
3970          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3971          * @param {Object} scope (optional) The scope of the callback function
3972          * @return {Roo.MessageBox} This message box
3973          */
3974         alert : function(title, msg, fn, scope)
3975         {
3976             this.show({
3977                 title : title,
3978                 msg : msg,
3979                 buttons: this.OK,
3980                 fn: fn,
3981                 closable : false,
3982                 scope : scope,
3983                 modal : true
3984             });
3985             return this;
3986         },
3987
3988         /**
3989          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3990          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3991          * You are responsible for closing the message box when the process is complete.
3992          * @param {String} msg The message box body text
3993          * @param {String} title (optional) The title bar text
3994          * @return {Roo.MessageBox} This message box
3995          */
3996         wait : function(msg, title){
3997             this.show({
3998                 title : title,
3999                 msg : msg,
4000                 buttons: false,
4001                 closable:false,
4002                 progress:true,
4003                 modal:true,
4004                 width:300,
4005                 wait:true
4006             });
4007             waitTimer = Roo.TaskMgr.start({
4008                 run: function(i){
4009                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4010                 },
4011                 interval: 1000
4012             });
4013             return this;
4014         },
4015
4016         /**
4017          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4018          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4019          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4020          * @param {String} title The title bar text
4021          * @param {String} msg The message box body text
4022          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4023          * @param {Object} scope (optional) The scope of the callback function
4024          * @return {Roo.MessageBox} This message box
4025          */
4026         confirm : function(title, msg, fn, scope){
4027             this.show({
4028                 title : title,
4029                 msg : msg,
4030                 buttons: this.YESNO,
4031                 fn: fn,
4032                 scope : scope,
4033                 modal : true
4034             });
4035             return this;
4036         },
4037
4038         /**
4039          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4040          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4041          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4042          * (could also be the top-right close button) and the text that was entered will be passed as the two
4043          * parameters to the callback.
4044          * @param {String} title The title bar text
4045          * @param {String} msg The message box body text
4046          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4047          * @param {Object} scope (optional) The scope of the callback function
4048          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4049          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4050          * @return {Roo.MessageBox} This message box
4051          */
4052         prompt : function(title, msg, fn, scope, multiline){
4053             this.show({
4054                 title : title,
4055                 msg : msg,
4056                 buttons: this.OKCANCEL,
4057                 fn: fn,
4058                 minWidth:250,
4059                 scope : scope,
4060                 prompt:true,
4061                 multiline: multiline,
4062                 modal : true
4063             });
4064             return this;
4065         },
4066
4067         /**
4068          * Button config that displays a single OK button
4069          * @type Object
4070          */
4071         OK : {ok:true},
4072         /**
4073          * Button config that displays Yes and No buttons
4074          * @type Object
4075          */
4076         YESNO : {yes:true, no:true},
4077         /**
4078          * Button config that displays OK and Cancel buttons
4079          * @type Object
4080          */
4081         OKCANCEL : {ok:true, cancel:true},
4082         /**
4083          * Button config that displays Yes, No and Cancel buttons
4084          * @type Object
4085          */
4086         YESNOCANCEL : {yes:true, no:true, cancel:true},
4087
4088         /**
4089          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4090          * @type Number
4091          */
4092         defaultTextHeight : 75,
4093         /**
4094          * The maximum width in pixels of the message box (defaults to 600)
4095          * @type Number
4096          */
4097         maxWidth : 600,
4098         /**
4099          * The minimum width in pixels of the message box (defaults to 100)
4100          * @type Number
4101          */
4102         minWidth : 100,
4103         /**
4104          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4105          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4106          * @type Number
4107          */
4108         minProgressWidth : 250,
4109         /**
4110          * An object containing the default button text strings that can be overriden for localized language support.
4111          * Supported properties are: ok, cancel, yes and no.
4112          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4113          * @type Object
4114          */
4115         buttonText : {
4116             ok : "OK",
4117             cancel : "Cancel",
4118             yes : "Yes",
4119             no : "No"
4120         }
4121     };
4122 }();
4123
4124 /**
4125  * Shorthand for {@link Roo.MessageBox}
4126  */
4127 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4128 Roo.Msg = Roo.Msg || Roo.MessageBox;
4129 /*
4130  * - LGPL
4131  *
4132  * navbar
4133  * 
4134  */
4135
4136 /**
4137  * @class Roo.bootstrap.Navbar
4138  * @extends Roo.bootstrap.Component
4139  * Bootstrap Navbar class
4140
4141  * @constructor
4142  * Create a new Navbar
4143  * @param {Object} config The config object
4144  */
4145
4146
4147 Roo.bootstrap.Navbar = function(config){
4148     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4149     this.addEvents({
4150         // raw events
4151         /**
4152          * @event beforetoggle
4153          * Fire before toggle the menu
4154          * @param {Roo.EventObject} e
4155          */
4156         "beforetoggle" : true
4157     });
4158 };
4159
4160 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4161     
4162     
4163    
4164     // private
4165     navItems : false,
4166     loadMask : false,
4167     
4168     
4169     getAutoCreate : function(){
4170         
4171         
4172         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4173         
4174     },
4175     
4176     initEvents :function ()
4177     {
4178         //Roo.log(this.el.select('.navbar-toggle',true));
4179         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4180         
4181         var mark = {
4182             tag: "div",
4183             cls:"x-dlg-mask"
4184         };
4185         
4186         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4187         
4188         var size = this.el.getSize();
4189         this.maskEl.setSize(size.width, size.height);
4190         this.maskEl.enableDisplayMode("block");
4191         this.maskEl.hide();
4192         
4193         if(this.loadMask){
4194             this.maskEl.show();
4195         }
4196     },
4197     
4198     
4199     getChildContainer : function()
4200     {
4201         if (this.el && this.el.select('.collapse').getCount()) {
4202             return this.el.select('.collapse',true).first();
4203         }
4204         
4205         return this.el;
4206     },
4207     
4208     mask : function()
4209     {
4210         this.maskEl.show();
4211     },
4212     
4213     unmask : function()
4214     {
4215         this.maskEl.hide();
4216     },
4217     onToggle : function()
4218     {
4219         
4220         if(this.fireEvent('beforetoggle', this) === false){
4221             return;
4222         }
4223         var ce = this.el.select('.navbar-collapse',true).first();
4224       
4225         if (!ce.hasClass('show')) {
4226            this.expand();
4227         } else {
4228             this.collapse();
4229         }
4230         
4231         
4232     
4233     },
4234     /**
4235      * Expand the navbar pulldown 
4236      */
4237     expand : function ()
4238     {
4239        
4240         var ce = this.el.select('.navbar-collapse',true).first();
4241         if (ce.hasClass('collapsing')) {
4242             return;
4243         }
4244         ce.dom.style.height = '';
4245                // show it...
4246         ce.addClass('in'); // old...
4247         ce.removeClass('collapse');
4248         ce.addClass('show');
4249         var h = ce.getHeight();
4250         Roo.log(h);
4251         ce.removeClass('show');
4252         // at this point we should be able to see it..
4253         ce.addClass('collapsing');
4254         
4255         ce.setHeight(0); // resize it ...
4256         ce.on('transitionend', function() {
4257             //Roo.log('done transition');
4258             ce.removeClass('collapsing');
4259             ce.addClass('show');
4260             ce.removeClass('collapse');
4261
4262             ce.dom.style.height = '';
4263         }, this, { single: true} );
4264         ce.setHeight(h);
4265         ce.dom.scrollTop = 0;
4266     },
4267     /**
4268      * Collapse the navbar pulldown 
4269      */
4270     collapse : function()
4271     {
4272          var ce = this.el.select('.navbar-collapse',true).first();
4273        
4274         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4275             // it's collapsed or collapsing..
4276             return;
4277         }
4278         ce.removeClass('in'); // old...
4279         ce.setHeight(ce.getHeight());
4280         ce.removeClass('show');
4281         ce.addClass('collapsing');
4282         
4283         ce.on('transitionend', function() {
4284             ce.dom.style.height = '';
4285             ce.removeClass('collapsing');
4286             ce.addClass('collapse');
4287         }, this, { single: true} );
4288         ce.setHeight(0);
4289     }
4290     
4291     
4292     
4293 });
4294
4295
4296
4297  
4298
4299  /*
4300  * - LGPL
4301  *
4302  * navbar
4303  * 
4304  */
4305
4306 /**
4307  * @class Roo.bootstrap.NavSimplebar
4308  * @extends Roo.bootstrap.Navbar
4309  * Bootstrap Sidebar class
4310  *
4311  * @cfg {Boolean} inverse is inverted color
4312  * 
4313  * @cfg {String} type (nav | pills | tabs)
4314  * @cfg {Boolean} arrangement stacked | justified
4315  * @cfg {String} align (left | right) alignment
4316  * 
4317  * @cfg {Boolean} main (true|false) main nav bar? default false
4318  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4319  * 
4320  * @cfg {String} tag (header|footer|nav|div) default is nav 
4321
4322  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4323  * 
4324  * 
4325  * @constructor
4326  * Create a new Sidebar
4327  * @param {Object} config The config object
4328  */
4329
4330
4331 Roo.bootstrap.NavSimplebar = function(config){
4332     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4333 };
4334
4335 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4336     
4337     inverse: false,
4338     
4339     type: false,
4340     arrangement: '',
4341     align : false,
4342     
4343     weight : 'light',
4344     
4345     main : false,
4346     
4347     
4348     tag : false,
4349     
4350     
4351     getAutoCreate : function(){
4352         
4353         
4354         var cfg = {
4355             tag : this.tag || 'div',
4356             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4357         };
4358         if (['light','white'].indexOf(this.weight) > -1) {
4359             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4360         }
4361         cfg.cls += ' bg-' + this.weight;
4362         
4363         if (this.inverse) {
4364             cfg.cls += ' navbar-inverse';
4365             
4366         }
4367         
4368         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4369         
4370         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4371             return cfg;
4372         }
4373         
4374         
4375     
4376         
4377         cfg.cn = [
4378             {
4379                 cls: 'nav nav-' + this.xtype,
4380                 tag : 'ul'
4381             }
4382         ];
4383         
4384          
4385         this.type = this.type || 'nav';
4386         if (['tabs','pills'].indexOf(this.type) != -1) {
4387             cfg.cn[0].cls += ' nav-' + this.type
4388         
4389         
4390         } else {
4391             if (this.type!=='nav') {
4392                 Roo.log('nav type must be nav/tabs/pills')
4393             }
4394             cfg.cn[0].cls += ' navbar-nav'
4395         }
4396         
4397         
4398         
4399         
4400         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4401             cfg.cn[0].cls += ' nav-' + this.arrangement;
4402         }
4403         
4404         
4405         if (this.align === 'right') {
4406             cfg.cn[0].cls += ' navbar-right';
4407         }
4408         
4409         
4410         
4411         
4412         return cfg;
4413     
4414         
4415     }
4416     
4417     
4418     
4419 });
4420
4421
4422
4423  
4424
4425  
4426        /*
4427  * - LGPL
4428  *
4429  * navbar
4430  * navbar-fixed-top
4431  * navbar-expand-md  fixed-top 
4432  */
4433
4434 /**
4435  * @class Roo.bootstrap.NavHeaderbar
4436  * @extends Roo.bootstrap.NavSimplebar
4437  * Bootstrap Sidebar class
4438  *
4439  * @cfg {String} brand what is brand
4440  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4441  * @cfg {String} brand_href href of the brand
4442  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4443  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4444  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4445  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4446  * 
4447  * @constructor
4448  * Create a new Sidebar
4449  * @param {Object} config The config object
4450  */
4451
4452
4453 Roo.bootstrap.NavHeaderbar = function(config){
4454     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4455       
4456 };
4457
4458 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4459     
4460     position: '',
4461     brand: '',
4462     brand_href: false,
4463     srButton : true,
4464     autohide : false,
4465     desktopCenter : false,
4466    
4467     
4468     getAutoCreate : function(){
4469         
4470         var   cfg = {
4471             tag: this.nav || 'nav',
4472             cls: 'navbar navbar-expand-md',
4473             role: 'navigation',
4474             cn: []
4475         };
4476         
4477         var cn = cfg.cn;
4478         if (this.desktopCenter) {
4479             cn.push({cls : 'container', cn : []});
4480             cn = cn[0].cn;
4481         }
4482         
4483         if(this.srButton){
4484             var btn = {
4485                 tag: 'button',
4486                 type: 'button',
4487                 cls: 'navbar-toggle navbar-toggler',
4488                 'data-toggle': 'collapse',
4489                 cn: [
4490                     {
4491                         tag: 'span',
4492                         cls: 'sr-only',
4493                         html: 'Toggle navigation'
4494                     },
4495                     {
4496                         tag: 'span',
4497                         cls: 'icon-bar navbar-toggler-icon'
4498                     },
4499                     {
4500                         tag: 'span',
4501                         cls: 'icon-bar'
4502                     },
4503                     {
4504                         tag: 'span',
4505                         cls: 'icon-bar'
4506                     }
4507                 ]
4508             };
4509             
4510             cn.push( Roo.bootstrap.version == 4 ? btn : {
4511                 tag: 'div',
4512                 cls: 'navbar-header',
4513                 cn: [
4514                     btn
4515                 ]
4516             });
4517         }
4518         
4519         cn.push({
4520             tag: 'div',
4521             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4522             cn : []
4523         });
4524         
4525         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4526         
4527         if (['light','white'].indexOf(this.weight) > -1) {
4528             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4529         }
4530         cfg.cls += ' bg-' + this.weight;
4531         
4532         
4533         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4534             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4535             
4536             // tag can override this..
4537             
4538             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4539         }
4540         
4541         if (this.brand !== '') {
4542             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4543             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4544                 tag: 'a',
4545                 href: this.brand_href ? this.brand_href : '#',
4546                 cls: 'navbar-brand',
4547                 cn: [
4548                 this.brand
4549                 ]
4550             });
4551         }
4552         
4553         if(this.main){
4554             cfg.cls += ' main-nav';
4555         }
4556         
4557         
4558         return cfg;
4559
4560         
4561     },
4562     getHeaderChildContainer : function()
4563     {
4564         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4565             return this.el.select('.navbar-header',true).first();
4566         }
4567         
4568         return this.getChildContainer();
4569     },
4570     
4571     getChildContainer : function()
4572     {
4573          
4574         return this.el.select('.roo-navbar-collapse',true).first();
4575          
4576         
4577     },
4578     
4579     initEvents : function()
4580     {
4581         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4582         
4583         if (this.autohide) {
4584             
4585             var prevScroll = 0;
4586             var ft = this.el;
4587             
4588             Roo.get(document).on('scroll',function(e) {
4589                 var ns = Roo.get(document).getScroll().top;
4590                 var os = prevScroll;
4591                 prevScroll = ns;
4592                 
4593                 if(ns > os){
4594                     ft.removeClass('slideDown');
4595                     ft.addClass('slideUp');
4596                     return;
4597                 }
4598                 ft.removeClass('slideUp');
4599                 ft.addClass('slideDown');
4600                  
4601               
4602           },this);
4603         }
4604     }    
4605     
4606 });
4607
4608
4609
4610  
4611
4612  /*
4613  * - LGPL
4614  *
4615  * navbar
4616  * 
4617  */
4618
4619 /**
4620  * @class Roo.bootstrap.NavSidebar
4621  * @extends Roo.bootstrap.Navbar
4622  * Bootstrap Sidebar class
4623  * 
4624  * @constructor
4625  * Create a new Sidebar
4626  * @param {Object} config The config object
4627  */
4628
4629
4630 Roo.bootstrap.NavSidebar = function(config){
4631     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4632 };
4633
4634 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4635     
4636     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4637     
4638     getAutoCreate : function(){
4639         
4640         
4641         return  {
4642             tag: 'div',
4643             cls: 'sidebar sidebar-nav'
4644         };
4645     
4646         
4647     }
4648     
4649     
4650     
4651 });
4652
4653
4654
4655  
4656
4657  /*
4658  * - LGPL
4659  *
4660  * nav group
4661  * 
4662  */
4663
4664 /**
4665  * @class Roo.bootstrap.NavGroup
4666  * @extends Roo.bootstrap.Component
4667  * Bootstrap NavGroup class
4668  * @cfg {String} align (left|right)
4669  * @cfg {Boolean} inverse
4670  * @cfg {String} type (nav|pills|tab) default nav
4671  * @cfg {String} navId - reference Id for navbar.
4672
4673  * 
4674  * @constructor
4675  * Create a new nav group
4676  * @param {Object} config The config object
4677  */
4678
4679 Roo.bootstrap.NavGroup = function(config){
4680     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4681     this.navItems = [];
4682    
4683     Roo.bootstrap.NavGroup.register(this);
4684      this.addEvents({
4685         /**
4686              * @event changed
4687              * Fires when the active item changes
4688              * @param {Roo.bootstrap.NavGroup} this
4689              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4690              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4691          */
4692         'changed': true
4693      });
4694     
4695 };
4696
4697 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4698     
4699     align: '',
4700     inverse: false,
4701     form: false,
4702     type: 'nav',
4703     navId : '',
4704     // private
4705     
4706     navItems : false, 
4707     
4708     getAutoCreate : function()
4709     {
4710         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4711         
4712         cfg = {
4713             tag : 'ul',
4714             cls: 'nav' 
4715         };
4716         if (Roo.bootstrap.version == 4) {
4717             if (['tabs','pills'].indexOf(this.type) != -1) {
4718                 cfg.cls += ' nav-' + this.type; 
4719             } else {
4720                 // trying to remove so header bar can right align top?
4721                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4722                     // do not use on header bar... 
4723                     cfg.cls += ' navbar-nav';
4724                 }
4725             }
4726             
4727         } else {
4728             if (['tabs','pills'].indexOf(this.type) != -1) {
4729                 cfg.cls += ' nav-' + this.type
4730             } else {
4731                 if (this.type !== 'nav') {
4732                     Roo.log('nav type must be nav/tabs/pills')
4733                 }
4734                 cfg.cls += ' navbar-nav'
4735             }
4736         }
4737         
4738         if (this.parent() && this.parent().sidebar) {
4739             cfg = {
4740                 tag: 'ul',
4741                 cls: 'dashboard-menu sidebar-menu'
4742             };
4743             
4744             return cfg;
4745         }
4746         
4747         if (this.form === true) {
4748             cfg = {
4749                 tag: 'form',
4750                 cls: 'navbar-form form-inline'
4751             };
4752             //nav navbar-right ml-md-auto
4753             if (this.align === 'right') {
4754                 cfg.cls += ' navbar-right ml-md-auto';
4755             } else {
4756                 cfg.cls += ' navbar-left';
4757             }
4758         }
4759         
4760         if (this.align === 'right') {
4761             cfg.cls += ' navbar-right ml-md-auto';
4762         } else {
4763             cfg.cls += ' mr-auto';
4764         }
4765         
4766         if (this.inverse) {
4767             cfg.cls += ' navbar-inverse';
4768             
4769         }
4770         
4771         
4772         return cfg;
4773     },
4774     /**
4775     * sets the active Navigation item
4776     * @param {Roo.bootstrap.NavItem} the new current navitem
4777     */
4778     setActiveItem : function(item)
4779     {
4780         var prev = false;
4781         Roo.each(this.navItems, function(v){
4782             if (v == item) {
4783                 return ;
4784             }
4785             if (v.isActive()) {
4786                 v.setActive(false, true);
4787                 prev = v;
4788                 
4789             }
4790             
4791         });
4792
4793         item.setActive(true, true);
4794         this.fireEvent('changed', this, item, prev);
4795         
4796         
4797     },
4798     /**
4799     * gets the active Navigation item
4800     * @return {Roo.bootstrap.NavItem} the current navitem
4801     */
4802     getActive : function()
4803     {
4804         
4805         var prev = false;
4806         Roo.each(this.navItems, function(v){
4807             
4808             if (v.isActive()) {
4809                 prev = v;
4810                 
4811             }
4812             
4813         });
4814         return prev;
4815     },
4816     
4817     indexOfNav : function()
4818     {
4819         
4820         var prev = false;
4821         Roo.each(this.navItems, function(v,i){
4822             
4823             if (v.isActive()) {
4824                 prev = i;
4825                 
4826             }
4827             
4828         });
4829         return prev;
4830     },
4831     /**
4832     * adds a Navigation item
4833     * @param {Roo.bootstrap.NavItem} the navitem to add
4834     */
4835     addItem : function(cfg)
4836     {
4837         if (this.form && Roo.bootstrap.version == 4) {
4838             cfg.tag = 'div';
4839         }
4840         var cn = new Roo.bootstrap.NavItem(cfg);
4841         this.register(cn);
4842         cn.parentId = this.id;
4843         cn.onRender(this.el, null);
4844         return cn;
4845     },
4846     /**
4847     * register a Navigation item
4848     * @param {Roo.bootstrap.NavItem} the navitem to add
4849     */
4850     register : function(item)
4851     {
4852         this.navItems.push( item);
4853         item.navId = this.navId;
4854     
4855     },
4856     
4857     /**
4858     * clear all the Navigation item
4859     */
4860    
4861     clearAll : function()
4862     {
4863         this.navItems = [];
4864         this.el.dom.innerHTML = '';
4865     },
4866     
4867     getNavItem: function(tabId)
4868     {
4869         var ret = false;
4870         Roo.each(this.navItems, function(e) {
4871             if (e.tabId == tabId) {
4872                ret =  e;
4873                return false;
4874             }
4875             return true;
4876             
4877         });
4878         return ret;
4879     },
4880     
4881     setActiveNext : function()
4882     {
4883         var i = this.indexOfNav(this.getActive());
4884         if (i > this.navItems.length) {
4885             return;
4886         }
4887         this.setActiveItem(this.navItems[i+1]);
4888     },
4889     setActivePrev : function()
4890     {
4891         var i = this.indexOfNav(this.getActive());
4892         if (i  < 1) {
4893             return;
4894         }
4895         this.setActiveItem(this.navItems[i-1]);
4896     },
4897     clearWasActive : function(except) {
4898         Roo.each(this.navItems, function(e) {
4899             if (e.tabId != except.tabId && e.was_active) {
4900                e.was_active = false;
4901                return false;
4902             }
4903             return true;
4904             
4905         });
4906     },
4907     getWasActive : function ()
4908     {
4909         var r = false;
4910         Roo.each(this.navItems, function(e) {
4911             if (e.was_active) {
4912                r = e;
4913                return false;
4914             }
4915             return true;
4916             
4917         });
4918         return r;
4919     }
4920     
4921     
4922 });
4923
4924  
4925 Roo.apply(Roo.bootstrap.NavGroup, {
4926     
4927     groups: {},
4928      /**
4929     * register a Navigation Group
4930     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4931     */
4932     register : function(navgrp)
4933     {
4934         this.groups[navgrp.navId] = navgrp;
4935         
4936     },
4937     /**
4938     * fetch a Navigation Group based on the navigation ID
4939     * @param {string} the navgroup to add
4940     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4941     */
4942     get: function(navId) {
4943         if (typeof(this.groups[navId]) == 'undefined') {
4944             return false;
4945             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4946         }
4947         return this.groups[navId] ;
4948     }
4949     
4950     
4951     
4952 });
4953
4954  /*
4955  * - LGPL
4956  *
4957  * row
4958  * 
4959  */
4960
4961 /**
4962  * @class Roo.bootstrap.NavItem
4963  * @extends Roo.bootstrap.Component
4964  * Bootstrap Navbar.NavItem class
4965  * @cfg {String} href  link to
4966  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4967
4968  * @cfg {String} html content of button
4969  * @cfg {String} badge text inside badge
4970  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4971  * @cfg {String} glyphicon DEPRICATED - use fa
4972  * @cfg {String} icon DEPRICATED - use fa
4973  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4974  * @cfg {Boolean} active Is item active
4975  * @cfg {Boolean} disabled Is item disabled
4976  
4977  * @cfg {Boolean} preventDefault (true | false) default false
4978  * @cfg {String} tabId the tab that this item activates.
4979  * @cfg {String} tagtype (a|span) render as a href or span?
4980  * @cfg {Boolean} animateRef (true|false) link to element default false  
4981   
4982  * @constructor
4983  * Create a new Navbar Item
4984  * @param {Object} config The config object
4985  */
4986 Roo.bootstrap.NavItem = function(config){
4987     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4988     this.addEvents({
4989         // raw events
4990         /**
4991          * @event click
4992          * The raw click event for the entire grid.
4993          * @param {Roo.EventObject} e
4994          */
4995         "click" : true,
4996          /**
4997             * @event changed
4998             * Fires when the active item active state changes
4999             * @param {Roo.bootstrap.NavItem} this
5000             * @param {boolean} state the new state
5001              
5002          */
5003         'changed': true,
5004         /**
5005             * @event scrollto
5006             * Fires when scroll to element
5007             * @param {Roo.bootstrap.NavItem} this
5008             * @param {Object} options
5009             * @param {Roo.EventObject} e
5010              
5011          */
5012         'scrollto': true
5013     });
5014    
5015 };
5016
5017 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5018     
5019     href: false,
5020     html: '',
5021     badge: '',
5022     icon: false,
5023     fa : false,
5024     glyphicon: false,
5025     active: false,
5026     preventDefault : false,
5027     tabId : false,
5028     tagtype : 'a',
5029     tag: 'li',
5030     disabled : false,
5031     animateRef : false,
5032     was_active : false,
5033     button_weight : '',
5034     button_outline : false,
5035     
5036     navLink: false,
5037     
5038     getAutoCreate : function(){
5039          
5040         var cfg = {
5041             tag: this.tag,
5042             cls: 'nav-item'
5043         };
5044         
5045         if (this.active) {
5046             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5047         }
5048         if (this.disabled) {
5049             cfg.cls += ' disabled';
5050         }
5051         
5052         // BS4 only?
5053         if (this.button_weight.length) {
5054             cfg.tag = this.href ? 'a' : 'button';
5055             cfg.html = this.html || '';
5056             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5057             if (this.href) {
5058                 cfg.href = this.href;
5059             }
5060             if (this.fa) {
5061                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5062             }
5063             
5064             // menu .. should add dropdown-menu class - so no need for carat..
5065             
5066             if (this.badge !== '') {
5067                  
5068                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5069             }
5070             return cfg;
5071         }
5072         
5073         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5074             cfg.cn = [
5075                 {
5076                     tag: this.tagtype,
5077                     href : this.href || "#",
5078                     html: this.html || ''
5079                 }
5080             ];
5081             if (this.tagtype == 'a') {
5082                 cfg.cn[0].cls = 'nav-link';
5083             }
5084             if (this.icon) {
5085                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5086             }
5087             if (this.fa) {
5088                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5089             }
5090             if(this.glyphicon) {
5091                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5092             }
5093             
5094             if (this.menu) {
5095                 
5096                 cfg.cn[0].html += " <span class='caret'></span>";
5097              
5098             }
5099             
5100             if (this.badge !== '') {
5101                  
5102                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5103             }
5104         }
5105         
5106         
5107         
5108         return cfg;
5109     },
5110     onRender : function(ct, position)
5111     {
5112        // Roo.log("Call onRender: " + this.xtype);
5113         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5114             this.tag = 'div';
5115         }
5116         
5117         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5118         this.navLink = this.el.select('.nav-link',true).first();
5119         return ret;
5120     },
5121       
5122     
5123     initEvents: function() 
5124     {
5125         if (typeof (this.menu) != 'undefined') {
5126             this.menu.parentType = this.xtype;
5127             this.menu.triggerEl = this.el;
5128             this.menu = this.addxtype(Roo.apply({}, this.menu));
5129         }
5130         
5131         this.el.select('a',true).on('click', this.onClick, this);
5132         
5133         if(this.tagtype == 'span'){
5134             this.el.select('span',true).on('click', this.onClick, this);
5135         }
5136        
5137         // at this point parent should be available..
5138         this.parent().register(this);
5139     },
5140     
5141     onClick : function(e)
5142     {
5143         if (e.getTarget('.dropdown-menu-item')) {
5144             // did you click on a menu itemm.... - then don't trigger onclick..
5145             return;
5146         }
5147         
5148         if(
5149                 this.preventDefault || 
5150                 this.href == '#' 
5151         ){
5152             Roo.log("NavItem - prevent Default?");
5153             e.preventDefault();
5154         }
5155         
5156         if (this.disabled) {
5157             return;
5158         }
5159         
5160         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5161         if (tg && tg.transition) {
5162             Roo.log("waiting for the transitionend");
5163             return;
5164         }
5165         
5166         
5167         
5168         //Roo.log("fire event clicked");
5169         if(this.fireEvent('click', this, e) === false){
5170             return;
5171         };
5172         
5173         if(this.tagtype == 'span'){
5174             return;
5175         }
5176         
5177         //Roo.log(this.href);
5178         var ael = this.el.select('a',true).first();
5179         //Roo.log(ael);
5180         
5181         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5182             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5183             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5184                 return; // ignore... - it's a 'hash' to another page.
5185             }
5186             Roo.log("NavItem - prevent Default?");
5187             e.preventDefault();
5188             this.scrollToElement(e);
5189         }
5190         
5191         
5192         var p =  this.parent();
5193    
5194         if (['tabs','pills'].indexOf(p.type)!==-1) {
5195             if (typeof(p.setActiveItem) !== 'undefined') {
5196                 p.setActiveItem(this);
5197             }
5198         }
5199         
5200         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5201         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5202             // remove the collapsed menu expand...
5203             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5204         }
5205     },
5206     
5207     isActive: function () {
5208         return this.active
5209     },
5210     setActive : function(state, fire, is_was_active)
5211     {
5212         if (this.active && !state && this.navId) {
5213             this.was_active = true;
5214             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5215             if (nv) {
5216                 nv.clearWasActive(this);
5217             }
5218             
5219         }
5220         this.active = state;
5221         
5222         if (!state ) {
5223             this.el.removeClass('active');
5224             this.navLink ? this.navLink.removeClass('active') : false;
5225         } else if (!this.el.hasClass('active')) {
5226             
5227             this.el.addClass('active');
5228             if (Roo.bootstrap.version == 4 && this.navLink ) {
5229                 this.navLink.addClass('active');
5230             }
5231             
5232         }
5233         if (fire) {
5234             this.fireEvent('changed', this, state);
5235         }
5236         
5237         // show a panel if it's registered and related..
5238         
5239         if (!this.navId || !this.tabId || !state || is_was_active) {
5240             return;
5241         }
5242         
5243         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5244         if (!tg) {
5245             return;
5246         }
5247         var pan = tg.getPanelByName(this.tabId);
5248         if (!pan) {
5249             return;
5250         }
5251         // if we can not flip to new panel - go back to old nav highlight..
5252         if (false == tg.showPanel(pan)) {
5253             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5254             if (nv) {
5255                 var onav = nv.getWasActive();
5256                 if (onav) {
5257                     onav.setActive(true, false, true);
5258                 }
5259             }
5260             
5261         }
5262         
5263         
5264         
5265     },
5266      // this should not be here...
5267     setDisabled : function(state)
5268     {
5269         this.disabled = state;
5270         if (!state ) {
5271             this.el.removeClass('disabled');
5272         } else if (!this.el.hasClass('disabled')) {
5273             this.el.addClass('disabled');
5274         }
5275         
5276     },
5277     
5278     /**
5279      * Fetch the element to display the tooltip on.
5280      * @return {Roo.Element} defaults to this.el
5281      */
5282     tooltipEl : function()
5283     {
5284         return this.el.select('' + this.tagtype + '', true).first();
5285     },
5286     
5287     scrollToElement : function(e)
5288     {
5289         var c = document.body;
5290         
5291         /*
5292          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5293          */
5294         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5295             c = document.documentElement;
5296         }
5297         
5298         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5299         
5300         if(!target){
5301             return;
5302         }
5303
5304         var o = target.calcOffsetsTo(c);
5305         
5306         var options = {
5307             target : target,
5308             value : o[1]
5309         };
5310         
5311         this.fireEvent('scrollto', this, options, e);
5312         
5313         Roo.get(c).scrollTo('top', options.value, true);
5314         
5315         return;
5316     }
5317 });
5318  
5319
5320  /*
5321  * - LGPL
5322  *
5323  * sidebar item
5324  *
5325  *  li
5326  *    <span> icon </span>
5327  *    <span> text </span>
5328  *    <span>badge </span>
5329  */
5330
5331 /**
5332  * @class Roo.bootstrap.NavSidebarItem
5333  * @extends Roo.bootstrap.NavItem
5334  * Bootstrap Navbar.NavSidebarItem class
5335  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5336  * {Boolean} open is the menu open
5337  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5338  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5339  * {String} buttonSize (sm|md|lg)the extra classes for the button
5340  * {Boolean} showArrow show arrow next to the text (default true)
5341  * @constructor
5342  * Create a new Navbar Button
5343  * @param {Object} config The config object
5344  */
5345 Roo.bootstrap.NavSidebarItem = function(config){
5346     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5347     this.addEvents({
5348         // raw events
5349         /**
5350          * @event click
5351          * The raw click event for the entire grid.
5352          * @param {Roo.EventObject} e
5353          */
5354         "click" : true,
5355          /**
5356             * @event changed
5357             * Fires when the active item active state changes
5358             * @param {Roo.bootstrap.NavSidebarItem} this
5359             * @param {boolean} state the new state
5360              
5361          */
5362         'changed': true
5363     });
5364    
5365 };
5366
5367 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5368     
5369     badgeWeight : 'default',
5370     
5371     open: false,
5372     
5373     buttonView : false,
5374     
5375     buttonWeight : 'default',
5376     
5377     buttonSize : 'md',
5378     
5379     showArrow : true,
5380     
5381     getAutoCreate : function(){
5382         
5383         
5384         var a = {
5385                 tag: 'a',
5386                 href : this.href || '#',
5387                 cls: '',
5388                 html : '',
5389                 cn : []
5390         };
5391         
5392         if(this.buttonView){
5393             a = {
5394                 tag: 'button',
5395                 href : this.href || '#',
5396                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5397                 html : this.html,
5398                 cn : []
5399             };
5400         }
5401         
5402         var cfg = {
5403             tag: 'li',
5404             cls: '',
5405             cn: [ a ]
5406         };
5407         
5408         if (this.active) {
5409             cfg.cls += ' active';
5410         }
5411         
5412         if (this.disabled) {
5413             cfg.cls += ' disabled';
5414         }
5415         if (this.open) {
5416             cfg.cls += ' open x-open';
5417         }
5418         // left icon..
5419         if (this.glyphicon || this.icon) {
5420             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5421             a.cn.push({ tag : 'i', cls : c }) ;
5422         }
5423         
5424         if(!this.buttonView){
5425             var span = {
5426                 tag: 'span',
5427                 html : this.html || ''
5428             };
5429
5430             a.cn.push(span);
5431             
5432         }
5433         
5434         if (this.badge !== '') {
5435             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5436         }
5437         
5438         if (this.menu) {
5439             
5440             if(this.showArrow){
5441                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5442             }
5443             
5444             a.cls += ' dropdown-toggle treeview' ;
5445         }
5446         
5447         return cfg;
5448     },
5449     
5450     initEvents : function()
5451     { 
5452         if (typeof (this.menu) != 'undefined') {
5453             this.menu.parentType = this.xtype;
5454             this.menu.triggerEl = this.el;
5455             this.menu = this.addxtype(Roo.apply({}, this.menu));
5456         }
5457         
5458         this.el.on('click', this.onClick, this);
5459         
5460         if(this.badge !== ''){
5461             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5462         }
5463         
5464     },
5465     
5466     onClick : function(e)
5467     {
5468         if(this.disabled){
5469             e.preventDefault();
5470             return;
5471         }
5472         
5473         if(this.preventDefault){
5474             e.preventDefault();
5475         }
5476         
5477         this.fireEvent('click', this, e);
5478     },
5479     
5480     disable : function()
5481     {
5482         this.setDisabled(true);
5483     },
5484     
5485     enable : function()
5486     {
5487         this.setDisabled(false);
5488     },
5489     
5490     setDisabled : function(state)
5491     {
5492         if(this.disabled == state){
5493             return;
5494         }
5495         
5496         this.disabled = state;
5497         
5498         if (state) {
5499             this.el.addClass('disabled');
5500             return;
5501         }
5502         
5503         this.el.removeClass('disabled');
5504         
5505         return;
5506     },
5507     
5508     setActive : function(state)
5509     {
5510         if(this.active == state){
5511             return;
5512         }
5513         
5514         this.active = state;
5515         
5516         if (state) {
5517             this.el.addClass('active');
5518             return;
5519         }
5520         
5521         this.el.removeClass('active');
5522         
5523         return;
5524     },
5525     
5526     isActive: function () 
5527     {
5528         return this.active;
5529     },
5530     
5531     setBadge : function(str)
5532     {
5533         if(!this.badgeEl){
5534             return;
5535         }
5536         
5537         this.badgeEl.dom.innerHTML = str;
5538     }
5539     
5540    
5541      
5542  
5543 });
5544  
5545
5546  /*
5547  * - LGPL
5548  *
5549  * row
5550  * 
5551  */
5552
5553 /**
5554  * @class Roo.bootstrap.Row
5555  * @extends Roo.bootstrap.Component
5556  * Bootstrap Row class (contains columns...)
5557  * 
5558  * @constructor
5559  * Create a new Row
5560  * @param {Object} config The config object
5561  */
5562
5563 Roo.bootstrap.Row = function(config){
5564     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5565 };
5566
5567 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5568     
5569     getAutoCreate : function(){
5570        return {
5571             cls: 'row clearfix'
5572        };
5573     }
5574     
5575     
5576 });
5577
5578  
5579
5580  /*
5581  * - LGPL
5582  *
5583  * element
5584  * 
5585  */
5586
5587 /**
5588  * @class Roo.bootstrap.Element
5589  * @extends Roo.bootstrap.Component
5590  * Bootstrap Element class
5591  * @cfg {String} html contents of the element
5592  * @cfg {String} tag tag of the element
5593  * @cfg {String} cls class of the element
5594  * @cfg {Boolean} preventDefault (true|false) default false
5595  * @cfg {Boolean} clickable (true|false) default false
5596  * 
5597  * @constructor
5598  * Create a new Element
5599  * @param {Object} config The config object
5600  */
5601
5602 Roo.bootstrap.Element = function(config){
5603     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5604     
5605     this.addEvents({
5606         // raw events
5607         /**
5608          * @event click
5609          * When a element is chick
5610          * @param {Roo.bootstrap.Element} this
5611          * @param {Roo.EventObject} e
5612          */
5613         "click" : true
5614     });
5615 };
5616
5617 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5618     
5619     tag: 'div',
5620     cls: '',
5621     html: '',
5622     preventDefault: false, 
5623     clickable: false,
5624     
5625     getAutoCreate : function(){
5626         
5627         var cfg = {
5628             tag: this.tag,
5629             // cls: this.cls, double assign in parent class Component.js :: onRender
5630             html: this.html
5631         };
5632         
5633         return cfg;
5634     },
5635     
5636     initEvents: function() 
5637     {
5638         Roo.bootstrap.Element.superclass.initEvents.call(this);
5639         
5640         if(this.clickable){
5641             this.el.on('click', this.onClick, this);
5642         }
5643         
5644     },
5645     
5646     onClick : function(e)
5647     {
5648         if(this.preventDefault){
5649             e.preventDefault();
5650         }
5651         
5652         this.fireEvent('click', this, e);
5653     },
5654     
5655     getValue : function()
5656     {
5657         return this.el.dom.innerHTML;
5658     },
5659     
5660     setValue : function(value)
5661     {
5662         this.el.dom.innerHTML = value;
5663     }
5664    
5665 });
5666
5667  
5668
5669  /*
5670  * - LGPL
5671  *
5672  * pagination
5673  * 
5674  */
5675
5676 /**
5677  * @class Roo.bootstrap.Pagination
5678  * @extends Roo.bootstrap.Component
5679  * Bootstrap Pagination class
5680  * @cfg {String} size xs | sm | md | lg
5681  * @cfg {Boolean} inverse false | true
5682  * 
5683  * @constructor
5684  * Create a new Pagination
5685  * @param {Object} config The config object
5686  */
5687
5688 Roo.bootstrap.Pagination = function(config){
5689     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5690 };
5691
5692 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5693     
5694     cls: false,
5695     size: false,
5696     inverse: false,
5697     
5698     getAutoCreate : function(){
5699         var cfg = {
5700             tag: 'ul',
5701                 cls: 'pagination'
5702         };
5703         if (this.inverse) {
5704             cfg.cls += ' inverse';
5705         }
5706         if (this.html) {
5707             cfg.html=this.html;
5708         }
5709         if (this.cls) {
5710             cfg.cls += " " + this.cls;
5711         }
5712         return cfg;
5713     }
5714    
5715 });
5716
5717  
5718
5719  /*
5720  * - LGPL
5721  *
5722  * Pagination item
5723  * 
5724  */
5725
5726
5727 /**
5728  * @class Roo.bootstrap.PaginationItem
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap PaginationItem class
5731  * @cfg {String} html text
5732  * @cfg {String} href the link
5733  * @cfg {Boolean} preventDefault (true | false) default true
5734  * @cfg {Boolean} active (true | false) default false
5735  * @cfg {Boolean} disabled default false
5736  * 
5737  * 
5738  * @constructor
5739  * Create a new PaginationItem
5740  * @param {Object} config The config object
5741  */
5742
5743
5744 Roo.bootstrap.PaginationItem = function(config){
5745     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5746     this.addEvents({
5747         // raw events
5748         /**
5749          * @event click
5750          * The raw click event for the entire grid.
5751          * @param {Roo.EventObject} e
5752          */
5753         "click" : true
5754     });
5755 };
5756
5757 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5758     
5759     href : false,
5760     html : false,
5761     preventDefault: true,
5762     active : false,
5763     cls : false,
5764     disabled: false,
5765     
5766     getAutoCreate : function(){
5767         var cfg= {
5768             tag: 'li',
5769             cn: [
5770                 {
5771                     tag : 'a',
5772                     href : this.href ? this.href : '#',
5773                     html : this.html ? this.html : ''
5774                 }
5775             ]
5776         };
5777         
5778         if(this.cls){
5779             cfg.cls = this.cls;
5780         }
5781         
5782         if(this.disabled){
5783             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5784         }
5785         
5786         if(this.active){
5787             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5788         }
5789         
5790         return cfg;
5791     },
5792     
5793     initEvents: function() {
5794         
5795         this.el.on('click', this.onClick, this);
5796         
5797     },
5798     onClick : function(e)
5799     {
5800         Roo.log('PaginationItem on click ');
5801         if(this.preventDefault){
5802             e.preventDefault();
5803         }
5804         
5805         if(this.disabled){
5806             return;
5807         }
5808         
5809         this.fireEvent('click', this, e);
5810     }
5811    
5812 });
5813
5814  
5815
5816  /*
5817  * - LGPL
5818  *
5819  * slider
5820  * 
5821  */
5822
5823
5824 /**
5825  * @class Roo.bootstrap.Slider
5826  * @extends Roo.bootstrap.Component
5827  * Bootstrap Slider class
5828  *    
5829  * @constructor
5830  * Create a new Slider
5831  * @param {Object} config The config object
5832  */
5833
5834 Roo.bootstrap.Slider = function(config){
5835     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5836 };
5837
5838 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5839     
5840     getAutoCreate : function(){
5841         
5842         var cfg = {
5843             tag: 'div',
5844             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5845             cn: [
5846                 {
5847                     tag: 'a',
5848                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5849                 }
5850             ]
5851         };
5852         
5853         return cfg;
5854     }
5855    
5856 });
5857
5858  /*
5859  * Based on:
5860  * Ext JS Library 1.1.1
5861  * Copyright(c) 2006-2007, Ext JS, LLC.
5862  *
5863  * Originally Released Under LGPL - original licence link has changed is not relivant.
5864  *
5865  * Fork - LGPL
5866  * <script type="text/javascript">
5867  */
5868  
5869
5870 /**
5871  * @class Roo.grid.ColumnModel
5872  * @extends Roo.util.Observable
5873  * This is the default implementation of a ColumnModel used by the Grid. It defines
5874  * the columns in the grid.
5875  * <br>Usage:<br>
5876  <pre><code>
5877  var colModel = new Roo.grid.ColumnModel([
5878         {header: "Ticker", width: 60, sortable: true, locked: true},
5879         {header: "Company Name", width: 150, sortable: true},
5880         {header: "Market Cap.", width: 100, sortable: true},
5881         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5882         {header: "Employees", width: 100, sortable: true, resizable: false}
5883  ]);
5884  </code></pre>
5885  * <p>
5886  
5887  * The config options listed for this class are options which may appear in each
5888  * individual column definition.
5889  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5890  * @constructor
5891  * @param {Object} config An Array of column config objects. See this class's
5892  * config objects for details.
5893 */
5894 Roo.grid.ColumnModel = function(config){
5895         /**
5896      * The config passed into the constructor
5897      */
5898     this.config = config;
5899     this.lookup = {};
5900
5901     // if no id, create one
5902     // if the column does not have a dataIndex mapping,
5903     // map it to the order it is in the config
5904     for(var i = 0, len = config.length; i < len; i++){
5905         var c = config[i];
5906         if(typeof c.dataIndex == "undefined"){
5907             c.dataIndex = i;
5908         }
5909         if(typeof c.renderer == "string"){
5910             c.renderer = Roo.util.Format[c.renderer];
5911         }
5912         if(typeof c.id == "undefined"){
5913             c.id = Roo.id();
5914         }
5915         if(c.editor && c.editor.xtype){
5916             c.editor  = Roo.factory(c.editor, Roo.grid);
5917         }
5918         if(c.editor && c.editor.isFormField){
5919             c.editor = new Roo.grid.GridEditor(c.editor);
5920         }
5921         this.lookup[c.id] = c;
5922     }
5923
5924     /**
5925      * The width of columns which have no width specified (defaults to 100)
5926      * @type Number
5927      */
5928     this.defaultWidth = 100;
5929
5930     /**
5931      * Default sortable of columns which have no sortable specified (defaults to false)
5932      * @type Boolean
5933      */
5934     this.defaultSortable = false;
5935
5936     this.addEvents({
5937         /**
5938              * @event widthchange
5939              * Fires when the width of a column changes.
5940              * @param {ColumnModel} this
5941              * @param {Number} columnIndex The column index
5942              * @param {Number} newWidth The new width
5943              */
5944             "widthchange": true,
5945         /**
5946              * @event headerchange
5947              * Fires when the text of a header changes.
5948              * @param {ColumnModel} this
5949              * @param {Number} columnIndex The column index
5950              * @param {Number} newText The new header text
5951              */
5952             "headerchange": true,
5953         /**
5954              * @event hiddenchange
5955              * Fires when a column is hidden or "unhidden".
5956              * @param {ColumnModel} this
5957              * @param {Number} columnIndex The column index
5958              * @param {Boolean} hidden true if hidden, false otherwise
5959              */
5960             "hiddenchange": true,
5961             /**
5962          * @event columnmoved
5963          * Fires when a column is moved.
5964          * @param {ColumnModel} this
5965          * @param {Number} oldIndex
5966          * @param {Number} newIndex
5967          */
5968         "columnmoved" : true,
5969         /**
5970          * @event columlockchange
5971          * Fires when a column's locked state is changed
5972          * @param {ColumnModel} this
5973          * @param {Number} colIndex
5974          * @param {Boolean} locked true if locked
5975          */
5976         "columnlockchange" : true
5977     });
5978     Roo.grid.ColumnModel.superclass.constructor.call(this);
5979 };
5980 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5981     /**
5982      * @cfg {String} header The header text to display in the Grid view.
5983      */
5984     /**
5985      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5986      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5987      * specified, the column's index is used as an index into the Record's data Array.
5988      */
5989     /**
5990      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5991      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5992      */
5993     /**
5994      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5995      * Defaults to the value of the {@link #defaultSortable} property.
5996      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5997      */
5998     /**
5999      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6000      */
6001     /**
6002      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6003      */
6004     /**
6005      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6006      */
6007     /**
6008      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6009      */
6010     /**
6011      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6012      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6013      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6014      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6015      */
6016        /**
6017      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6018      */
6019     /**
6020      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6021      */
6022     /**
6023      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6024      */
6025     /**
6026      * @cfg {String} cursor (Optional)
6027      */
6028     /**
6029      * @cfg {String} tooltip (Optional)
6030      */
6031     /**
6032      * @cfg {Number} xs (Optional)
6033      */
6034     /**
6035      * @cfg {Number} sm (Optional)
6036      */
6037     /**
6038      * @cfg {Number} md (Optional)
6039      */
6040     /**
6041      * @cfg {Number} lg (Optional)
6042      */
6043     /**
6044      * Returns the id of the column at the specified index.
6045      * @param {Number} index The column index
6046      * @return {String} the id
6047      */
6048     getColumnId : function(index){
6049         return this.config[index].id;
6050     },
6051
6052     /**
6053      * Returns the column for a specified id.
6054      * @param {String} id The column id
6055      * @return {Object} the column
6056      */
6057     getColumnById : function(id){
6058         return this.lookup[id];
6059     },
6060
6061     
6062     /**
6063      * Returns the column for a specified dataIndex.
6064      * @param {String} dataIndex The column dataIndex
6065      * @return {Object|Boolean} the column or false if not found
6066      */
6067     getColumnByDataIndex: function(dataIndex){
6068         var index = this.findColumnIndex(dataIndex);
6069         return index > -1 ? this.config[index] : false;
6070     },
6071     
6072     /**
6073      * Returns the index for a specified column id.
6074      * @param {String} id The column id
6075      * @return {Number} the index, or -1 if not found
6076      */
6077     getIndexById : function(id){
6078         for(var i = 0, len = this.config.length; i < len; i++){
6079             if(this.config[i].id == id){
6080                 return i;
6081             }
6082         }
6083         return -1;
6084     },
6085     
6086     /**
6087      * Returns the index for a specified column dataIndex.
6088      * @param {String} dataIndex The column dataIndex
6089      * @return {Number} the index, or -1 if not found
6090      */
6091     
6092     findColumnIndex : function(dataIndex){
6093         for(var i = 0, len = this.config.length; i < len; i++){
6094             if(this.config[i].dataIndex == dataIndex){
6095                 return i;
6096             }
6097         }
6098         return -1;
6099     },
6100     
6101     
6102     moveColumn : function(oldIndex, newIndex){
6103         var c = this.config[oldIndex];
6104         this.config.splice(oldIndex, 1);
6105         this.config.splice(newIndex, 0, c);
6106         this.dataMap = null;
6107         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6108     },
6109
6110     isLocked : function(colIndex){
6111         return this.config[colIndex].locked === true;
6112     },
6113
6114     setLocked : function(colIndex, value, suppressEvent){
6115         if(this.isLocked(colIndex) == value){
6116             return;
6117         }
6118         this.config[colIndex].locked = value;
6119         if(!suppressEvent){
6120             this.fireEvent("columnlockchange", this, colIndex, value);
6121         }
6122     },
6123
6124     getTotalLockedWidth : function(){
6125         var totalWidth = 0;
6126         for(var i = 0; i < this.config.length; i++){
6127             if(this.isLocked(i) && !this.isHidden(i)){
6128                 this.totalWidth += this.getColumnWidth(i);
6129             }
6130         }
6131         return totalWidth;
6132     },
6133
6134     getLockedCount : function(){
6135         for(var i = 0, len = this.config.length; i < len; i++){
6136             if(!this.isLocked(i)){
6137                 return i;
6138             }
6139         }
6140         
6141         return this.config.length;
6142     },
6143
6144     /**
6145      * Returns the number of columns.
6146      * @return {Number}
6147      */
6148     getColumnCount : function(visibleOnly){
6149         if(visibleOnly === true){
6150             var c = 0;
6151             for(var i = 0, len = this.config.length; i < len; i++){
6152                 if(!this.isHidden(i)){
6153                     c++;
6154                 }
6155             }
6156             return c;
6157         }
6158         return this.config.length;
6159     },
6160
6161     /**
6162      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6163      * @param {Function} fn
6164      * @param {Object} scope (optional)
6165      * @return {Array} result
6166      */
6167     getColumnsBy : function(fn, scope){
6168         var r = [];
6169         for(var i = 0, len = this.config.length; i < len; i++){
6170             var c = this.config[i];
6171             if(fn.call(scope||this, c, i) === true){
6172                 r[r.length] = c;
6173             }
6174         }
6175         return r;
6176     },
6177
6178     /**
6179      * Returns true if the specified column is sortable.
6180      * @param {Number} col The column index
6181      * @return {Boolean}
6182      */
6183     isSortable : function(col){
6184         if(typeof this.config[col].sortable == "undefined"){
6185             return this.defaultSortable;
6186         }
6187         return this.config[col].sortable;
6188     },
6189
6190     /**
6191      * Returns the rendering (formatting) function defined for the column.
6192      * @param {Number} col The column index.
6193      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6194      */
6195     getRenderer : function(col){
6196         if(!this.config[col].renderer){
6197             return Roo.grid.ColumnModel.defaultRenderer;
6198         }
6199         return this.config[col].renderer;
6200     },
6201
6202     /**
6203      * Sets the rendering (formatting) function for a column.
6204      * @param {Number} col The column index
6205      * @param {Function} fn The function to use to process the cell's raw data
6206      * to return HTML markup for the grid view. The render function is called with
6207      * the following parameters:<ul>
6208      * <li>Data value.</li>
6209      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6210      * <li>css A CSS style string to apply to the table cell.</li>
6211      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6212      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6213      * <li>Row index</li>
6214      * <li>Column index</li>
6215      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6216      */
6217     setRenderer : function(col, fn){
6218         this.config[col].renderer = fn;
6219     },
6220
6221     /**
6222      * Returns the width for the specified column.
6223      * @param {Number} col The column index
6224      * @return {Number}
6225      */
6226     getColumnWidth : function(col){
6227         return this.config[col].width * 1 || this.defaultWidth;
6228     },
6229
6230     /**
6231      * Sets the width for a column.
6232      * @param {Number} col The column index
6233      * @param {Number} width The new width
6234      */
6235     setColumnWidth : function(col, width, suppressEvent){
6236         this.config[col].width = width;
6237         this.totalWidth = null;
6238         if(!suppressEvent){
6239              this.fireEvent("widthchange", this, col, width);
6240         }
6241     },
6242
6243     /**
6244      * Returns the total width of all columns.
6245      * @param {Boolean} includeHidden True to include hidden column widths
6246      * @return {Number}
6247      */
6248     getTotalWidth : function(includeHidden){
6249         if(!this.totalWidth){
6250             this.totalWidth = 0;
6251             for(var i = 0, len = this.config.length; i < len; i++){
6252                 if(includeHidden || !this.isHidden(i)){
6253                     this.totalWidth += this.getColumnWidth(i);
6254                 }
6255             }
6256         }
6257         return this.totalWidth;
6258     },
6259
6260     /**
6261      * Returns the header for the specified column.
6262      * @param {Number} col The column index
6263      * @return {String}
6264      */
6265     getColumnHeader : function(col){
6266         return this.config[col].header;
6267     },
6268
6269     /**
6270      * Sets the header for a column.
6271      * @param {Number} col The column index
6272      * @param {String} header The new header
6273      */
6274     setColumnHeader : function(col, header){
6275         this.config[col].header = header;
6276         this.fireEvent("headerchange", this, col, header);
6277     },
6278
6279     /**
6280      * Returns the tooltip for the specified column.
6281      * @param {Number} col The column index
6282      * @return {String}
6283      */
6284     getColumnTooltip : function(col){
6285             return this.config[col].tooltip;
6286     },
6287     /**
6288      * Sets the tooltip for a column.
6289      * @param {Number} col The column index
6290      * @param {String} tooltip The new tooltip
6291      */
6292     setColumnTooltip : function(col, tooltip){
6293             this.config[col].tooltip = tooltip;
6294     },
6295
6296     /**
6297      * Returns the dataIndex for the specified column.
6298      * @param {Number} col The column index
6299      * @return {Number}
6300      */
6301     getDataIndex : function(col){
6302         return this.config[col].dataIndex;
6303     },
6304
6305     /**
6306      * Sets the dataIndex for a column.
6307      * @param {Number} col The column index
6308      * @param {Number} dataIndex The new dataIndex
6309      */
6310     setDataIndex : function(col, dataIndex){
6311         this.config[col].dataIndex = dataIndex;
6312     },
6313
6314     
6315     
6316     /**
6317      * Returns true if the cell is editable.
6318      * @param {Number} colIndex The column index
6319      * @param {Number} rowIndex The row index - this is nto actually used..?
6320      * @return {Boolean}
6321      */
6322     isCellEditable : function(colIndex, rowIndex){
6323         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6324     },
6325
6326     /**
6327      * Returns the editor defined for the cell/column.
6328      * return false or null to disable editing.
6329      * @param {Number} colIndex The column index
6330      * @param {Number} rowIndex The row index
6331      * @return {Object}
6332      */
6333     getCellEditor : function(colIndex, rowIndex){
6334         return this.config[colIndex].editor;
6335     },
6336
6337     /**
6338      * Sets if a column is editable.
6339      * @param {Number} col The column index
6340      * @param {Boolean} editable True if the column is editable
6341      */
6342     setEditable : function(col, editable){
6343         this.config[col].editable = editable;
6344     },
6345
6346
6347     /**
6348      * Returns true if the column is hidden.
6349      * @param {Number} colIndex The column index
6350      * @return {Boolean}
6351      */
6352     isHidden : function(colIndex){
6353         return this.config[colIndex].hidden;
6354     },
6355
6356
6357     /**
6358      * Returns true if the column width cannot be changed
6359      */
6360     isFixed : function(colIndex){
6361         return this.config[colIndex].fixed;
6362     },
6363
6364     /**
6365      * Returns true if the column can be resized
6366      * @return {Boolean}
6367      */
6368     isResizable : function(colIndex){
6369         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6370     },
6371     /**
6372      * Sets if a column is hidden.
6373      * @param {Number} colIndex The column index
6374      * @param {Boolean} hidden True if the column is hidden
6375      */
6376     setHidden : function(colIndex, hidden){
6377         this.config[colIndex].hidden = hidden;
6378         this.totalWidth = null;
6379         this.fireEvent("hiddenchange", this, colIndex, hidden);
6380     },
6381
6382     /**
6383      * Sets the editor for a column.
6384      * @param {Number} col The column index
6385      * @param {Object} editor The editor object
6386      */
6387     setEditor : function(col, editor){
6388         this.config[col].editor = editor;
6389     }
6390 });
6391
6392 Roo.grid.ColumnModel.defaultRenderer = function(value)
6393 {
6394     if(typeof value == "object") {
6395         return value;
6396     }
6397         if(typeof value == "string" && value.length < 1){
6398             return "&#160;";
6399         }
6400     
6401         return String.format("{0}", value);
6402 };
6403
6404 // Alias for backwards compatibility
6405 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6406 /*
6407  * Based on:
6408  * Ext JS Library 1.1.1
6409  * Copyright(c) 2006-2007, Ext JS, LLC.
6410  *
6411  * Originally Released Under LGPL - original licence link has changed is not relivant.
6412  *
6413  * Fork - LGPL
6414  * <script type="text/javascript">
6415  */
6416  
6417 /**
6418  * @class Roo.LoadMask
6419  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6420  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6421  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6422  * element's UpdateManager load indicator and will be destroyed after the initial load.
6423  * @constructor
6424  * Create a new LoadMask
6425  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6426  * @param {Object} config The config object
6427  */
6428 Roo.LoadMask = function(el, config){
6429     this.el = Roo.get(el);
6430     Roo.apply(this, config);
6431     if(this.store){
6432         this.store.on('beforeload', this.onBeforeLoad, this);
6433         this.store.on('load', this.onLoad, this);
6434         this.store.on('loadexception', this.onLoadException, this);
6435         this.removeMask = false;
6436     }else{
6437         var um = this.el.getUpdateManager();
6438         um.showLoadIndicator = false; // disable the default indicator
6439         um.on('beforeupdate', this.onBeforeLoad, this);
6440         um.on('update', this.onLoad, this);
6441         um.on('failure', this.onLoad, this);
6442         this.removeMask = true;
6443     }
6444 };
6445
6446 Roo.LoadMask.prototype = {
6447     /**
6448      * @cfg {Boolean} removeMask
6449      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6450      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6451      */
6452     /**
6453      * @cfg {String} msg
6454      * The text to display in a centered loading message box (defaults to 'Loading...')
6455      */
6456     msg : 'Loading...',
6457     /**
6458      * @cfg {String} msgCls
6459      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6460      */
6461     msgCls : 'x-mask-loading',
6462
6463     /**
6464      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6465      * @type Boolean
6466      */
6467     disabled: false,
6468
6469     /**
6470      * Disables the mask to prevent it from being displayed
6471      */
6472     disable : function(){
6473        this.disabled = true;
6474     },
6475
6476     /**
6477      * Enables the mask so that it can be displayed
6478      */
6479     enable : function(){
6480         this.disabled = false;
6481     },
6482     
6483     onLoadException : function()
6484     {
6485         Roo.log(arguments);
6486         
6487         if (typeof(arguments[3]) != 'undefined') {
6488             Roo.MessageBox.alert("Error loading",arguments[3]);
6489         } 
6490         /*
6491         try {
6492             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6493                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6494             }   
6495         } catch(e) {
6496             
6497         }
6498         */
6499     
6500         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6501     },
6502     // private
6503     onLoad : function()
6504     {
6505         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6506     },
6507
6508     // private
6509     onBeforeLoad : function(){
6510         if(!this.disabled){
6511             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6512         }
6513     },
6514
6515     // private
6516     destroy : function(){
6517         if(this.store){
6518             this.store.un('beforeload', this.onBeforeLoad, this);
6519             this.store.un('load', this.onLoad, this);
6520             this.store.un('loadexception', this.onLoadException, this);
6521         }else{
6522             var um = this.el.getUpdateManager();
6523             um.un('beforeupdate', this.onBeforeLoad, this);
6524             um.un('update', this.onLoad, this);
6525             um.un('failure', this.onLoad, this);
6526         }
6527     }
6528 };/*
6529  * - LGPL
6530  *
6531  * table
6532  * 
6533  */
6534
6535 /**
6536  * @class Roo.bootstrap.Table
6537  * @extends Roo.bootstrap.Component
6538  * Bootstrap Table class
6539  * @cfg {String} cls table class
6540  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6541  * @cfg {String} bgcolor Specifies the background color for a table
6542  * @cfg {Number} border Specifies whether the table cells should have borders or not
6543  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6544  * @cfg {Number} cellspacing Specifies the space between cells
6545  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6546  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6547  * @cfg {String} sortable Specifies that the table should be sortable
6548  * @cfg {String} summary Specifies a summary of the content of a table
6549  * @cfg {Number} width Specifies the width of a table
6550  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6551  * 
6552  * @cfg {boolean} striped Should the rows be alternative striped
6553  * @cfg {boolean} bordered Add borders to the table
6554  * @cfg {boolean} hover Add hover highlighting
6555  * @cfg {boolean} condensed Format condensed
6556  * @cfg {boolean} responsive Format condensed
6557  * @cfg {Boolean} loadMask (true|false) default false
6558  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6559  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6560  * @cfg {Boolean} rowSelection (true|false) default false
6561  * @cfg {Boolean} cellSelection (true|false) default false
6562  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6563  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6564  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6565  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6566  
6567  * 
6568  * @constructor
6569  * Create a new Table
6570  * @param {Object} config The config object
6571  */
6572
6573 Roo.bootstrap.Table = function(config){
6574     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6575     
6576   
6577     
6578     // BC...
6579     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6580     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6581     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6582     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6583     
6584     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6585     if (this.sm) {
6586         this.sm.grid = this;
6587         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6588         this.sm = this.selModel;
6589         this.sm.xmodule = this.xmodule || false;
6590     }
6591     
6592     if (this.cm && typeof(this.cm.config) == 'undefined') {
6593         this.colModel = new Roo.grid.ColumnModel(this.cm);
6594         this.cm = this.colModel;
6595         this.cm.xmodule = this.xmodule || false;
6596     }
6597     if (this.store) {
6598         this.store= Roo.factory(this.store, Roo.data);
6599         this.ds = this.store;
6600         this.ds.xmodule = this.xmodule || false;
6601          
6602     }
6603     if (this.footer && this.store) {
6604         this.footer.dataSource = this.ds;
6605         this.footer = Roo.factory(this.footer);
6606     }
6607     
6608     /** @private */
6609     this.addEvents({
6610         /**
6611          * @event cellclick
6612          * Fires when a cell is clicked
6613          * @param {Roo.bootstrap.Table} this
6614          * @param {Roo.Element} el
6615          * @param {Number} rowIndex
6616          * @param {Number} columnIndex
6617          * @param {Roo.EventObject} e
6618          */
6619         "cellclick" : true,
6620         /**
6621          * @event celldblclick
6622          * Fires when a cell is double clicked
6623          * @param {Roo.bootstrap.Table} this
6624          * @param {Roo.Element} el
6625          * @param {Number} rowIndex
6626          * @param {Number} columnIndex
6627          * @param {Roo.EventObject} e
6628          */
6629         "celldblclick" : true,
6630         /**
6631          * @event rowclick
6632          * Fires when a row is clicked
6633          * @param {Roo.bootstrap.Table} this
6634          * @param {Roo.Element} el
6635          * @param {Number} rowIndex
6636          * @param {Roo.EventObject} e
6637          */
6638         "rowclick" : true,
6639         /**
6640          * @event rowdblclick
6641          * Fires when a row is double clicked
6642          * @param {Roo.bootstrap.Table} this
6643          * @param {Roo.Element} el
6644          * @param {Number} rowIndex
6645          * @param {Roo.EventObject} e
6646          */
6647         "rowdblclick" : true,
6648         /**
6649          * @event mouseover
6650          * Fires when a mouseover occur
6651          * @param {Roo.bootstrap.Table} this
6652          * @param {Roo.Element} el
6653          * @param {Number} rowIndex
6654          * @param {Number} columnIndex
6655          * @param {Roo.EventObject} e
6656          */
6657         "mouseover" : true,
6658         /**
6659          * @event mouseout
6660          * Fires when a mouseout occur
6661          * @param {Roo.bootstrap.Table} this
6662          * @param {Roo.Element} el
6663          * @param {Number} rowIndex
6664          * @param {Number} columnIndex
6665          * @param {Roo.EventObject} e
6666          */
6667         "mouseout" : true,
6668         /**
6669          * @event rowclass
6670          * Fires when a row is rendered, so you can change add a style to it.
6671          * @param {Roo.bootstrap.Table} this
6672          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6673          */
6674         'rowclass' : true,
6675           /**
6676          * @event rowsrendered
6677          * Fires when all the  rows have been rendered
6678          * @param {Roo.bootstrap.Table} this
6679          */
6680         'rowsrendered' : true,
6681         /**
6682          * @event contextmenu
6683          * The raw contextmenu event for the entire grid.
6684          * @param {Roo.EventObject} e
6685          */
6686         "contextmenu" : true,
6687         /**
6688          * @event rowcontextmenu
6689          * Fires when a row is right clicked
6690          * @param {Roo.bootstrap.Table} this
6691          * @param {Number} rowIndex
6692          * @param {Roo.EventObject} e
6693          */
6694         "rowcontextmenu" : true,
6695         /**
6696          * @event cellcontextmenu
6697          * Fires when a cell is right clicked
6698          * @param {Roo.bootstrap.Table} this
6699          * @param {Number} rowIndex
6700          * @param {Number} cellIndex
6701          * @param {Roo.EventObject} e
6702          */
6703          "cellcontextmenu" : true,
6704          /**
6705          * @event headercontextmenu
6706          * Fires when a header is right clicked
6707          * @param {Roo.bootstrap.Table} this
6708          * @param {Number} columnIndex
6709          * @param {Roo.EventObject} e
6710          */
6711         "headercontextmenu" : true
6712     });
6713 };
6714
6715 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6716     
6717     cls: false,
6718     align: false,
6719     bgcolor: false,
6720     border: false,
6721     cellpadding: false,
6722     cellspacing: false,
6723     frame: false,
6724     rules: false,
6725     sortable: false,
6726     summary: false,
6727     width: false,
6728     striped : false,
6729     scrollBody : false,
6730     bordered: false,
6731     hover:  false,
6732     condensed : false,
6733     responsive : false,
6734     sm : false,
6735     cm : false,
6736     store : false,
6737     loadMask : false,
6738     footerShow : true,
6739     headerShow : true,
6740   
6741     rowSelection : false,
6742     cellSelection : false,
6743     layout : false,
6744     
6745     // Roo.Element - the tbody
6746     mainBody: false,
6747     // Roo.Element - thead element
6748     mainHead: false,
6749     
6750     container: false, // used by gridpanel...
6751     
6752     lazyLoad : false,
6753     
6754     CSS : Roo.util.CSS,
6755     
6756     auto_hide_footer : false,
6757     
6758     getAutoCreate : function()
6759     {
6760         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6761         
6762         cfg = {
6763             tag: 'table',
6764             cls : 'table',
6765             cn : []
6766         };
6767         if (this.scrollBody) {
6768             cfg.cls += ' table-body-fixed';
6769         }    
6770         if (this.striped) {
6771             cfg.cls += ' table-striped';
6772         }
6773         
6774         if (this.hover) {
6775             cfg.cls += ' table-hover';
6776         }
6777         if (this.bordered) {
6778             cfg.cls += ' table-bordered';
6779         }
6780         if (this.condensed) {
6781             cfg.cls += ' table-condensed';
6782         }
6783         if (this.responsive) {
6784             cfg.cls += ' table-responsive';
6785         }
6786         
6787         if (this.cls) {
6788             cfg.cls+=  ' ' +this.cls;
6789         }
6790         
6791         // this lot should be simplifed...
6792         var _t = this;
6793         var cp = [
6794             'align',
6795             'bgcolor',
6796             'border',
6797             'cellpadding',
6798             'cellspacing',
6799             'frame',
6800             'rules',
6801             'sortable',
6802             'summary',
6803             'width'
6804         ].forEach(function(k) {
6805             if (_t[k]) {
6806                 cfg[k] = _t[k];
6807             }
6808         });
6809         
6810         
6811         if (this.layout) {
6812             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6813         }
6814         
6815         if(this.store || this.cm){
6816             if(this.headerShow){
6817                 cfg.cn.push(this.renderHeader());
6818             }
6819             
6820             cfg.cn.push(this.renderBody());
6821             
6822             if(this.footerShow){
6823                 cfg.cn.push(this.renderFooter());
6824             }
6825             // where does this come from?
6826             //cfg.cls+=  ' TableGrid';
6827         }
6828         
6829         return { cn : [ cfg ] };
6830     },
6831     
6832     initEvents : function()
6833     {   
6834         if(!this.store || !this.cm){
6835             return;
6836         }
6837         if (this.selModel) {
6838             this.selModel.initEvents();
6839         }
6840         
6841         
6842         //Roo.log('initEvents with ds!!!!');
6843         
6844         this.mainBody = this.el.select('tbody', true).first();
6845         this.mainHead = this.el.select('thead', true).first();
6846         this.mainFoot = this.el.select('tfoot', true).first();
6847         
6848         
6849         
6850         var _this = this;
6851         
6852         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6853             e.on('click', _this.sort, _this);
6854         });
6855         
6856         this.mainBody.on("click", this.onClick, this);
6857         this.mainBody.on("dblclick", this.onDblClick, this);
6858         
6859         // why is this done????? = it breaks dialogs??
6860         //this.parent().el.setStyle('position', 'relative');
6861         
6862         
6863         if (this.footer) {
6864             this.footer.parentId = this.id;
6865             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6866             
6867             if(this.lazyLoad){
6868                 this.el.select('tfoot tr td').first().addClass('hide');
6869             }
6870         } 
6871         
6872         if(this.loadMask) {
6873             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6874         }
6875         
6876         this.store.on('load', this.onLoad, this);
6877         this.store.on('beforeload', this.onBeforeLoad, this);
6878         this.store.on('update', this.onUpdate, this);
6879         this.store.on('add', this.onAdd, this);
6880         this.store.on("clear", this.clear, this);
6881         
6882         this.el.on("contextmenu", this.onContextMenu, this);
6883         
6884         this.mainBody.on('scroll', this.onBodyScroll, this);
6885         
6886         this.cm.on("headerchange", this.onHeaderChange, this);
6887         
6888         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6889         
6890     },
6891     
6892     onContextMenu : function(e, t)
6893     {
6894         this.processEvent("contextmenu", e);
6895     },
6896     
6897     processEvent : function(name, e)
6898     {
6899         if (name != 'touchstart' ) {
6900             this.fireEvent(name, e);    
6901         }
6902         
6903         var t = e.getTarget();
6904         
6905         var cell = Roo.get(t);
6906         
6907         if(!cell){
6908             return;
6909         }
6910         
6911         if(cell.findParent('tfoot', false, true)){
6912             return;
6913         }
6914         
6915         if(cell.findParent('thead', false, true)){
6916             
6917             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6918                 cell = Roo.get(t).findParent('th', false, true);
6919                 if (!cell) {
6920                     Roo.log("failed to find th in thead?");
6921                     Roo.log(e.getTarget());
6922                     return;
6923                 }
6924             }
6925             
6926             var cellIndex = cell.dom.cellIndex;
6927             
6928             var ename = name == 'touchstart' ? 'click' : name;
6929             this.fireEvent("header" + ename, this, cellIndex, e);
6930             
6931             return;
6932         }
6933         
6934         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6935             cell = Roo.get(t).findParent('td', false, true);
6936             if (!cell) {
6937                 Roo.log("failed to find th in tbody?");
6938                 Roo.log(e.getTarget());
6939                 return;
6940             }
6941         }
6942         
6943         var row = cell.findParent('tr', false, true);
6944         var cellIndex = cell.dom.cellIndex;
6945         var rowIndex = row.dom.rowIndex - 1;
6946         
6947         if(row !== false){
6948             
6949             this.fireEvent("row" + name, this, rowIndex, e);
6950             
6951             if(cell !== false){
6952             
6953                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6954             }
6955         }
6956         
6957     },
6958     
6959     onMouseover : function(e, el)
6960     {
6961         var cell = Roo.get(el);
6962         
6963         if(!cell){
6964             return;
6965         }
6966         
6967         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6968             cell = cell.findParent('td', false, true);
6969         }
6970         
6971         var row = cell.findParent('tr', false, true);
6972         var cellIndex = cell.dom.cellIndex;
6973         var rowIndex = row.dom.rowIndex - 1; // start from 0
6974         
6975         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6976         
6977     },
6978     
6979     onMouseout : function(e, el)
6980     {
6981         var cell = Roo.get(el);
6982         
6983         if(!cell){
6984             return;
6985         }
6986         
6987         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6988             cell = cell.findParent('td', false, true);
6989         }
6990         
6991         var row = cell.findParent('tr', false, true);
6992         var cellIndex = cell.dom.cellIndex;
6993         var rowIndex = row.dom.rowIndex - 1; // start from 0
6994         
6995         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6996         
6997     },
6998     
6999     onClick : function(e, el)
7000     {
7001         var cell = Roo.get(el);
7002         
7003         if(!cell || (!this.cellSelection && !this.rowSelection)){
7004             return;
7005         }
7006         
7007         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7008             cell = cell.findParent('td', false, true);
7009         }
7010         
7011         if(!cell || typeof(cell) == 'undefined'){
7012             return;
7013         }
7014         
7015         var row = cell.findParent('tr', false, true);
7016         
7017         if(!row || typeof(row) == 'undefined'){
7018             return;
7019         }
7020         
7021         var cellIndex = cell.dom.cellIndex;
7022         var rowIndex = this.getRowIndex(row);
7023         
7024         // why??? - should these not be based on SelectionModel?
7025         if(this.cellSelection){
7026             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7027         }
7028         
7029         if(this.rowSelection){
7030             this.fireEvent('rowclick', this, row, rowIndex, e);
7031         }
7032         
7033         
7034     },
7035         
7036     onDblClick : function(e,el)
7037     {
7038         var cell = Roo.get(el);
7039         
7040         if(!cell || (!this.cellSelection && !this.rowSelection)){
7041             return;
7042         }
7043         
7044         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7045             cell = cell.findParent('td', false, true);
7046         }
7047         
7048         if(!cell || typeof(cell) == 'undefined'){
7049             return;
7050         }
7051         
7052         var row = cell.findParent('tr', false, true);
7053         
7054         if(!row || typeof(row) == 'undefined'){
7055             return;
7056         }
7057         
7058         var cellIndex = cell.dom.cellIndex;
7059         var rowIndex = this.getRowIndex(row);
7060         
7061         if(this.cellSelection){
7062             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7063         }
7064         
7065         if(this.rowSelection){
7066             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7067         }
7068     },
7069     
7070     sort : function(e,el)
7071     {
7072         var col = Roo.get(el);
7073         
7074         if(!col.hasClass('sortable')){
7075             return;
7076         }
7077         
7078         var sort = col.attr('sort');
7079         var dir = 'ASC';
7080         
7081         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7082             dir = 'DESC';
7083         }
7084         
7085         this.store.sortInfo = {field : sort, direction : dir};
7086         
7087         if (this.footer) {
7088             Roo.log("calling footer first");
7089             this.footer.onClick('first');
7090         } else {
7091         
7092             this.store.load({ params : { start : 0 } });
7093         }
7094     },
7095     
7096     renderHeader : function()
7097     {
7098         var header = {
7099             tag: 'thead',
7100             cn : []
7101         };
7102         
7103         var cm = this.cm;
7104         this.totalWidth = 0;
7105         
7106         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7107             
7108             var config = cm.config[i];
7109             
7110             var c = {
7111                 tag: 'th',
7112                 cls : 'x-hcol-' + i,
7113                 style : '',
7114                 html: cm.getColumnHeader(i)
7115             };
7116             
7117             var hh = '';
7118             
7119             if(typeof(config.sortable) != 'undefined' && config.sortable){
7120                 c.cls = 'sortable';
7121                 c.html = '<i class="glyphicon"></i>' + c.html;
7122             }
7123             
7124             // could use BS4 hidden-..-down 
7125             
7126             if(typeof(config.lgHeader) != 'undefined'){
7127                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7128             }
7129             
7130             if(typeof(config.mdHeader) != 'undefined'){
7131                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7132             }
7133             
7134             if(typeof(config.smHeader) != 'undefined'){
7135                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7136             }
7137             
7138             if(typeof(config.xsHeader) != 'undefined'){
7139                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7140             }
7141             
7142             if(hh.length){
7143                 c.html = hh;
7144             }
7145             
7146             if(typeof(config.tooltip) != 'undefined'){
7147                 c.tooltip = config.tooltip;
7148             }
7149             
7150             if(typeof(config.colspan) != 'undefined'){
7151                 c.colspan = config.colspan;
7152             }
7153             
7154             if(typeof(config.hidden) != 'undefined' && config.hidden){
7155                 c.style += ' display:none;';
7156             }
7157             
7158             if(typeof(config.dataIndex) != 'undefined'){
7159                 c.sort = config.dataIndex;
7160             }
7161             
7162            
7163             
7164             if(typeof(config.align) != 'undefined' && config.align.length){
7165                 c.style += ' text-align:' + config.align + ';';
7166             }
7167             
7168             if(typeof(config.width) != 'undefined'){
7169                 c.style += ' width:' + config.width + 'px;';
7170                 this.totalWidth += config.width;
7171             } else {
7172                 this.totalWidth += 100; // assume minimum of 100 per column?
7173             }
7174             
7175             if(typeof(config.cls) != 'undefined'){
7176                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7177             }
7178             
7179             ['xs','sm','md','lg'].map(function(size){
7180                 
7181                 if(typeof(config[size]) == 'undefined'){
7182                     return;
7183                 }
7184                  
7185                 if (!config[size]) { // 0 = hidden
7186                     // BS 4 '0' is treated as hide that column and below.
7187                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7188                     return;
7189                 }
7190                 
7191                 c.cls += ' col-' + size + '-' + config[size] + (
7192                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7193                 );
7194                 
7195                 
7196             });
7197             
7198             header.cn.push(c)
7199         }
7200         
7201         return header;
7202     },
7203     
7204     renderBody : function()
7205     {
7206         var body = {
7207             tag: 'tbody',
7208             cn : [
7209                 {
7210                     tag: 'tr',
7211                     cn : [
7212                         {
7213                             tag : 'td',
7214                             colspan :  this.cm.getColumnCount()
7215                         }
7216                     ]
7217                 }
7218             ]
7219         };
7220         
7221         return body;
7222     },
7223     
7224     renderFooter : function()
7225     {
7226         var footer = {
7227             tag: 'tfoot',
7228             cn : [
7229                 {
7230                     tag: 'tr',
7231                     cn : [
7232                         {
7233                             tag : 'td',
7234                             colspan :  this.cm.getColumnCount()
7235                         }
7236                     ]
7237                 }
7238             ]
7239         };
7240         
7241         return footer;
7242     },
7243     
7244     
7245     
7246     onLoad : function()
7247     {
7248 //        Roo.log('ds onload');
7249         this.clear();
7250         
7251         var _this = this;
7252         var cm = this.cm;
7253         var ds = this.store;
7254         
7255         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7256             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7257             if (_this.store.sortInfo) {
7258                     
7259                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7260                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7261                 }
7262                 
7263                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7264                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7265                 }
7266             }
7267         });
7268         
7269         var tbody =  this.mainBody;
7270               
7271         if(ds.getCount() > 0){
7272             ds.data.each(function(d,rowIndex){
7273                 var row =  this.renderRow(cm, ds, rowIndex);
7274                 
7275                 tbody.createChild(row);
7276                 
7277                 var _this = this;
7278                 
7279                 if(row.cellObjects.length){
7280                     Roo.each(row.cellObjects, function(r){
7281                         _this.renderCellObject(r);
7282                     })
7283                 }
7284                 
7285             }, this);
7286         }
7287         
7288         var tfoot = this.el.select('tfoot', true).first();
7289         
7290         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7291             
7292             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7293             
7294             var total = this.ds.getTotalCount();
7295             
7296             if(this.footer.pageSize < total){
7297                 this.mainFoot.show();
7298             }
7299         }
7300         
7301         Roo.each(this.el.select('tbody td', true).elements, function(e){
7302             e.on('mouseover', _this.onMouseover, _this);
7303         });
7304         
7305         Roo.each(this.el.select('tbody td', true).elements, function(e){
7306             e.on('mouseout', _this.onMouseout, _this);
7307         });
7308         this.fireEvent('rowsrendered', this);
7309         
7310         this.autoSize();
7311     },
7312     
7313     
7314     onUpdate : function(ds,record)
7315     {
7316         this.refreshRow(record);
7317         this.autoSize();
7318     },
7319     
7320     onRemove : function(ds, record, index, isUpdate){
7321         if(isUpdate !== true){
7322             this.fireEvent("beforerowremoved", this, index, record);
7323         }
7324         var bt = this.mainBody.dom;
7325         
7326         var rows = this.el.select('tbody > tr', true).elements;
7327         
7328         if(typeof(rows[index]) != 'undefined'){
7329             bt.removeChild(rows[index].dom);
7330         }
7331         
7332 //        if(bt.rows[index]){
7333 //            bt.removeChild(bt.rows[index]);
7334 //        }
7335         
7336         if(isUpdate !== true){
7337             //this.stripeRows(index);
7338             //this.syncRowHeights(index, index);
7339             //this.layout();
7340             this.fireEvent("rowremoved", this, index, record);
7341         }
7342     },
7343     
7344     onAdd : function(ds, records, rowIndex)
7345     {
7346         //Roo.log('on Add called');
7347         // - note this does not handle multiple adding very well..
7348         var bt = this.mainBody.dom;
7349         for (var i =0 ; i < records.length;i++) {
7350             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7351             //Roo.log(records[i]);
7352             //Roo.log(this.store.getAt(rowIndex+i));
7353             this.insertRow(this.store, rowIndex + i, false);
7354             return;
7355         }
7356         
7357     },
7358     
7359     
7360     refreshRow : function(record){
7361         var ds = this.store, index;
7362         if(typeof record == 'number'){
7363             index = record;
7364             record = ds.getAt(index);
7365         }else{
7366             index = ds.indexOf(record);
7367         }
7368         this.insertRow(ds, index, true);
7369         this.autoSize();
7370         this.onRemove(ds, record, index+1, true);
7371         this.autoSize();
7372         //this.syncRowHeights(index, index);
7373         //this.layout();
7374         this.fireEvent("rowupdated", this, index, record);
7375     },
7376     
7377     insertRow : function(dm, rowIndex, isUpdate){
7378         
7379         if(!isUpdate){
7380             this.fireEvent("beforerowsinserted", this, rowIndex);
7381         }
7382             //var s = this.getScrollState();
7383         var row = this.renderRow(this.cm, this.store, rowIndex);
7384         // insert before rowIndex..
7385         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7386         
7387         var _this = this;
7388                 
7389         if(row.cellObjects.length){
7390             Roo.each(row.cellObjects, function(r){
7391                 _this.renderCellObject(r);
7392             })
7393         }
7394             
7395         if(!isUpdate){
7396             this.fireEvent("rowsinserted", this, rowIndex);
7397             //this.syncRowHeights(firstRow, lastRow);
7398             //this.stripeRows(firstRow);
7399             //this.layout();
7400         }
7401         
7402     },
7403     
7404     
7405     getRowDom : function(rowIndex)
7406     {
7407         var rows = this.el.select('tbody > tr', true).elements;
7408         
7409         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7410         
7411     },
7412     // returns the object tree for a tr..
7413   
7414     
7415     renderRow : function(cm, ds, rowIndex) 
7416     {
7417         var d = ds.getAt(rowIndex);
7418         
7419         var row = {
7420             tag : 'tr',
7421             cls : 'x-row-' + rowIndex,
7422             cn : []
7423         };
7424             
7425         var cellObjects = [];
7426         
7427         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7428             var config = cm.config[i];
7429             
7430             var renderer = cm.getRenderer(i);
7431             var value = '';
7432             var id = false;
7433             
7434             if(typeof(renderer) !== 'undefined'){
7435                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7436             }
7437             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7438             // and are rendered into the cells after the row is rendered - using the id for the element.
7439             
7440             if(typeof(value) === 'object'){
7441                 id = Roo.id();
7442                 cellObjects.push({
7443                     container : id,
7444                     cfg : value 
7445                 })
7446             }
7447             
7448             var rowcfg = {
7449                 record: d,
7450                 rowIndex : rowIndex,
7451                 colIndex : i,
7452                 rowClass : ''
7453             };
7454
7455             this.fireEvent('rowclass', this, rowcfg);
7456             
7457             var td = {
7458                 tag: 'td',
7459                 cls : rowcfg.rowClass + ' x-col-' + i,
7460                 style: '',
7461                 html: (typeof(value) === 'object') ? '' : value
7462             };
7463             
7464             if (id) {
7465                 td.id = id;
7466             }
7467             
7468             if(typeof(config.colspan) != 'undefined'){
7469                 td.colspan = config.colspan;
7470             }
7471             
7472             if(typeof(config.hidden) != 'undefined' && config.hidden){
7473                 td.style += ' display:none;';
7474             }
7475             
7476             if(typeof(config.align) != 'undefined' && config.align.length){
7477                 td.style += ' text-align:' + config.align + ';';
7478             }
7479             if(typeof(config.valign) != 'undefined' && config.valign.length){
7480                 td.style += ' vertical-align:' + config.valign + ';';
7481             }
7482             
7483             if(typeof(config.width) != 'undefined'){
7484                 td.style += ' width:' +  config.width + 'px;';
7485             }
7486             
7487             if(typeof(config.cursor) != 'undefined'){
7488                 td.style += ' cursor:' +  config.cursor + ';';
7489             }
7490             
7491             if(typeof(config.cls) != 'undefined'){
7492                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7493             }
7494             
7495             ['xs','sm','md','lg'].map(function(size){
7496                 
7497                 if(typeof(config[size]) == 'undefined'){
7498                     return;
7499                 }
7500                 
7501                 
7502                   
7503                 if (!config[size]) { // 0 = hidden
7504                     // BS 4 '0' is treated as hide that column and below.
7505                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7506                     return;
7507                 }
7508                 
7509                 td.cls += ' col-' + size + '-' + config[size] + (
7510                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7511                 );
7512                  
7513
7514             });
7515             
7516             row.cn.push(td);
7517            
7518         }
7519         
7520         row.cellObjects = cellObjects;
7521         
7522         return row;
7523           
7524     },
7525     
7526     
7527     
7528     onBeforeLoad : function()
7529     {
7530         
7531     },
7532      /**
7533      * Remove all rows
7534      */
7535     clear : function()
7536     {
7537         this.el.select('tbody', true).first().dom.innerHTML = '';
7538     },
7539     /**
7540      * Show or hide a row.
7541      * @param {Number} rowIndex to show or hide
7542      * @param {Boolean} state hide
7543      */
7544     setRowVisibility : function(rowIndex, state)
7545     {
7546         var bt = this.mainBody.dom;
7547         
7548         var rows = this.el.select('tbody > tr', true).elements;
7549         
7550         if(typeof(rows[rowIndex]) == 'undefined'){
7551             return;
7552         }
7553         rows[rowIndex].dom.style.display = state ? '' : 'none';
7554     },
7555     
7556     
7557     getSelectionModel : function(){
7558         if(!this.selModel){
7559             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7560         }
7561         return this.selModel;
7562     },
7563     /*
7564      * Render the Roo.bootstrap object from renderder
7565      */
7566     renderCellObject : function(r)
7567     {
7568         var _this = this;
7569         
7570         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7571         
7572         var t = r.cfg.render(r.container);
7573         
7574         if(r.cfg.cn){
7575             Roo.each(r.cfg.cn, function(c){
7576                 var child = {
7577                     container: t.getChildContainer(),
7578                     cfg: c
7579                 };
7580                 _this.renderCellObject(child);
7581             })
7582         }
7583     },
7584     
7585     getRowIndex : function(row)
7586     {
7587         var rowIndex = -1;
7588         
7589         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7590             if(el != row){
7591                 return;
7592             }
7593             
7594             rowIndex = index;
7595         });
7596         
7597         return rowIndex;
7598     },
7599      /**
7600      * Returns the grid's underlying element = used by panel.Grid
7601      * @return {Element} The element
7602      */
7603     getGridEl : function(){
7604         return this.el;
7605     },
7606      /**
7607      * Forces a resize - used by panel.Grid
7608      * @return {Element} The element
7609      */
7610     autoSize : function()
7611     {
7612         //var ctr = Roo.get(this.container.dom.parentElement);
7613         var ctr = Roo.get(this.el.dom);
7614         
7615         var thd = this.getGridEl().select('thead',true).first();
7616         var tbd = this.getGridEl().select('tbody', true).first();
7617         var tfd = this.getGridEl().select('tfoot', true).first();
7618         
7619         var cw = ctr.getWidth();
7620         
7621         if (tbd) {
7622             
7623             tbd.setWidth(ctr.getWidth());
7624             // if the body has a max height - and then scrolls - we should perhaps set up the height here
7625             // this needs fixing for various usage - currently only hydra job advers I think..
7626             //tdb.setHeight(
7627             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7628             //); 
7629             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7630             cw -= barsize;
7631         }
7632         cw = Math.max(cw, this.totalWidth);
7633         this.getGridEl().select('tr',true).setWidth(cw);
7634         // resize 'expandable coloumn?
7635         
7636         return; // we doe not have a view in this design..
7637         
7638     },
7639     onBodyScroll: function()
7640     {
7641         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7642         if(this.mainHead){
7643             this.mainHead.setStyle({
7644                 'position' : 'relative',
7645                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7646             });
7647         }
7648         
7649         if(this.lazyLoad){
7650             
7651             var scrollHeight = this.mainBody.dom.scrollHeight;
7652             
7653             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7654             
7655             var height = this.mainBody.getHeight();
7656             
7657             if(scrollHeight - height == scrollTop) {
7658                 
7659                 var total = this.ds.getTotalCount();
7660                 
7661                 if(this.footer.cursor + this.footer.pageSize < total){
7662                     
7663                     this.footer.ds.load({
7664                         params : {
7665                             start : this.footer.cursor + this.footer.pageSize,
7666                             limit : this.footer.pageSize
7667                         },
7668                         add : true
7669                     });
7670                 }
7671             }
7672             
7673         }
7674     },
7675     
7676     onHeaderChange : function()
7677     {
7678         var header = this.renderHeader();
7679         var table = this.el.select('table', true).first();
7680         
7681         this.mainHead.remove();
7682         this.mainHead = table.createChild(header, this.mainBody, false);
7683     },
7684     
7685     onHiddenChange : function(colModel, colIndex, hidden)
7686     {
7687         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7688         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7689         
7690         this.CSS.updateRule(thSelector, "display", "");
7691         this.CSS.updateRule(tdSelector, "display", "");
7692         
7693         if(hidden){
7694             this.CSS.updateRule(thSelector, "display", "none");
7695             this.CSS.updateRule(tdSelector, "display", "none");
7696         }
7697         
7698         this.onHeaderChange();
7699         this.onLoad();
7700     },
7701     
7702     setColumnWidth: function(col_index, width)
7703     {
7704         // width = "md-2 xs-2..."
7705         if(!this.colModel.config[col_index]) {
7706             return;
7707         }
7708         
7709         var w = width.split(" ");
7710         
7711         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7712         
7713         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7714         
7715         
7716         for(var j = 0; j < w.length; j++) {
7717             
7718             if(!w[j]) {
7719                 continue;
7720             }
7721             
7722             var size_cls = w[j].split("-");
7723             
7724             if(!Number.isInteger(size_cls[1] * 1)) {
7725                 continue;
7726             }
7727             
7728             if(!this.colModel.config[col_index][size_cls[0]]) {
7729                 continue;
7730             }
7731             
7732             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7733                 continue;
7734             }
7735             
7736             h_row[0].classList.replace(
7737                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7738                 "col-"+size_cls[0]+"-"+size_cls[1]
7739             );
7740             
7741             for(var i = 0; i < rows.length; i++) {
7742                 
7743                 var size_cls = w[j].split("-");
7744                 
7745                 if(!Number.isInteger(size_cls[1] * 1)) {
7746                     continue;
7747                 }
7748                 
7749                 if(!this.colModel.config[col_index][size_cls[0]]) {
7750                     continue;
7751                 }
7752                 
7753                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7754                     continue;
7755                 }
7756                 
7757                 rows[i].classList.replace(
7758                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7759                     "col-"+size_cls[0]+"-"+size_cls[1]
7760                 );
7761             }
7762             
7763             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7764         }
7765     }
7766 });
7767
7768  
7769
7770  /*
7771  * - LGPL
7772  *
7773  * table cell
7774  * 
7775  */
7776
7777 /**
7778  * @class Roo.bootstrap.TableCell
7779  * @extends Roo.bootstrap.Component
7780  * Bootstrap TableCell class
7781  * @cfg {String} html cell contain text
7782  * @cfg {String} cls cell class
7783  * @cfg {String} tag cell tag (td|th) default td
7784  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7785  * @cfg {String} align Aligns the content in a cell
7786  * @cfg {String} axis Categorizes cells
7787  * @cfg {String} bgcolor Specifies the background color of a cell
7788  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7789  * @cfg {Number} colspan Specifies the number of columns a cell should span
7790  * @cfg {String} headers Specifies one or more header cells a cell is related to
7791  * @cfg {Number} height Sets the height of a cell
7792  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7793  * @cfg {Number} rowspan Sets the number of rows a cell should span
7794  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7795  * @cfg {String} valign Vertical aligns the content in a cell
7796  * @cfg {Number} width Specifies the width of a cell
7797  * 
7798  * @constructor
7799  * Create a new TableCell
7800  * @param {Object} config The config object
7801  */
7802
7803 Roo.bootstrap.TableCell = function(config){
7804     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7805 };
7806
7807 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7808     
7809     html: false,
7810     cls: false,
7811     tag: false,
7812     abbr: false,
7813     align: false,
7814     axis: false,
7815     bgcolor: false,
7816     charoff: false,
7817     colspan: false,
7818     headers: false,
7819     height: false,
7820     nowrap: false,
7821     rowspan: false,
7822     scope: false,
7823     valign: false,
7824     width: false,
7825     
7826     
7827     getAutoCreate : function(){
7828         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7829         
7830         cfg = {
7831             tag: 'td'
7832         };
7833         
7834         if(this.tag){
7835             cfg.tag = this.tag;
7836         }
7837         
7838         if (this.html) {
7839             cfg.html=this.html
7840         }
7841         if (this.cls) {
7842             cfg.cls=this.cls
7843         }
7844         if (this.abbr) {
7845             cfg.abbr=this.abbr
7846         }
7847         if (this.align) {
7848             cfg.align=this.align
7849         }
7850         if (this.axis) {
7851             cfg.axis=this.axis
7852         }
7853         if (this.bgcolor) {
7854             cfg.bgcolor=this.bgcolor
7855         }
7856         if (this.charoff) {
7857             cfg.charoff=this.charoff
7858         }
7859         if (this.colspan) {
7860             cfg.colspan=this.colspan
7861         }
7862         if (this.headers) {
7863             cfg.headers=this.headers
7864         }
7865         if (this.height) {
7866             cfg.height=this.height
7867         }
7868         if (this.nowrap) {
7869             cfg.nowrap=this.nowrap
7870         }
7871         if (this.rowspan) {
7872             cfg.rowspan=this.rowspan
7873         }
7874         if (this.scope) {
7875             cfg.scope=this.scope
7876         }
7877         if (this.valign) {
7878             cfg.valign=this.valign
7879         }
7880         if (this.width) {
7881             cfg.width=this.width
7882         }
7883         
7884         
7885         return cfg;
7886     }
7887    
7888 });
7889
7890  
7891
7892  /*
7893  * - LGPL
7894  *
7895  * table row
7896  * 
7897  */
7898
7899 /**
7900  * @class Roo.bootstrap.TableRow
7901  * @extends Roo.bootstrap.Component
7902  * Bootstrap TableRow class
7903  * @cfg {String} cls row class
7904  * @cfg {String} align Aligns the content in a table row
7905  * @cfg {String} bgcolor Specifies a background color for a table row
7906  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7907  * @cfg {String} valign Vertical aligns the content in a table row
7908  * 
7909  * @constructor
7910  * Create a new TableRow
7911  * @param {Object} config The config object
7912  */
7913
7914 Roo.bootstrap.TableRow = function(config){
7915     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7916 };
7917
7918 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7919     
7920     cls: false,
7921     align: false,
7922     bgcolor: false,
7923     charoff: false,
7924     valign: false,
7925     
7926     getAutoCreate : function(){
7927         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7928         
7929         cfg = {
7930             tag: 'tr'
7931         };
7932             
7933         if(this.cls){
7934             cfg.cls = this.cls;
7935         }
7936         if(this.align){
7937             cfg.align = this.align;
7938         }
7939         if(this.bgcolor){
7940             cfg.bgcolor = this.bgcolor;
7941         }
7942         if(this.charoff){
7943             cfg.charoff = this.charoff;
7944         }
7945         if(this.valign){
7946             cfg.valign = this.valign;
7947         }
7948         
7949         return cfg;
7950     }
7951    
7952 });
7953
7954  
7955
7956  /*
7957  * - LGPL
7958  *
7959  * table body
7960  * 
7961  */
7962
7963 /**
7964  * @class Roo.bootstrap.TableBody
7965  * @extends Roo.bootstrap.Component
7966  * Bootstrap TableBody class
7967  * @cfg {String} cls element class
7968  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7969  * @cfg {String} align Aligns the content inside the element
7970  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7971  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7972  * 
7973  * @constructor
7974  * Create a new TableBody
7975  * @param {Object} config The config object
7976  */
7977
7978 Roo.bootstrap.TableBody = function(config){
7979     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7980 };
7981
7982 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7983     
7984     cls: false,
7985     tag: false,
7986     align: false,
7987     charoff: false,
7988     valign: false,
7989     
7990     getAutoCreate : function(){
7991         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7992         
7993         cfg = {
7994             tag: 'tbody'
7995         };
7996             
7997         if (this.cls) {
7998             cfg.cls=this.cls
7999         }
8000         if(this.tag){
8001             cfg.tag = this.tag;
8002         }
8003         
8004         if(this.align){
8005             cfg.align = this.align;
8006         }
8007         if(this.charoff){
8008             cfg.charoff = this.charoff;
8009         }
8010         if(this.valign){
8011             cfg.valign = this.valign;
8012         }
8013         
8014         return cfg;
8015     }
8016     
8017     
8018 //    initEvents : function()
8019 //    {
8020 //        
8021 //        if(!this.store){
8022 //            return;
8023 //        }
8024 //        
8025 //        this.store = Roo.factory(this.store, Roo.data);
8026 //        this.store.on('load', this.onLoad, this);
8027 //        
8028 //        this.store.load();
8029 //        
8030 //    },
8031 //    
8032 //    onLoad: function () 
8033 //    {   
8034 //        this.fireEvent('load', this);
8035 //    }
8036 //    
8037 //   
8038 });
8039
8040  
8041
8042  /*
8043  * Based on:
8044  * Ext JS Library 1.1.1
8045  * Copyright(c) 2006-2007, Ext JS, LLC.
8046  *
8047  * Originally Released Under LGPL - original licence link has changed is not relivant.
8048  *
8049  * Fork - LGPL
8050  * <script type="text/javascript">
8051  */
8052
8053 // as we use this in bootstrap.
8054 Roo.namespace('Roo.form');
8055  /**
8056  * @class Roo.form.Action
8057  * Internal Class used to handle form actions
8058  * @constructor
8059  * @param {Roo.form.BasicForm} el The form element or its id
8060  * @param {Object} config Configuration options
8061  */
8062
8063  
8064  
8065 // define the action interface
8066 Roo.form.Action = function(form, options){
8067     this.form = form;
8068     this.options = options || {};
8069 };
8070 /**
8071  * Client Validation Failed
8072  * @const 
8073  */
8074 Roo.form.Action.CLIENT_INVALID = 'client';
8075 /**
8076  * Server Validation Failed
8077  * @const 
8078  */
8079 Roo.form.Action.SERVER_INVALID = 'server';
8080  /**
8081  * Connect to Server Failed
8082  * @const 
8083  */
8084 Roo.form.Action.CONNECT_FAILURE = 'connect';
8085 /**
8086  * Reading Data from Server Failed
8087  * @const 
8088  */
8089 Roo.form.Action.LOAD_FAILURE = 'load';
8090
8091 Roo.form.Action.prototype = {
8092     type : 'default',
8093     failureType : undefined,
8094     response : undefined,
8095     result : undefined,
8096
8097     // interface method
8098     run : function(options){
8099
8100     },
8101
8102     // interface method
8103     success : function(response){
8104
8105     },
8106
8107     // interface method
8108     handleResponse : function(response){
8109
8110     },
8111
8112     // default connection failure
8113     failure : function(response){
8114         
8115         this.response = response;
8116         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8117         this.form.afterAction(this, false);
8118     },
8119
8120     processResponse : function(response){
8121         this.response = response;
8122         if(!response.responseText){
8123             return true;
8124         }
8125         this.result = this.handleResponse(response);
8126         return this.result;
8127     },
8128
8129     // utility functions used internally
8130     getUrl : function(appendParams){
8131         var url = this.options.url || this.form.url || this.form.el.dom.action;
8132         if(appendParams){
8133             var p = this.getParams();
8134             if(p){
8135                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8136             }
8137         }
8138         return url;
8139     },
8140
8141     getMethod : function(){
8142         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8143     },
8144
8145     getParams : function(){
8146         var bp = this.form.baseParams;
8147         var p = this.options.params;
8148         if(p){
8149             if(typeof p == "object"){
8150                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8151             }else if(typeof p == 'string' && bp){
8152                 p += '&' + Roo.urlEncode(bp);
8153             }
8154         }else if(bp){
8155             p = Roo.urlEncode(bp);
8156         }
8157         return p;
8158     },
8159
8160     createCallback : function(){
8161         return {
8162             success: this.success,
8163             failure: this.failure,
8164             scope: this,
8165             timeout: (this.form.timeout*1000),
8166             upload: this.form.fileUpload ? this.success : undefined
8167         };
8168     }
8169 };
8170
8171 Roo.form.Action.Submit = function(form, options){
8172     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8173 };
8174
8175 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8176     type : 'submit',
8177
8178     haveProgress : false,
8179     uploadComplete : false,
8180     
8181     // uploadProgress indicator.
8182     uploadProgress : function()
8183     {
8184         if (!this.form.progressUrl) {
8185             return;
8186         }
8187         
8188         if (!this.haveProgress) {
8189             Roo.MessageBox.progress("Uploading", "Uploading");
8190         }
8191         if (this.uploadComplete) {
8192            Roo.MessageBox.hide();
8193            return;
8194         }
8195         
8196         this.haveProgress = true;
8197    
8198         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8199         
8200         var c = new Roo.data.Connection();
8201         c.request({
8202             url : this.form.progressUrl,
8203             params: {
8204                 id : uid
8205             },
8206             method: 'GET',
8207             success : function(req){
8208                //console.log(data);
8209                 var rdata = false;
8210                 var edata;
8211                 try  {
8212                    rdata = Roo.decode(req.responseText)
8213                 } catch (e) {
8214                     Roo.log("Invalid data from server..");
8215                     Roo.log(edata);
8216                     return;
8217                 }
8218                 if (!rdata || !rdata.success) {
8219                     Roo.log(rdata);
8220                     Roo.MessageBox.alert(Roo.encode(rdata));
8221                     return;
8222                 }
8223                 var data = rdata.data;
8224                 
8225                 if (this.uploadComplete) {
8226                    Roo.MessageBox.hide();
8227                    return;
8228                 }
8229                    
8230                 if (data){
8231                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8232                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8233                     );
8234                 }
8235                 this.uploadProgress.defer(2000,this);
8236             },
8237        
8238             failure: function(data) {
8239                 Roo.log('progress url failed ');
8240                 Roo.log(data);
8241             },
8242             scope : this
8243         });
8244            
8245     },
8246     
8247     
8248     run : function()
8249     {
8250         // run get Values on the form, so it syncs any secondary forms.
8251         this.form.getValues();
8252         
8253         var o = this.options;
8254         var method = this.getMethod();
8255         var isPost = method == 'POST';
8256         if(o.clientValidation === false || this.form.isValid()){
8257             
8258             if (this.form.progressUrl) {
8259                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8260                     (new Date() * 1) + '' + Math.random());
8261                     
8262             } 
8263             
8264             
8265             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8266                 form:this.form.el.dom,
8267                 url:this.getUrl(!isPost),
8268                 method: method,
8269                 params:isPost ? this.getParams() : null,
8270                 isUpload: this.form.fileUpload,
8271                 formData : this.form.formData
8272             }));
8273             
8274             this.uploadProgress();
8275
8276         }else if (o.clientValidation !== false){ // client validation failed
8277             this.failureType = Roo.form.Action.CLIENT_INVALID;
8278             this.form.afterAction(this, false);
8279         }
8280     },
8281
8282     success : function(response)
8283     {
8284         this.uploadComplete= true;
8285         if (this.haveProgress) {
8286             Roo.MessageBox.hide();
8287         }
8288         
8289         
8290         var result = this.processResponse(response);
8291         if(result === true || result.success){
8292             this.form.afterAction(this, true);
8293             return;
8294         }
8295         if(result.errors){
8296             this.form.markInvalid(result.errors);
8297             this.failureType = Roo.form.Action.SERVER_INVALID;
8298         }
8299         this.form.afterAction(this, false);
8300     },
8301     failure : function(response)
8302     {
8303         this.uploadComplete= true;
8304         if (this.haveProgress) {
8305             Roo.MessageBox.hide();
8306         }
8307         
8308         this.response = response;
8309         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8310         this.form.afterAction(this, false);
8311     },
8312     
8313     handleResponse : function(response){
8314         if(this.form.errorReader){
8315             var rs = this.form.errorReader.read(response);
8316             var errors = [];
8317             if(rs.records){
8318                 for(var i = 0, len = rs.records.length; i < len; i++) {
8319                     var r = rs.records[i];
8320                     errors[i] = r.data;
8321                 }
8322             }
8323             if(errors.length < 1){
8324                 errors = null;
8325             }
8326             return {
8327                 success : rs.success,
8328                 errors : errors
8329             };
8330         }
8331         var ret = false;
8332         try {
8333             ret = Roo.decode(response.responseText);
8334         } catch (e) {
8335             ret = {
8336                 success: false,
8337                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8338                 errors : []
8339             };
8340         }
8341         return ret;
8342         
8343     }
8344 });
8345
8346
8347 Roo.form.Action.Load = function(form, options){
8348     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8349     this.reader = this.form.reader;
8350 };
8351
8352 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8353     type : 'load',
8354
8355     run : function(){
8356         
8357         Roo.Ajax.request(Roo.apply(
8358                 this.createCallback(), {
8359                     method:this.getMethod(),
8360                     url:this.getUrl(false),
8361                     params:this.getParams()
8362         }));
8363     },
8364
8365     success : function(response){
8366         
8367         var result = this.processResponse(response);
8368         if(result === true || !result.success || !result.data){
8369             this.failureType = Roo.form.Action.LOAD_FAILURE;
8370             this.form.afterAction(this, false);
8371             return;
8372         }
8373         this.form.clearInvalid();
8374         this.form.setValues(result.data);
8375         this.form.afterAction(this, true);
8376     },
8377
8378     handleResponse : function(response){
8379         if(this.form.reader){
8380             var rs = this.form.reader.read(response);
8381             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8382             return {
8383                 success : rs.success,
8384                 data : data
8385             };
8386         }
8387         return Roo.decode(response.responseText);
8388     }
8389 });
8390
8391 Roo.form.Action.ACTION_TYPES = {
8392     'load' : Roo.form.Action.Load,
8393     'submit' : Roo.form.Action.Submit
8394 };/*
8395  * - LGPL
8396  *
8397  * form
8398  *
8399  */
8400
8401 /**
8402  * @class Roo.bootstrap.Form
8403  * @extends Roo.bootstrap.Component
8404  * Bootstrap Form class
8405  * @cfg {String} method  GET | POST (default POST)
8406  * @cfg {String} labelAlign top | left (default top)
8407  * @cfg {String} align left  | right - for navbars
8408  * @cfg {Boolean} loadMask load mask when submit (default true)
8409
8410  *
8411  * @constructor
8412  * Create a new Form
8413  * @param {Object} config The config object
8414  */
8415
8416
8417 Roo.bootstrap.Form = function(config){
8418     
8419     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8420     
8421     Roo.bootstrap.Form.popover.apply();
8422     
8423     this.addEvents({
8424         /**
8425          * @event clientvalidation
8426          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8427          * @param {Form} this
8428          * @param {Boolean} valid true if the form has passed client-side validation
8429          */
8430         clientvalidation: true,
8431         /**
8432          * @event beforeaction
8433          * Fires before any action is performed. Return false to cancel the action.
8434          * @param {Form} this
8435          * @param {Action} action The action to be performed
8436          */
8437         beforeaction: true,
8438         /**
8439          * @event actionfailed
8440          * Fires when an action fails.
8441          * @param {Form} this
8442          * @param {Action} action The action that failed
8443          */
8444         actionfailed : true,
8445         /**
8446          * @event actioncomplete
8447          * Fires when an action is completed.
8448          * @param {Form} this
8449          * @param {Action} action The action that completed
8450          */
8451         actioncomplete : true
8452     });
8453 };
8454
8455 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8456
8457      /**
8458      * @cfg {String} method
8459      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8460      */
8461     method : 'POST',
8462     /**
8463      * @cfg {String} url
8464      * The URL to use for form actions if one isn't supplied in the action options.
8465      */
8466     /**
8467      * @cfg {Boolean} fileUpload
8468      * Set to true if this form is a file upload.
8469      */
8470
8471     /**
8472      * @cfg {Object} baseParams
8473      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8474      */
8475
8476     /**
8477      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8478      */
8479     timeout: 30,
8480     /**
8481      * @cfg {Sting} align (left|right) for navbar forms
8482      */
8483     align : 'left',
8484
8485     // private
8486     activeAction : null,
8487
8488     /**
8489      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8490      * element by passing it or its id or mask the form itself by passing in true.
8491      * @type Mixed
8492      */
8493     waitMsgTarget : false,
8494
8495     loadMask : true,
8496     
8497     /**
8498      * @cfg {Boolean} errorMask (true|false) default false
8499      */
8500     errorMask : false,
8501     
8502     /**
8503      * @cfg {Number} maskOffset Default 100
8504      */
8505     maskOffset : 100,
8506     
8507     /**
8508      * @cfg {Boolean} maskBody
8509      */
8510     maskBody : false,
8511
8512     getAutoCreate : function(){
8513
8514         var cfg = {
8515             tag: 'form',
8516             method : this.method || 'POST',
8517             id : this.id || Roo.id(),
8518             cls : ''
8519         };
8520         if (this.parent().xtype.match(/^Nav/)) {
8521             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8522
8523         }
8524
8525         if (this.labelAlign == 'left' ) {
8526             cfg.cls += ' form-horizontal';
8527         }
8528
8529
8530         return cfg;
8531     },
8532     initEvents : function()
8533     {
8534         this.el.on('submit', this.onSubmit, this);
8535         // this was added as random key presses on the form where triggering form submit.
8536         this.el.on('keypress', function(e) {
8537             if (e.getCharCode() != 13) {
8538                 return true;
8539             }
8540             // we might need to allow it for textareas.. and some other items.
8541             // check e.getTarget().
8542
8543             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8544                 return true;
8545             }
8546
8547             Roo.log("keypress blocked");
8548
8549             e.preventDefault();
8550             return false;
8551         });
8552         
8553     },
8554     // private
8555     onSubmit : function(e){
8556         e.stopEvent();
8557     },
8558
8559      /**
8560      * Returns true if client-side validation on the form is successful.
8561      * @return Boolean
8562      */
8563     isValid : function(){
8564         var items = this.getItems();
8565         var valid = true;
8566         var target = false;
8567         
8568         items.each(function(f){
8569             
8570             if(f.validate()){
8571                 return;
8572             }
8573             
8574             Roo.log('invalid field: ' + f.name);
8575             
8576             valid = false;
8577
8578             if(!target && f.el.isVisible(true)){
8579                 target = f;
8580             }
8581            
8582         });
8583         
8584         if(this.errorMask && !valid){
8585             Roo.bootstrap.Form.popover.mask(this, target);
8586         }
8587         
8588         return valid;
8589     },
8590     
8591     /**
8592      * Returns true if any fields in this form have changed since their original load.
8593      * @return Boolean
8594      */
8595     isDirty : function(){
8596         var dirty = false;
8597         var items = this.getItems();
8598         items.each(function(f){
8599            if(f.isDirty()){
8600                dirty = true;
8601                return false;
8602            }
8603            return true;
8604         });
8605         return dirty;
8606     },
8607      /**
8608      * Performs a predefined action (submit or load) or custom actions you define on this form.
8609      * @param {String} actionName The name of the action type
8610      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8611      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8612      * accept other config options):
8613      * <pre>
8614 Property          Type             Description
8615 ----------------  ---------------  ----------------------------------------------------------------------------------
8616 url               String           The url for the action (defaults to the form's url)
8617 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8618 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8619 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8620                                    validate the form on the client (defaults to false)
8621      * </pre>
8622      * @return {BasicForm} this
8623      */
8624     doAction : function(action, options){
8625         if(typeof action == 'string'){
8626             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8627         }
8628         if(this.fireEvent('beforeaction', this, action) !== false){
8629             this.beforeAction(action);
8630             action.run.defer(100, action);
8631         }
8632         return this;
8633     },
8634
8635     // private
8636     beforeAction : function(action){
8637         var o = action.options;
8638         
8639         if(this.loadMask){
8640             
8641             if(this.maskBody){
8642                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8643             } else {
8644                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8645             }
8646         }
8647         // not really supported yet.. ??
8648
8649         //if(this.waitMsgTarget === true){
8650         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8651         //}else if(this.waitMsgTarget){
8652         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8653         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8654         //}else {
8655         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8656        // }
8657
8658     },
8659
8660     // private
8661     afterAction : function(action, success){
8662         this.activeAction = null;
8663         var o = action.options;
8664
8665         if(this.loadMask){
8666             
8667             if(this.maskBody){
8668                 Roo.get(document.body).unmask();
8669             } else {
8670                 this.el.unmask();
8671             }
8672         }
8673         
8674         //if(this.waitMsgTarget === true){
8675 //            this.el.unmask();
8676         //}else if(this.waitMsgTarget){
8677         //    this.waitMsgTarget.unmask();
8678         //}else{
8679         //    Roo.MessageBox.updateProgress(1);
8680         //    Roo.MessageBox.hide();
8681        // }
8682         //
8683         if(success){
8684             if(o.reset){
8685                 this.reset();
8686             }
8687             Roo.callback(o.success, o.scope, [this, action]);
8688             this.fireEvent('actioncomplete', this, action);
8689
8690         }else{
8691
8692             // failure condition..
8693             // we have a scenario where updates need confirming.
8694             // eg. if a locking scenario exists..
8695             // we look for { errors : { needs_confirm : true }} in the response.
8696             if (
8697                 (typeof(action.result) != 'undefined')  &&
8698                 (typeof(action.result.errors) != 'undefined')  &&
8699                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8700            ){
8701                 var _t = this;
8702                 Roo.log("not supported yet");
8703                  /*
8704
8705                 Roo.MessageBox.confirm(
8706                     "Change requires confirmation",
8707                     action.result.errorMsg,
8708                     function(r) {
8709                         if (r != 'yes') {
8710                             return;
8711                         }
8712                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8713                     }
8714
8715                 );
8716                 */
8717
8718
8719                 return;
8720             }
8721
8722             Roo.callback(o.failure, o.scope, [this, action]);
8723             // show an error message if no failed handler is set..
8724             if (!this.hasListener('actionfailed')) {
8725                 Roo.log("need to add dialog support");
8726                 /*
8727                 Roo.MessageBox.alert("Error",
8728                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8729                         action.result.errorMsg :
8730                         "Saving Failed, please check your entries or try again"
8731                 );
8732                 */
8733             }
8734
8735             this.fireEvent('actionfailed', this, action);
8736         }
8737
8738     },
8739     /**
8740      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8741      * @param {String} id The value to search for
8742      * @return Field
8743      */
8744     findField : function(id){
8745         var items = this.getItems();
8746         var field = items.get(id);
8747         if(!field){
8748              items.each(function(f){
8749                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8750                     field = f;
8751                     return false;
8752                 }
8753                 return true;
8754             });
8755         }
8756         return field || null;
8757     },
8758      /**
8759      * Mark fields in this form invalid in bulk.
8760      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8761      * @return {BasicForm} this
8762      */
8763     markInvalid : function(errors){
8764         if(errors instanceof Array){
8765             for(var i = 0, len = errors.length; i < len; i++){
8766                 var fieldError = errors[i];
8767                 var f = this.findField(fieldError.id);
8768                 if(f){
8769                     f.markInvalid(fieldError.msg);
8770                 }
8771             }
8772         }else{
8773             var field, id;
8774             for(id in errors){
8775                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8776                     field.markInvalid(errors[id]);
8777                 }
8778             }
8779         }
8780         //Roo.each(this.childForms || [], function (f) {
8781         //    f.markInvalid(errors);
8782         //});
8783
8784         return this;
8785     },
8786
8787     /**
8788      * Set values for fields in this form in bulk.
8789      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8790      * @return {BasicForm} this
8791      */
8792     setValues : function(values){
8793         if(values instanceof Array){ // array of objects
8794             for(var i = 0, len = values.length; i < len; i++){
8795                 var v = values[i];
8796                 var f = this.findField(v.id);
8797                 if(f){
8798                     f.setValue(v.value);
8799                     if(this.trackResetOnLoad){
8800                         f.originalValue = f.getValue();
8801                     }
8802                 }
8803             }
8804         }else{ // object hash
8805             var field, id;
8806             for(id in values){
8807                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8808
8809                     if (field.setFromData &&
8810                         field.valueField &&
8811                         field.displayField &&
8812                         // combos' with local stores can
8813                         // be queried via setValue()
8814                         // to set their value..
8815                         (field.store && !field.store.isLocal)
8816                         ) {
8817                         // it's a combo
8818                         var sd = { };
8819                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8820                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8821                         field.setFromData(sd);
8822
8823                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8824                         
8825                         field.setFromData(values);
8826                         
8827                     } else {
8828                         field.setValue(values[id]);
8829                     }
8830
8831
8832                     if(this.trackResetOnLoad){
8833                         field.originalValue = field.getValue();
8834                     }
8835                 }
8836             }
8837         }
8838
8839         //Roo.each(this.childForms || [], function (f) {
8840         //    f.setValues(values);
8841         //});
8842
8843         return this;
8844     },
8845
8846     /**
8847      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8848      * they are returned as an array.
8849      * @param {Boolean} asString
8850      * @return {Object}
8851      */
8852     getValues : function(asString){
8853         //if (this.childForms) {
8854             // copy values from the child forms
8855         //    Roo.each(this.childForms, function (f) {
8856         //        this.setValues(f.getValues());
8857         //    }, this);
8858         //}
8859
8860
8861
8862         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8863         if(asString === true){
8864             return fs;
8865         }
8866         return Roo.urlDecode(fs);
8867     },
8868
8869     /**
8870      * Returns the fields in this form as an object with key/value pairs.
8871      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8872      * @return {Object}
8873      */
8874     getFieldValues : function(with_hidden)
8875     {
8876         var items = this.getItems();
8877         var ret = {};
8878         items.each(function(f){
8879             
8880             if (!f.getName()) {
8881                 return;
8882             }
8883             
8884             var v = f.getValue();
8885             
8886             if (f.inputType =='radio') {
8887                 if (typeof(ret[f.getName()]) == 'undefined') {
8888                     ret[f.getName()] = ''; // empty..
8889                 }
8890
8891                 if (!f.el.dom.checked) {
8892                     return;
8893
8894                 }
8895                 v = f.el.dom.value;
8896
8897             }
8898             
8899             if(f.xtype == 'MoneyField'){
8900                 ret[f.currencyName] = f.getCurrency();
8901             }
8902
8903             // not sure if this supported any more..
8904             if ((typeof(v) == 'object') && f.getRawValue) {
8905                 v = f.getRawValue() ; // dates..
8906             }
8907             // combo boxes where name != hiddenName...
8908             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8909                 ret[f.name] = f.getRawValue();
8910             }
8911             ret[f.getName()] = v;
8912         });
8913
8914         return ret;
8915     },
8916
8917     /**
8918      * Clears all invalid messages in this form.
8919      * @return {BasicForm} this
8920      */
8921     clearInvalid : function(){
8922         var items = this.getItems();
8923
8924         items.each(function(f){
8925            f.clearInvalid();
8926         });
8927
8928         return this;
8929     },
8930
8931     /**
8932      * Resets this form.
8933      * @return {BasicForm} this
8934      */
8935     reset : function(){
8936         var items = this.getItems();
8937         items.each(function(f){
8938             f.reset();
8939         });
8940
8941         Roo.each(this.childForms || [], function (f) {
8942             f.reset();
8943         });
8944
8945
8946         return this;
8947     },
8948     
8949     getItems : function()
8950     {
8951         var r=new Roo.util.MixedCollection(false, function(o){
8952             return o.id || (o.id = Roo.id());
8953         });
8954         var iter = function(el) {
8955             if (el.inputEl) {
8956                 r.add(el);
8957             }
8958             if (!el.items) {
8959                 return;
8960             }
8961             Roo.each(el.items,function(e) {
8962                 iter(e);
8963             });
8964         };
8965
8966         iter(this);
8967         return r;
8968     },
8969     
8970     hideFields : function(items)
8971     {
8972         Roo.each(items, function(i){
8973             
8974             var f = this.findField(i);
8975             
8976             if(!f){
8977                 return;
8978             }
8979             
8980             f.hide();
8981             
8982         }, this);
8983     },
8984     
8985     showFields : 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.show();
8996             
8997         }, this);
8998     }
8999
9000 });
9001
9002 Roo.apply(Roo.bootstrap.Form, {
9003     
9004     popover : {
9005         
9006         padding : 5,
9007         
9008         isApplied : false,
9009         
9010         isMasked : false,
9011         
9012         form : false,
9013         
9014         target : false,
9015         
9016         toolTip : false,
9017         
9018         intervalID : false,
9019         
9020         maskEl : false,
9021         
9022         apply : function()
9023         {
9024             if(this.isApplied){
9025                 return;
9026             }
9027             
9028             this.maskEl = {
9029                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9030                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9031                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9032                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9033             };
9034             
9035             this.maskEl.top.enableDisplayMode("block");
9036             this.maskEl.left.enableDisplayMode("block");
9037             this.maskEl.bottom.enableDisplayMode("block");
9038             this.maskEl.right.enableDisplayMode("block");
9039             
9040             this.toolTip = new Roo.bootstrap.Tooltip({
9041                 cls : 'roo-form-error-popover',
9042                 alignment : {
9043                     'left' : ['r-l', [-2,0], 'right'],
9044                     'right' : ['l-r', [2,0], 'left'],
9045                     'bottom' : ['tl-bl', [0,2], 'top'],
9046                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9047                 }
9048             });
9049             
9050             this.toolTip.render(Roo.get(document.body));
9051
9052             this.toolTip.el.enableDisplayMode("block");
9053             
9054             Roo.get(document.body).on('click', function(){
9055                 this.unmask();
9056             }, this);
9057             
9058             Roo.get(document.body).on('touchstart', function(){
9059                 this.unmask();
9060             }, this);
9061             
9062             this.isApplied = true
9063         },
9064         
9065         mask : function(form, target)
9066         {
9067             this.form = form;
9068             
9069             this.target = target;
9070             
9071             if(!this.form.errorMask || !target.el){
9072                 return;
9073             }
9074             
9075             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9076             
9077             Roo.log(scrollable);
9078             
9079             var ot = this.target.el.calcOffsetsTo(scrollable);
9080             
9081             var scrollTo = ot[1] - this.form.maskOffset;
9082             
9083             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9084             
9085             scrollable.scrollTo('top', scrollTo);
9086             
9087             var box = this.target.el.getBox();
9088             Roo.log(box);
9089             var zIndex = Roo.bootstrap.Modal.zIndex++;
9090
9091             
9092             this.maskEl.top.setStyle('position', 'absolute');
9093             this.maskEl.top.setStyle('z-index', zIndex);
9094             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9095             this.maskEl.top.setLeft(0);
9096             this.maskEl.top.setTop(0);
9097             this.maskEl.top.show();
9098             
9099             this.maskEl.left.setStyle('position', 'absolute');
9100             this.maskEl.left.setStyle('z-index', zIndex);
9101             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9102             this.maskEl.left.setLeft(0);
9103             this.maskEl.left.setTop(box.y - this.padding);
9104             this.maskEl.left.show();
9105
9106             this.maskEl.bottom.setStyle('position', 'absolute');
9107             this.maskEl.bottom.setStyle('z-index', zIndex);
9108             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9109             this.maskEl.bottom.setLeft(0);
9110             this.maskEl.bottom.setTop(box.bottom + this.padding);
9111             this.maskEl.bottom.show();
9112
9113             this.maskEl.right.setStyle('position', 'absolute');
9114             this.maskEl.right.setStyle('z-index', zIndex);
9115             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9116             this.maskEl.right.setLeft(box.right + this.padding);
9117             this.maskEl.right.setTop(box.y - this.padding);
9118             this.maskEl.right.show();
9119
9120             this.toolTip.bindEl = this.target.el;
9121
9122             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9123
9124             var tip = this.target.blankText;
9125
9126             if(this.target.getValue() !== '' ) {
9127                 
9128                 if (this.target.invalidText.length) {
9129                     tip = this.target.invalidText;
9130                 } else if (this.target.regexText.length){
9131                     tip = this.target.regexText;
9132                 }
9133             }
9134
9135             this.toolTip.show(tip);
9136
9137             this.intervalID = window.setInterval(function() {
9138                 Roo.bootstrap.Form.popover.unmask();
9139             }, 10000);
9140
9141             window.onwheel = function(){ return false;};
9142             
9143             (function(){ this.isMasked = true; }).defer(500, this);
9144             
9145         },
9146         
9147         unmask : function()
9148         {
9149             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9150                 return;
9151             }
9152             
9153             this.maskEl.top.setStyle('position', 'absolute');
9154             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9155             this.maskEl.top.hide();
9156
9157             this.maskEl.left.setStyle('position', 'absolute');
9158             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9159             this.maskEl.left.hide();
9160
9161             this.maskEl.bottom.setStyle('position', 'absolute');
9162             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9163             this.maskEl.bottom.hide();
9164
9165             this.maskEl.right.setStyle('position', 'absolute');
9166             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9167             this.maskEl.right.hide();
9168             
9169             this.toolTip.hide();
9170             
9171             this.toolTip.el.hide();
9172             
9173             window.onwheel = function(){ return true;};
9174             
9175             if(this.intervalID){
9176                 window.clearInterval(this.intervalID);
9177                 this.intervalID = false;
9178             }
9179             
9180             this.isMasked = false;
9181             
9182         }
9183         
9184     }
9185     
9186 });
9187
9188 /*
9189  * Based on:
9190  * Ext JS Library 1.1.1
9191  * Copyright(c) 2006-2007, Ext JS, LLC.
9192  *
9193  * Originally Released Under LGPL - original licence link has changed is not relivant.
9194  *
9195  * Fork - LGPL
9196  * <script type="text/javascript">
9197  */
9198 /**
9199  * @class Roo.form.VTypes
9200  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9201  * @singleton
9202  */
9203 Roo.form.VTypes = function(){
9204     // closure these in so they are only created once.
9205     var alpha = /^[a-zA-Z_]+$/;
9206     var alphanum = /^[a-zA-Z0-9_]+$/;
9207     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9208     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9209
9210     // All these messages and functions are configurable
9211     return {
9212         /**
9213          * The function used to validate email addresses
9214          * @param {String} value The email address
9215          */
9216         'email' : function(v){
9217             return email.test(v);
9218         },
9219         /**
9220          * The error text to display when the email validation function returns false
9221          * @type String
9222          */
9223         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9224         /**
9225          * The keystroke filter mask to be applied on email input
9226          * @type RegExp
9227          */
9228         'emailMask' : /[a-z0-9_\.\-@]/i,
9229
9230         /**
9231          * The function used to validate URLs
9232          * @param {String} value The URL
9233          */
9234         'url' : function(v){
9235             return url.test(v);
9236         },
9237         /**
9238          * The error text to display when the url validation function returns false
9239          * @type String
9240          */
9241         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9242         
9243         /**
9244          * The function used to validate alpha values
9245          * @param {String} value The value
9246          */
9247         'alpha' : function(v){
9248             return alpha.test(v);
9249         },
9250         /**
9251          * The error text to display when the alpha validation function returns false
9252          * @type String
9253          */
9254         'alphaText' : 'This field should only contain letters and _',
9255         /**
9256          * The keystroke filter mask to be applied on alpha input
9257          * @type RegExp
9258          */
9259         'alphaMask' : /[a-z_]/i,
9260
9261         /**
9262          * The function used to validate alphanumeric values
9263          * @param {String} value The value
9264          */
9265         'alphanum' : function(v){
9266             return alphanum.test(v);
9267         },
9268         /**
9269          * The error text to display when the alphanumeric validation function returns false
9270          * @type String
9271          */
9272         'alphanumText' : 'This field should only contain letters, numbers and _',
9273         /**
9274          * The keystroke filter mask to be applied on alphanumeric input
9275          * @type RegExp
9276          */
9277         'alphanumMask' : /[a-z0-9_]/i
9278     };
9279 }();/*
9280  * - LGPL
9281  *
9282  * Input
9283  * 
9284  */
9285
9286 /**
9287  * @class Roo.bootstrap.Input
9288  * @extends Roo.bootstrap.Component
9289  * Bootstrap Input class
9290  * @cfg {Boolean} disabled is it disabled
9291  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9292  * @cfg {String} name name of the input
9293  * @cfg {string} fieldLabel - the label associated
9294  * @cfg {string} placeholder - placeholder to put in text.
9295  * @cfg {string}  before - input group add on before
9296  * @cfg {string} after - input group add on after
9297  * @cfg {string} size - (lg|sm) or leave empty..
9298  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9299  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9300  * @cfg {Number} md colspan out of 12 for computer-sized screens
9301  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9302  * @cfg {string} value default value of the input
9303  * @cfg {Number} labelWidth set the width of label 
9304  * @cfg {Number} labellg set the width of label (1-12)
9305  * @cfg {Number} labelmd set the width of label (1-12)
9306  * @cfg {Number} labelsm set the width of label (1-12)
9307  * @cfg {Number} labelxs set the width of label (1-12)
9308  * @cfg {String} labelAlign (top|left)
9309  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9310  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9311  * @cfg {String} indicatorpos (left|right) default left
9312  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9313  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9314
9315  * @cfg {String} align (left|center|right) Default left
9316  * @cfg {Boolean} forceFeedback (true|false) Default false
9317  * 
9318  * @constructor
9319  * Create a new Input
9320  * @param {Object} config The config object
9321  */
9322
9323 Roo.bootstrap.Input = function(config){
9324     
9325     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9326     
9327     this.addEvents({
9328         /**
9329          * @event focus
9330          * Fires when this field receives input focus.
9331          * @param {Roo.form.Field} this
9332          */
9333         focus : true,
9334         /**
9335          * @event blur
9336          * Fires when this field loses input focus.
9337          * @param {Roo.form.Field} this
9338          */
9339         blur : true,
9340         /**
9341          * @event specialkey
9342          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9343          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9344          * @param {Roo.form.Field} this
9345          * @param {Roo.EventObject} e The event object
9346          */
9347         specialkey : true,
9348         /**
9349          * @event change
9350          * Fires just before the field blurs if the field value has changed.
9351          * @param {Roo.form.Field} this
9352          * @param {Mixed} newValue The new value
9353          * @param {Mixed} oldValue The original value
9354          */
9355         change : true,
9356         /**
9357          * @event invalid
9358          * Fires after the field has been marked as invalid.
9359          * @param {Roo.form.Field} this
9360          * @param {String} msg The validation message
9361          */
9362         invalid : true,
9363         /**
9364          * @event valid
9365          * Fires after the field has been validated with no errors.
9366          * @param {Roo.form.Field} this
9367          */
9368         valid : true,
9369          /**
9370          * @event keyup
9371          * Fires after the key up
9372          * @param {Roo.form.Field} this
9373          * @param {Roo.EventObject}  e The event Object
9374          */
9375         keyup : true
9376     });
9377 };
9378
9379 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9380      /**
9381      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9382       automatic validation (defaults to "keyup").
9383      */
9384     validationEvent : "keyup",
9385      /**
9386      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9387      */
9388     validateOnBlur : true,
9389     /**
9390      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9391      */
9392     validationDelay : 250,
9393      /**
9394      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9395      */
9396     focusClass : "x-form-focus",  // not needed???
9397     
9398        
9399     /**
9400      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9401      */
9402     invalidClass : "has-warning",
9403     
9404     /**
9405      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9406      */
9407     validClass : "has-success",
9408     
9409     /**
9410      * @cfg {Boolean} hasFeedback (true|false) default true
9411      */
9412     hasFeedback : true,
9413     
9414     /**
9415      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9416      */
9417     invalidFeedbackClass : "glyphicon-warning-sign",
9418     
9419     /**
9420      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9421      */
9422     validFeedbackClass : "glyphicon-ok",
9423     
9424     /**
9425      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9426      */
9427     selectOnFocus : false,
9428     
9429      /**
9430      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9431      */
9432     maskRe : null,
9433        /**
9434      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9435      */
9436     vtype : null,
9437     
9438       /**
9439      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9440      */
9441     disableKeyFilter : false,
9442     
9443        /**
9444      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9445      */
9446     disabled : false,
9447      /**
9448      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9449      */
9450     allowBlank : true,
9451     /**
9452      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9453      */
9454     blankText : "Please complete this mandatory field",
9455     
9456      /**
9457      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9458      */
9459     minLength : 0,
9460     /**
9461      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9462      */
9463     maxLength : Number.MAX_VALUE,
9464     /**
9465      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9466      */
9467     minLengthText : "The minimum length for this field is {0}",
9468     /**
9469      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9470      */
9471     maxLengthText : "The maximum length for this field is {0}",
9472   
9473     
9474     /**
9475      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9476      * If available, this function will be called only after the basic validators all return true, and will be passed the
9477      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9478      */
9479     validator : null,
9480     /**
9481      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9482      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9483      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9484      */
9485     regex : null,
9486     /**
9487      * @cfg {String} regexText -- Depricated - use Invalid Text
9488      */
9489     regexText : "",
9490     
9491     /**
9492      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9493      */
9494     invalidText : "",
9495     
9496     
9497     
9498     autocomplete: false,
9499     
9500     
9501     fieldLabel : '',
9502     inputType : 'text',
9503     
9504     name : false,
9505     placeholder: false,
9506     before : false,
9507     after : false,
9508     size : false,
9509     hasFocus : false,
9510     preventMark: false,
9511     isFormField : true,
9512     value : '',
9513     labelWidth : 2,
9514     labelAlign : false,
9515     readOnly : false,
9516     align : false,
9517     formatedValue : false,
9518     forceFeedback : false,
9519     
9520     indicatorpos : 'left',
9521     
9522     labellg : 0,
9523     labelmd : 0,
9524     labelsm : 0,
9525     labelxs : 0,
9526     
9527     capture : '',
9528     accept : '',
9529     
9530     parentLabelAlign : function()
9531     {
9532         var parent = this;
9533         while (parent.parent()) {
9534             parent = parent.parent();
9535             if (typeof(parent.labelAlign) !='undefined') {
9536                 return parent.labelAlign;
9537             }
9538         }
9539         return 'left';
9540         
9541     },
9542     
9543     getAutoCreate : function()
9544     {
9545         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9546         
9547         var id = Roo.id();
9548         
9549         var cfg = {};
9550         
9551         if(this.inputType != 'hidden'){
9552             cfg.cls = 'form-group' //input-group
9553         }
9554         
9555         var input =  {
9556             tag: 'input',
9557             id : id,
9558             type : this.inputType,
9559             value : this.value,
9560             cls : 'form-control',
9561             placeholder : this.placeholder || '',
9562             autocomplete : this.autocomplete || 'new-password'
9563         };
9564         
9565         if(this.capture.length){
9566             input.capture = this.capture;
9567         }
9568         
9569         if(this.accept.length){
9570             input.accept = this.accept + "/*";
9571         }
9572         
9573         if(this.align){
9574             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9575         }
9576         
9577         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9578             input.maxLength = this.maxLength;
9579         }
9580         
9581         if (this.disabled) {
9582             input.disabled=true;
9583         }
9584         
9585         if (this.readOnly) {
9586             input.readonly=true;
9587         }
9588         
9589         if (this.name) {
9590             input.name = this.name;
9591         }
9592         
9593         if (this.size) {
9594             input.cls += ' input-' + this.size;
9595         }
9596         
9597         var settings=this;
9598         ['xs','sm','md','lg'].map(function(size){
9599             if (settings[size]) {
9600                 cfg.cls += ' col-' + size + '-' + settings[size];
9601             }
9602         });
9603         
9604         var inputblock = input;
9605         
9606         var feedback = {
9607             tag: 'span',
9608             cls: 'glyphicon form-control-feedback'
9609         };
9610             
9611         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9612             
9613             inputblock = {
9614                 cls : 'has-feedback',
9615                 cn :  [
9616                     input,
9617                     feedback
9618                 ] 
9619             };  
9620         }
9621         
9622         if (this.before || this.after) {
9623             
9624             inputblock = {
9625                 cls : 'input-group',
9626                 cn :  [] 
9627             };
9628             
9629             if (this.before && typeof(this.before) == 'string') {
9630                 
9631                 inputblock.cn.push({
9632                     tag :'span',
9633                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9634                     html : this.before
9635                 });
9636             }
9637             if (this.before && typeof(this.before) == 'object') {
9638                 this.before = Roo.factory(this.before);
9639                 
9640                 inputblock.cn.push({
9641                     tag :'span',
9642                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9643                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9644                 });
9645             }
9646             
9647             inputblock.cn.push(input);
9648             
9649             if (this.after && typeof(this.after) == 'string') {
9650                 inputblock.cn.push({
9651                     tag :'span',
9652                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9653                     html : this.after
9654                 });
9655             }
9656             if (this.after && typeof(this.after) == 'object') {
9657                 this.after = Roo.factory(this.after);
9658                 
9659                 inputblock.cn.push({
9660                     tag :'span',
9661                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9662                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9663                 });
9664             }
9665             
9666             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9667                 inputblock.cls += ' has-feedback';
9668                 inputblock.cn.push(feedback);
9669             }
9670         };
9671         var indicator = {
9672             tag : 'i',
9673             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9674             tooltip : 'This field is required'
9675         };
9676         if (Roo.bootstrap.version == 4) {
9677             indicator = {
9678                 tag : 'i',
9679                 style : 'display-none'
9680             };
9681         }
9682         if (align ==='left' && this.fieldLabel.length) {
9683             
9684             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9685             
9686             cfg.cn = [
9687                 indicator,
9688                 {
9689                     tag: 'label',
9690                     'for' :  id,
9691                     cls : 'control-label col-form-label',
9692                     html : this.fieldLabel
9693
9694                 },
9695                 {
9696                     cls : "", 
9697                     cn: [
9698                         inputblock
9699                     ]
9700                 }
9701             ];
9702             
9703             var labelCfg = cfg.cn[1];
9704             var contentCfg = cfg.cn[2];
9705             
9706             if(this.indicatorpos == 'right'){
9707                 cfg.cn = [
9708                     {
9709                         tag: 'label',
9710                         'for' :  id,
9711                         cls : 'control-label col-form-label',
9712                         cn : [
9713                             {
9714                                 tag : 'span',
9715                                 html : this.fieldLabel
9716                             },
9717                             indicator
9718                         ]
9719                     },
9720                     {
9721                         cls : "",
9722                         cn: [
9723                             inputblock
9724                         ]
9725                     }
9726
9727                 ];
9728                 
9729                 labelCfg = cfg.cn[0];
9730                 contentCfg = cfg.cn[1];
9731             
9732             }
9733             
9734             if(this.labelWidth > 12){
9735                 labelCfg.style = "width: " + this.labelWidth + 'px';
9736             }
9737             
9738             if(this.labelWidth < 13 && this.labelmd == 0){
9739                 this.labelmd = this.labelWidth;
9740             }
9741             
9742             if(this.labellg > 0){
9743                 labelCfg.cls += ' col-lg-' + this.labellg;
9744                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9745             }
9746             
9747             if(this.labelmd > 0){
9748                 labelCfg.cls += ' col-md-' + this.labelmd;
9749                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9750             }
9751             
9752             if(this.labelsm > 0){
9753                 labelCfg.cls += ' col-sm-' + this.labelsm;
9754                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9755             }
9756             
9757             if(this.labelxs > 0){
9758                 labelCfg.cls += ' col-xs-' + this.labelxs;
9759                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9760             }
9761             
9762             
9763         } else if ( this.fieldLabel.length) {
9764                 
9765             cfg.cn = [
9766                 {
9767                     tag : 'i',
9768                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9769                     tooltip : 'This field is required'
9770                 },
9771                 {
9772                     tag: 'label',
9773                    //cls : 'input-group-addon',
9774                     html : this.fieldLabel
9775
9776                 },
9777
9778                inputblock
9779
9780            ];
9781            
9782            if(this.indicatorpos == 'right'){
9783                 
9784                 cfg.cn = [
9785                     {
9786                         tag: 'label',
9787                        //cls : 'input-group-addon',
9788                         html : this.fieldLabel
9789
9790                     },
9791                     {
9792                         tag : 'i',
9793                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9794                         tooltip : 'This field is required'
9795                     },
9796
9797                    inputblock
9798
9799                ];
9800
9801             }
9802
9803         } else {
9804             
9805             cfg.cn = [
9806
9807                     inputblock
9808
9809             ];
9810                 
9811                 
9812         };
9813         
9814         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9815            cfg.cls += ' navbar-form';
9816         }
9817         
9818         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9819             // on BS4 we do this only if not form 
9820             cfg.cls += ' navbar-form';
9821             cfg.tag = 'li';
9822         }
9823         
9824         return cfg;
9825         
9826     },
9827     /**
9828      * return the real input element.
9829      */
9830     inputEl: function ()
9831     {
9832         return this.el.select('input.form-control',true).first();
9833     },
9834     
9835     tooltipEl : function()
9836     {
9837         return this.inputEl();
9838     },
9839     
9840     indicatorEl : function()
9841     {
9842         if (Roo.bootstrap.version == 4) {
9843             return false; // not enabled in v4 yet.
9844         }
9845         
9846         var indicator = this.el.select('i.roo-required-indicator',true).first();
9847         
9848         if(!indicator){
9849             return false;
9850         }
9851         
9852         return indicator;
9853         
9854     },
9855     
9856     setDisabled : function(v)
9857     {
9858         var i  = this.inputEl().dom;
9859         if (!v) {
9860             i.removeAttribute('disabled');
9861             return;
9862             
9863         }
9864         i.setAttribute('disabled','true');
9865     },
9866     initEvents : function()
9867     {
9868           
9869         this.inputEl().on("keydown" , this.fireKey,  this);
9870         this.inputEl().on("focus", this.onFocus,  this);
9871         this.inputEl().on("blur", this.onBlur,  this);
9872         
9873         this.inputEl().relayEvent('keyup', this);
9874         
9875         this.indicator = this.indicatorEl();
9876         
9877         if(this.indicator){
9878             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9879         }
9880  
9881         // reference to original value for reset
9882         this.originalValue = this.getValue();
9883         //Roo.form.TextField.superclass.initEvents.call(this);
9884         if(this.validationEvent == 'keyup'){
9885             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9886             this.inputEl().on('keyup', this.filterValidation, this);
9887         }
9888         else if(this.validationEvent !== false){
9889             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9890         }
9891         
9892         if(this.selectOnFocus){
9893             this.on("focus", this.preFocus, this);
9894             
9895         }
9896         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9897             this.inputEl().on("keypress", this.filterKeys, this);
9898         } else {
9899             this.inputEl().relayEvent('keypress', this);
9900         }
9901        /* if(this.grow){
9902             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9903             this.el.on("click", this.autoSize,  this);
9904         }
9905         */
9906         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9907             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9908         }
9909         
9910         if (typeof(this.before) == 'object') {
9911             this.before.render(this.el.select('.roo-input-before',true).first());
9912         }
9913         if (typeof(this.after) == 'object') {
9914             this.after.render(this.el.select('.roo-input-after',true).first());
9915         }
9916         
9917         this.inputEl().on('change', this.onChange, this);
9918         
9919     },
9920     filterValidation : function(e){
9921         if(!e.isNavKeyPress()){
9922             this.validationTask.delay(this.validationDelay);
9923         }
9924     },
9925      /**
9926      * Validates the field value
9927      * @return {Boolean} True if the value is valid, else false
9928      */
9929     validate : function(){
9930         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9931         if(this.disabled || this.validateValue(this.getRawValue())){
9932             this.markValid();
9933             return true;
9934         }
9935         
9936         this.markInvalid();
9937         return false;
9938     },
9939     
9940     
9941     /**
9942      * Validates a value according to the field's validation rules and marks the field as invalid
9943      * if the validation fails
9944      * @param {Mixed} value The value to validate
9945      * @return {Boolean} True if the value is valid, else false
9946      */
9947     validateValue : function(value)
9948     {
9949         if(this.getVisibilityEl().hasClass('hidden')){
9950             return true;
9951         }
9952         
9953         if(value.length < 1)  { // if it's blank
9954             if(this.allowBlank){
9955                 return true;
9956             }
9957             return false;
9958         }
9959         
9960         if(value.length < this.minLength){
9961             return false;
9962         }
9963         if(value.length > this.maxLength){
9964             return false;
9965         }
9966         if(this.vtype){
9967             var vt = Roo.form.VTypes;
9968             if(!vt[this.vtype](value, this)){
9969                 return false;
9970             }
9971         }
9972         if(typeof this.validator == "function"){
9973             var msg = this.validator(value);
9974             if(msg !== true){
9975                 return false;
9976             }
9977             if (typeof(msg) == 'string') {
9978                 this.invalidText = msg;
9979             }
9980         }
9981         
9982         if(this.regex && !this.regex.test(value)){
9983             return false;
9984         }
9985         
9986         return true;
9987     },
9988     
9989      // private
9990     fireKey : function(e){
9991         //Roo.log('field ' + e.getKey());
9992         if(e.isNavKeyPress()){
9993             this.fireEvent("specialkey", this, e);
9994         }
9995     },
9996     focus : function (selectText){
9997         if(this.rendered){
9998             this.inputEl().focus();
9999             if(selectText === true){
10000                 this.inputEl().dom.select();
10001             }
10002         }
10003         return this;
10004     } ,
10005     
10006     onFocus : function(){
10007         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10008            // this.el.addClass(this.focusClass);
10009         }
10010         if(!this.hasFocus){
10011             this.hasFocus = true;
10012             this.startValue = this.getValue();
10013             this.fireEvent("focus", this);
10014         }
10015     },
10016     
10017     beforeBlur : Roo.emptyFn,
10018
10019     
10020     // private
10021     onBlur : function(){
10022         this.beforeBlur();
10023         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10024             //this.el.removeClass(this.focusClass);
10025         }
10026         this.hasFocus = false;
10027         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10028             this.validate();
10029         }
10030         var v = this.getValue();
10031         if(String(v) !== String(this.startValue)){
10032             this.fireEvent('change', this, v, this.startValue);
10033         }
10034         this.fireEvent("blur", this);
10035     },
10036     
10037     onChange : function(e)
10038     {
10039         var v = this.getValue();
10040         if(String(v) !== String(this.startValue)){
10041             this.fireEvent('change', this, v, this.startValue);
10042         }
10043         
10044     },
10045     
10046     /**
10047      * Resets the current field value to the originally loaded value and clears any validation messages
10048      */
10049     reset : function(){
10050         this.setValue(this.originalValue);
10051         this.validate();
10052     },
10053      /**
10054      * Returns the name of the field
10055      * @return {Mixed} name The name field
10056      */
10057     getName: function(){
10058         return this.name;
10059     },
10060      /**
10061      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10062      * @return {Mixed} value The field value
10063      */
10064     getValue : function(){
10065         
10066         var v = this.inputEl().getValue();
10067         
10068         return v;
10069     },
10070     /**
10071      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10072      * @return {Mixed} value The field value
10073      */
10074     getRawValue : function(){
10075         var v = this.inputEl().getValue();
10076         
10077         return v;
10078     },
10079     
10080     /**
10081      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10082      * @param {Mixed} value The value to set
10083      */
10084     setRawValue : function(v){
10085         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10086     },
10087     
10088     selectText : function(start, end){
10089         var v = this.getRawValue();
10090         if(v.length > 0){
10091             start = start === undefined ? 0 : start;
10092             end = end === undefined ? v.length : end;
10093             var d = this.inputEl().dom;
10094             if(d.setSelectionRange){
10095                 d.setSelectionRange(start, end);
10096             }else if(d.createTextRange){
10097                 var range = d.createTextRange();
10098                 range.moveStart("character", start);
10099                 range.moveEnd("character", v.length-end);
10100                 range.select();
10101             }
10102         }
10103     },
10104     
10105     /**
10106      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10107      * @param {Mixed} value The value to set
10108      */
10109     setValue : function(v){
10110         this.value = v;
10111         if(this.rendered){
10112             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10113             this.validate();
10114         }
10115     },
10116     
10117     /*
10118     processValue : function(value){
10119         if(this.stripCharsRe){
10120             var newValue = value.replace(this.stripCharsRe, '');
10121             if(newValue !== value){
10122                 this.setRawValue(newValue);
10123                 return newValue;
10124             }
10125         }
10126         return value;
10127     },
10128   */
10129     preFocus : function(){
10130         
10131         if(this.selectOnFocus){
10132             this.inputEl().dom.select();
10133         }
10134     },
10135     filterKeys : function(e){
10136         var k = e.getKey();
10137         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10138             return;
10139         }
10140         var c = e.getCharCode(), cc = String.fromCharCode(c);
10141         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10142             return;
10143         }
10144         if(!this.maskRe.test(cc)){
10145             e.stopEvent();
10146         }
10147     },
10148      /**
10149      * Clear any invalid styles/messages for this field
10150      */
10151     clearInvalid : function(){
10152         
10153         if(!this.el || this.preventMark){ // not rendered
10154             return;
10155         }
10156         
10157         
10158         this.el.removeClass([this.invalidClass, 'is-invalid']);
10159         
10160         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10161             
10162             var feedback = this.el.select('.form-control-feedback', true).first();
10163             
10164             if(feedback){
10165                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10166             }
10167             
10168         }
10169         
10170         if(this.indicator){
10171             this.indicator.removeClass('visible');
10172             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10173         }
10174         
10175         this.fireEvent('valid', this);
10176     },
10177     
10178      /**
10179      * Mark this field as valid
10180      */
10181     markValid : function()
10182     {
10183         if(!this.el  || this.preventMark){ // not rendered...
10184             return;
10185         }
10186         
10187         this.el.removeClass([this.invalidClass, this.validClass]);
10188         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10189
10190         var feedback = this.el.select('.form-control-feedback', true).first();
10191             
10192         if(feedback){
10193             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10194         }
10195         
10196         if(this.indicator){
10197             this.indicator.removeClass('visible');
10198             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10199         }
10200         
10201         if(this.disabled){
10202             return;
10203         }
10204         
10205         if(this.allowBlank && !this.getRawValue().length){
10206             return;
10207         }
10208         if (Roo.bootstrap.version == 3) {
10209             this.el.addClass(this.validClass);
10210         } else {
10211             this.inputEl().addClass('is-valid');
10212         }
10213
10214         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10215             
10216             var feedback = this.el.select('.form-control-feedback', true).first();
10217             
10218             if(feedback){
10219                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10220                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10221             }
10222             
10223         }
10224         
10225         this.fireEvent('valid', this);
10226     },
10227     
10228      /**
10229      * Mark this field as invalid
10230      * @param {String} msg The validation message
10231      */
10232     markInvalid : function(msg)
10233     {
10234         if(!this.el  || this.preventMark){ // not rendered
10235             return;
10236         }
10237         
10238         this.el.removeClass([this.invalidClass, this.validClass]);
10239         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10240         
10241         var feedback = this.el.select('.form-control-feedback', true).first();
10242             
10243         if(feedback){
10244             this.el.select('.form-control-feedback', true).first().removeClass(
10245                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10246         }
10247
10248         if(this.disabled){
10249             return;
10250         }
10251         
10252         if(this.allowBlank && !this.getRawValue().length){
10253             return;
10254         }
10255         
10256         if(this.indicator){
10257             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10258             this.indicator.addClass('visible');
10259         }
10260         if (Roo.bootstrap.version == 3) {
10261             this.el.addClass(this.invalidClass);
10262         } else {
10263             this.inputEl().addClass('is-invalid');
10264         }
10265         
10266         
10267         
10268         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10269             
10270             var feedback = this.el.select('.form-control-feedback', true).first();
10271             
10272             if(feedback){
10273                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10274                 
10275                 if(this.getValue().length || this.forceFeedback){
10276                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10277                 }
10278                 
10279             }
10280             
10281         }
10282         
10283         this.fireEvent('invalid', this, msg);
10284     },
10285     // private
10286     SafariOnKeyDown : function(event)
10287     {
10288         // this is a workaround for a password hang bug on chrome/ webkit.
10289         if (this.inputEl().dom.type != 'password') {
10290             return;
10291         }
10292         
10293         var isSelectAll = false;
10294         
10295         if(this.inputEl().dom.selectionEnd > 0){
10296             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10297         }
10298         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10299             event.preventDefault();
10300             this.setValue('');
10301             return;
10302         }
10303         
10304         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10305             
10306             event.preventDefault();
10307             // this is very hacky as keydown always get's upper case.
10308             //
10309             var cc = String.fromCharCode(event.getCharCode());
10310             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10311             
10312         }
10313     },
10314     adjustWidth : function(tag, w){
10315         tag = tag.toLowerCase();
10316         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10317             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10318                 if(tag == 'input'){
10319                     return w + 2;
10320                 }
10321                 if(tag == 'textarea'){
10322                     return w-2;
10323                 }
10324             }else if(Roo.isOpera){
10325                 if(tag == 'input'){
10326                     return w + 2;
10327                 }
10328                 if(tag == 'textarea'){
10329                     return w-2;
10330                 }
10331             }
10332         }
10333         return w;
10334     },
10335     
10336     setFieldLabel : function(v)
10337     {
10338         if(!this.rendered){
10339             return;
10340         }
10341         
10342         if(this.indicatorEl()){
10343             var ar = this.el.select('label > span',true);
10344             
10345             if (ar.elements.length) {
10346                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10347                 this.fieldLabel = v;
10348                 return;
10349             }
10350             
10351             var br = this.el.select('label',true);
10352             
10353             if(br.elements.length) {
10354                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10355                 this.fieldLabel = v;
10356                 return;
10357             }
10358             
10359             Roo.log('Cannot Found any of label > span || label in input');
10360             return;
10361         }
10362         
10363         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10364         this.fieldLabel = v;
10365         
10366         
10367     }
10368 });
10369
10370  
10371 /*
10372  * - LGPL
10373  *
10374  * Input
10375  * 
10376  */
10377
10378 /**
10379  * @class Roo.bootstrap.TextArea
10380  * @extends Roo.bootstrap.Input
10381  * Bootstrap TextArea class
10382  * @cfg {Number} cols Specifies the visible width of a text area
10383  * @cfg {Number} rows Specifies the visible number of lines in a text area
10384  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10385  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10386  * @cfg {string} html text
10387  * 
10388  * @constructor
10389  * Create a new TextArea
10390  * @param {Object} config The config object
10391  */
10392
10393 Roo.bootstrap.TextArea = function(config){
10394     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10395    
10396 };
10397
10398 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10399      
10400     cols : false,
10401     rows : 5,
10402     readOnly : false,
10403     warp : 'soft',
10404     resize : false,
10405     value: false,
10406     html: false,
10407     
10408     getAutoCreate : function(){
10409         
10410         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10411         
10412         var id = Roo.id();
10413         
10414         var cfg = {};
10415         
10416         if(this.inputType != 'hidden'){
10417             cfg.cls = 'form-group' //input-group
10418         }
10419         
10420         var input =  {
10421             tag: 'textarea',
10422             id : id,
10423             warp : this.warp,
10424             rows : this.rows,
10425             value : this.value || '',
10426             html: this.html || '',
10427             cls : 'form-control',
10428             placeholder : this.placeholder || '' 
10429             
10430         };
10431         
10432         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10433             input.maxLength = this.maxLength;
10434         }
10435         
10436         if(this.resize){
10437             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10438         }
10439         
10440         if(this.cols){
10441             input.cols = this.cols;
10442         }
10443         
10444         if (this.readOnly) {
10445             input.readonly = true;
10446         }
10447         
10448         if (this.name) {
10449             input.name = this.name;
10450         }
10451         
10452         if (this.size) {
10453             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10454         }
10455         
10456         var settings=this;
10457         ['xs','sm','md','lg'].map(function(size){
10458             if (settings[size]) {
10459                 cfg.cls += ' col-' + size + '-' + settings[size];
10460             }
10461         });
10462         
10463         var inputblock = input;
10464         
10465         if(this.hasFeedback && !this.allowBlank){
10466             
10467             var feedback = {
10468                 tag: 'span',
10469                 cls: 'glyphicon form-control-feedback'
10470             };
10471
10472             inputblock = {
10473                 cls : 'has-feedback',
10474                 cn :  [
10475                     input,
10476                     feedback
10477                 ] 
10478             };  
10479         }
10480         
10481         
10482         if (this.before || this.after) {
10483             
10484             inputblock = {
10485                 cls : 'input-group',
10486                 cn :  [] 
10487             };
10488             if (this.before) {
10489                 inputblock.cn.push({
10490                     tag :'span',
10491                     cls : 'input-group-addon',
10492                     html : this.before
10493                 });
10494             }
10495             
10496             inputblock.cn.push(input);
10497             
10498             if(this.hasFeedback && !this.allowBlank){
10499                 inputblock.cls += ' has-feedback';
10500                 inputblock.cn.push(feedback);
10501             }
10502             
10503             if (this.after) {
10504                 inputblock.cn.push({
10505                     tag :'span',
10506                     cls : 'input-group-addon',
10507                     html : this.after
10508                 });
10509             }
10510             
10511         }
10512         
10513         if (align ==='left' && this.fieldLabel.length) {
10514             cfg.cn = [
10515                 {
10516                     tag: 'label',
10517                     'for' :  id,
10518                     cls : 'control-label',
10519                     html : this.fieldLabel
10520                 },
10521                 {
10522                     cls : "",
10523                     cn: [
10524                         inputblock
10525                     ]
10526                 }
10527
10528             ];
10529             
10530             if(this.labelWidth > 12){
10531                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10532             }
10533
10534             if(this.labelWidth < 13 && this.labelmd == 0){
10535                 this.labelmd = this.labelWidth;
10536             }
10537
10538             if(this.labellg > 0){
10539                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10540                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10541             }
10542
10543             if(this.labelmd > 0){
10544                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10545                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10546             }
10547
10548             if(this.labelsm > 0){
10549                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10550                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10551             }
10552
10553             if(this.labelxs > 0){
10554                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10555                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10556             }
10557             
10558         } else if ( this.fieldLabel.length) {
10559             cfg.cn = [
10560
10561                {
10562                    tag: 'label',
10563                    //cls : 'input-group-addon',
10564                    html : this.fieldLabel
10565
10566                },
10567
10568                inputblock
10569
10570            ];
10571
10572         } else {
10573
10574             cfg.cn = [
10575
10576                 inputblock
10577
10578             ];
10579                 
10580         }
10581         
10582         if (this.disabled) {
10583             input.disabled=true;
10584         }
10585         
10586         return cfg;
10587         
10588     },
10589     /**
10590      * return the real textarea element.
10591      */
10592     inputEl: function ()
10593     {
10594         return this.el.select('textarea.form-control',true).first();
10595     },
10596     
10597     /**
10598      * Clear any invalid styles/messages for this field
10599      */
10600     clearInvalid : function()
10601     {
10602         
10603         if(!this.el || this.preventMark){ // not rendered
10604             return;
10605         }
10606         
10607         var label = this.el.select('label', true).first();
10608         var icon = this.el.select('i.fa-star', true).first();
10609         
10610         if(label && icon){
10611             icon.remove();
10612         }
10613         this.el.removeClass( this.validClass);
10614         this.inputEl().removeClass('is-invalid');
10615          
10616         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10617             
10618             var feedback = this.el.select('.form-control-feedback', true).first();
10619             
10620             if(feedback){
10621                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10622             }
10623             
10624         }
10625         
10626         this.fireEvent('valid', this);
10627     },
10628     
10629      /**
10630      * Mark this field as valid
10631      */
10632     markValid : function()
10633     {
10634         if(!this.el  || this.preventMark){ // not rendered
10635             return;
10636         }
10637         
10638         this.el.removeClass([this.invalidClass, this.validClass]);
10639         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10640         
10641         var feedback = this.el.select('.form-control-feedback', true).first();
10642             
10643         if(feedback){
10644             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10645         }
10646
10647         if(this.disabled || this.allowBlank){
10648             return;
10649         }
10650         
10651         var label = this.el.select('label', true).first();
10652         var icon = this.el.select('i.fa-star', true).first();
10653         
10654         if(label && icon){
10655             icon.remove();
10656         }
10657         if (Roo.bootstrap.version == 3) {
10658             this.el.addClass(this.validClass);
10659         } else {
10660             this.inputEl().addClass('is-valid');
10661         }
10662         
10663         
10664         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10665             
10666             var feedback = this.el.select('.form-control-feedback', true).first();
10667             
10668             if(feedback){
10669                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10670                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10671             }
10672             
10673         }
10674         
10675         this.fireEvent('valid', this);
10676     },
10677     
10678      /**
10679      * Mark this field as invalid
10680      * @param {String} msg The validation message
10681      */
10682     markInvalid : function(msg)
10683     {
10684         if(!this.el  || this.preventMark){ // not rendered
10685             return;
10686         }
10687         
10688         this.el.removeClass([this.invalidClass, this.validClass]);
10689         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10690         
10691         var feedback = this.el.select('.form-control-feedback', true).first();
10692             
10693         if(feedback){
10694             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10695         }
10696
10697         if(this.disabled || this.allowBlank){
10698             return;
10699         }
10700         
10701         var label = this.el.select('label', true).first();
10702         var icon = this.el.select('i.fa-star', true).first();
10703         
10704         if(!this.getValue().length && label && !icon){
10705             this.el.createChild({
10706                 tag : 'i',
10707                 cls : 'text-danger fa fa-lg fa-star',
10708                 tooltip : 'This field is required',
10709                 style : 'margin-right:5px;'
10710             }, label, true);
10711         }
10712         
10713         if (Roo.bootstrap.version == 3) {
10714             this.el.addClass(this.invalidClass);
10715         } else {
10716             this.inputEl().addClass('is-invalid');
10717         }
10718         
10719         // fixme ... this may be depricated need to test..
10720         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10721             
10722             var feedback = this.el.select('.form-control-feedback', true).first();
10723             
10724             if(feedback){
10725                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10726                 
10727                 if(this.getValue().length || this.forceFeedback){
10728                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10729                 }
10730                 
10731             }
10732             
10733         }
10734         
10735         this.fireEvent('invalid', this, msg);
10736     }
10737 });
10738
10739  
10740 /*
10741  * - LGPL
10742  *
10743  * trigger field - base class for combo..
10744  * 
10745  */
10746  
10747 /**
10748  * @class Roo.bootstrap.TriggerField
10749  * @extends Roo.bootstrap.Input
10750  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10751  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10752  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10753  * for which you can provide a custom implementation.  For example:
10754  * <pre><code>
10755 var trigger = new Roo.bootstrap.TriggerField();
10756 trigger.onTriggerClick = myTriggerFn;
10757 trigger.applyTo('my-field');
10758 </code></pre>
10759  *
10760  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10761  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10762  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10763  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10764  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10765
10766  * @constructor
10767  * Create a new TriggerField.
10768  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10769  * to the base TextField)
10770  */
10771 Roo.bootstrap.TriggerField = function(config){
10772     this.mimicing = false;
10773     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10774 };
10775
10776 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10777     /**
10778      * @cfg {String} triggerClass A CSS class to apply to the trigger
10779      */
10780      /**
10781      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10782      */
10783     hideTrigger:false,
10784
10785     /**
10786      * @cfg {Boolean} removable (true|false) special filter default false
10787      */
10788     removable : false,
10789     
10790     /** @cfg {Boolean} grow @hide */
10791     /** @cfg {Number} growMin @hide */
10792     /** @cfg {Number} growMax @hide */
10793
10794     /**
10795      * @hide 
10796      * @method
10797      */
10798     autoSize: Roo.emptyFn,
10799     // private
10800     monitorTab : true,
10801     // private
10802     deferHeight : true,
10803
10804     
10805     actionMode : 'wrap',
10806     
10807     caret : false,
10808     
10809     
10810     getAutoCreate : function(){
10811        
10812         var align = this.labelAlign || this.parentLabelAlign();
10813         
10814         var id = Roo.id();
10815         
10816         var cfg = {
10817             cls: 'form-group' //input-group
10818         };
10819         
10820         
10821         var input =  {
10822             tag: 'input',
10823             id : id,
10824             type : this.inputType,
10825             cls : 'form-control',
10826             autocomplete: 'new-password',
10827             placeholder : this.placeholder || '' 
10828             
10829         };
10830         if (this.name) {
10831             input.name = this.name;
10832         }
10833         if (this.size) {
10834             input.cls += ' input-' + this.size;
10835         }
10836         
10837         if (this.disabled) {
10838             input.disabled=true;
10839         }
10840         
10841         var inputblock = input;
10842         
10843         if(this.hasFeedback && !this.allowBlank){
10844             
10845             var feedback = {
10846                 tag: 'span',
10847                 cls: 'glyphicon form-control-feedback'
10848             };
10849             
10850             if(this.removable && !this.editable && !this.tickable){
10851                 inputblock = {
10852                     cls : 'has-feedback',
10853                     cn :  [
10854                         inputblock,
10855                         {
10856                             tag: 'button',
10857                             html : 'x',
10858                             cls : 'roo-combo-removable-btn close'
10859                         },
10860                         feedback
10861                     ] 
10862                 };
10863             } else {
10864                 inputblock = {
10865                     cls : 'has-feedback',
10866                     cn :  [
10867                         inputblock,
10868                         feedback
10869                     ] 
10870                 };
10871             }
10872
10873         } else {
10874             if(this.removable && !this.editable && !this.tickable){
10875                 inputblock = {
10876                     cls : 'roo-removable',
10877                     cn :  [
10878                         inputblock,
10879                         {
10880                             tag: 'button',
10881                             html : 'x',
10882                             cls : 'roo-combo-removable-btn close'
10883                         }
10884                     ] 
10885                 };
10886             }
10887         }
10888         
10889         if (this.before || this.after) {
10890             
10891             inputblock = {
10892                 cls : 'input-group',
10893                 cn :  [] 
10894             };
10895             if (this.before) {
10896                 inputblock.cn.push({
10897                     tag :'span',
10898                     cls : 'input-group-addon input-group-prepend input-group-text',
10899                     html : this.before
10900                 });
10901             }
10902             
10903             inputblock.cn.push(input);
10904             
10905             if(this.hasFeedback && !this.allowBlank){
10906                 inputblock.cls += ' has-feedback';
10907                 inputblock.cn.push(feedback);
10908             }
10909             
10910             if (this.after) {
10911                 inputblock.cn.push({
10912                     tag :'span',
10913                     cls : 'input-group-addon input-group-append input-group-text',
10914                     html : this.after
10915                 });
10916             }
10917             
10918         };
10919         
10920       
10921         
10922         var ibwrap = inputblock;
10923         
10924         if(this.multiple){
10925             ibwrap = {
10926                 tag: 'ul',
10927                 cls: 'roo-select2-choices',
10928                 cn:[
10929                     {
10930                         tag: 'li',
10931                         cls: 'roo-select2-search-field',
10932                         cn: [
10933
10934                             inputblock
10935                         ]
10936                     }
10937                 ]
10938             };
10939                 
10940         }
10941         
10942         var combobox = {
10943             cls: 'roo-select2-container input-group',
10944             cn: [
10945                  {
10946                     tag: 'input',
10947                     type : 'hidden',
10948                     cls: 'form-hidden-field'
10949                 },
10950                 ibwrap
10951             ]
10952         };
10953         
10954         if(!this.multiple && this.showToggleBtn){
10955             
10956             var caret = {
10957                         tag: 'span',
10958                         cls: 'caret'
10959              };
10960             if (this.caret != false) {
10961                 caret = {
10962                      tag: 'i',
10963                      cls: 'fa fa-' + this.caret
10964                 };
10965                 
10966             }
10967             
10968             combobox.cn.push({
10969                 tag :'span',
10970                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10971                 cn : [
10972                     Roo.bootstrap.version == 3 ? caret : '',
10973                     {
10974                         tag: 'span',
10975                         cls: 'combobox-clear',
10976                         cn  : [
10977                             {
10978                                 tag : 'i',
10979                                 cls: 'icon-remove'
10980                             }
10981                         ]
10982                     }
10983                 ]
10984
10985             })
10986         }
10987         
10988         if(this.multiple){
10989             combobox.cls += ' roo-select2-container-multi';
10990         }
10991          var indicator = {
10992             tag : 'i',
10993             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10994             tooltip : 'This field is required'
10995         };
10996         if (Roo.bootstrap.version == 4) {
10997             indicator = {
10998                 tag : 'i',
10999                 style : 'display:none'
11000             };
11001         }
11002         
11003         
11004         if (align ==='left' && this.fieldLabel.length) {
11005             
11006             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11007
11008             cfg.cn = [
11009                 indicator,
11010                 {
11011                     tag: 'label',
11012                     'for' :  id,
11013                     cls : 'control-label',
11014                     html : this.fieldLabel
11015
11016                 },
11017                 {
11018                     cls : "", 
11019                     cn: [
11020                         combobox
11021                     ]
11022                 }
11023
11024             ];
11025             
11026             var labelCfg = cfg.cn[1];
11027             var contentCfg = cfg.cn[2];
11028             
11029             if(this.indicatorpos == 'right'){
11030                 cfg.cn = [
11031                     {
11032                         tag: 'label',
11033                         'for' :  id,
11034                         cls : 'control-label',
11035                         cn : [
11036                             {
11037                                 tag : 'span',
11038                                 html : this.fieldLabel
11039                             },
11040                             indicator
11041                         ]
11042                     },
11043                     {
11044                         cls : "", 
11045                         cn: [
11046                             combobox
11047                         ]
11048                     }
11049
11050                 ];
11051                 
11052                 labelCfg = cfg.cn[0];
11053                 contentCfg = cfg.cn[1];
11054             }
11055             
11056             if(this.labelWidth > 12){
11057                 labelCfg.style = "width: " + this.labelWidth + 'px';
11058             }
11059             
11060             if(this.labelWidth < 13 && this.labelmd == 0){
11061                 this.labelmd = this.labelWidth;
11062             }
11063             
11064             if(this.labellg > 0){
11065                 labelCfg.cls += ' col-lg-' + this.labellg;
11066                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11067             }
11068             
11069             if(this.labelmd > 0){
11070                 labelCfg.cls += ' col-md-' + this.labelmd;
11071                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11072             }
11073             
11074             if(this.labelsm > 0){
11075                 labelCfg.cls += ' col-sm-' + this.labelsm;
11076                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11077             }
11078             
11079             if(this.labelxs > 0){
11080                 labelCfg.cls += ' col-xs-' + this.labelxs;
11081                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11082             }
11083             
11084         } else if ( this.fieldLabel.length) {
11085 //                Roo.log(" label");
11086             cfg.cn = [
11087                 indicator,
11088                {
11089                    tag: 'label',
11090                    //cls : 'input-group-addon',
11091                    html : this.fieldLabel
11092
11093                },
11094
11095                combobox
11096
11097             ];
11098             
11099             if(this.indicatorpos == 'right'){
11100                 
11101                 cfg.cn = [
11102                     {
11103                        tag: 'label',
11104                        cn : [
11105                            {
11106                                tag : 'span',
11107                                html : this.fieldLabel
11108                            },
11109                            indicator
11110                        ]
11111
11112                     },
11113                     combobox
11114
11115                 ];
11116
11117             }
11118
11119         } else {
11120             
11121 //                Roo.log(" no label && no align");
11122                 cfg = combobox
11123                      
11124                 
11125         }
11126         
11127         var settings=this;
11128         ['xs','sm','md','lg'].map(function(size){
11129             if (settings[size]) {
11130                 cfg.cls += ' col-' + size + '-' + settings[size];
11131             }
11132         });
11133         
11134         return cfg;
11135         
11136     },
11137     
11138     
11139     
11140     // private
11141     onResize : function(w, h){
11142 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11143 //        if(typeof w == 'number'){
11144 //            var x = w - this.trigger.getWidth();
11145 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11146 //            this.trigger.setStyle('left', x+'px');
11147 //        }
11148     },
11149
11150     // private
11151     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11152
11153     // private
11154     getResizeEl : function(){
11155         return this.inputEl();
11156     },
11157
11158     // private
11159     getPositionEl : function(){
11160         return this.inputEl();
11161     },
11162
11163     // private
11164     alignErrorIcon : function(){
11165         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11166     },
11167
11168     // private
11169     initEvents : function(){
11170         
11171         this.createList();
11172         
11173         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11174         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11175         if(!this.multiple && this.showToggleBtn){
11176             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11177             if(this.hideTrigger){
11178                 this.trigger.setDisplayed(false);
11179             }
11180             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11181         }
11182         
11183         if(this.multiple){
11184             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11185         }
11186         
11187         if(this.removable && !this.editable && !this.tickable){
11188             var close = this.closeTriggerEl();
11189             
11190             if(close){
11191                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11192                 close.on('click', this.removeBtnClick, this, close);
11193             }
11194         }
11195         
11196         //this.trigger.addClassOnOver('x-form-trigger-over');
11197         //this.trigger.addClassOnClick('x-form-trigger-click');
11198         
11199         //if(!this.width){
11200         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11201         //}
11202     },
11203     
11204     closeTriggerEl : function()
11205     {
11206         var close = this.el.select('.roo-combo-removable-btn', true).first();
11207         return close ? close : false;
11208     },
11209     
11210     removeBtnClick : function(e, h, el)
11211     {
11212         e.preventDefault();
11213         
11214         if(this.fireEvent("remove", this) !== false){
11215             this.reset();
11216             this.fireEvent("afterremove", this)
11217         }
11218     },
11219     
11220     createList : function()
11221     {
11222         this.list = Roo.get(document.body).createChild({
11223             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11224             cls: 'typeahead typeahead-long dropdown-menu',
11225             style: 'display:none'
11226         });
11227         
11228         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11229         
11230     },
11231
11232     // private
11233     initTrigger : function(){
11234        
11235     },
11236
11237     // private
11238     onDestroy : function(){
11239         if(this.trigger){
11240             this.trigger.removeAllListeners();
11241           //  this.trigger.remove();
11242         }
11243         //if(this.wrap){
11244         //    this.wrap.remove();
11245         //}
11246         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11247     },
11248
11249     // private
11250     onFocus : function(){
11251         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11252         /*
11253         if(!this.mimicing){
11254             this.wrap.addClass('x-trigger-wrap-focus');
11255             this.mimicing = true;
11256             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11257             if(this.monitorTab){
11258                 this.el.on("keydown", this.checkTab, this);
11259             }
11260         }
11261         */
11262     },
11263
11264     // private
11265     checkTab : function(e){
11266         if(e.getKey() == e.TAB){
11267             this.triggerBlur();
11268         }
11269     },
11270
11271     // private
11272     onBlur : function(){
11273         // do nothing
11274     },
11275
11276     // private
11277     mimicBlur : function(e, t){
11278         /*
11279         if(!this.wrap.contains(t) && this.validateBlur()){
11280             this.triggerBlur();
11281         }
11282         */
11283     },
11284
11285     // private
11286     triggerBlur : function(){
11287         this.mimicing = false;
11288         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11289         if(this.monitorTab){
11290             this.el.un("keydown", this.checkTab, this);
11291         }
11292         //this.wrap.removeClass('x-trigger-wrap-focus');
11293         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11294     },
11295
11296     // private
11297     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11298     validateBlur : function(e, t){
11299         return true;
11300     },
11301
11302     // private
11303     onDisable : function(){
11304         this.inputEl().dom.disabled = true;
11305         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11306         //if(this.wrap){
11307         //    this.wrap.addClass('x-item-disabled');
11308         //}
11309     },
11310
11311     // private
11312     onEnable : function(){
11313         this.inputEl().dom.disabled = false;
11314         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11315         //if(this.wrap){
11316         //    this.el.removeClass('x-item-disabled');
11317         //}
11318     },
11319
11320     // private
11321     onShow : function(){
11322         var ae = this.getActionEl();
11323         
11324         if(ae){
11325             ae.dom.style.display = '';
11326             ae.dom.style.visibility = 'visible';
11327         }
11328     },
11329
11330     // private
11331     
11332     onHide : function(){
11333         var ae = this.getActionEl();
11334         ae.dom.style.display = 'none';
11335     },
11336
11337     /**
11338      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11339      * by an implementing function.
11340      * @method
11341      * @param {EventObject} e
11342      */
11343     onTriggerClick : Roo.emptyFn
11344 });
11345  /*
11346  * Based on:
11347  * Ext JS Library 1.1.1
11348  * Copyright(c) 2006-2007, Ext JS, LLC.
11349  *
11350  * Originally Released Under LGPL - original licence link has changed is not relivant.
11351  *
11352  * Fork - LGPL
11353  * <script type="text/javascript">
11354  */
11355
11356
11357 /**
11358  * @class Roo.data.SortTypes
11359  * @singleton
11360  * Defines the default sorting (casting?) comparison functions used when sorting data.
11361  */
11362 Roo.data.SortTypes = {
11363     /**
11364      * Default sort that does nothing
11365      * @param {Mixed} s The value being converted
11366      * @return {Mixed} The comparison value
11367      */
11368     none : function(s){
11369         return s;
11370     },
11371     
11372     /**
11373      * The regular expression used to strip tags
11374      * @type {RegExp}
11375      * @property
11376      */
11377     stripTagsRE : /<\/?[^>]+>/gi,
11378     
11379     /**
11380      * Strips all HTML tags to sort on text only
11381      * @param {Mixed} s The value being converted
11382      * @return {String} The comparison value
11383      */
11384     asText : function(s){
11385         return String(s).replace(this.stripTagsRE, "");
11386     },
11387     
11388     /**
11389      * Strips all HTML tags to sort on text only - Case insensitive
11390      * @param {Mixed} s The value being converted
11391      * @return {String} The comparison value
11392      */
11393     asUCText : function(s){
11394         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11395     },
11396     
11397     /**
11398      * Case insensitive string
11399      * @param {Mixed} s The value being converted
11400      * @return {String} The comparison value
11401      */
11402     asUCString : function(s) {
11403         return String(s).toUpperCase();
11404     },
11405     
11406     /**
11407      * Date sorting
11408      * @param {Mixed} s The value being converted
11409      * @return {Number} The comparison value
11410      */
11411     asDate : function(s) {
11412         if(!s){
11413             return 0;
11414         }
11415         if(s instanceof Date){
11416             return s.getTime();
11417         }
11418         return Date.parse(String(s));
11419     },
11420     
11421     /**
11422      * Float sorting
11423      * @param {Mixed} s The value being converted
11424      * @return {Float} The comparison value
11425      */
11426     asFloat : function(s) {
11427         var val = parseFloat(String(s).replace(/,/g, ""));
11428         if(isNaN(val)) {
11429             val = 0;
11430         }
11431         return val;
11432     },
11433     
11434     /**
11435      * Integer sorting
11436      * @param {Mixed} s The value being converted
11437      * @return {Number} The comparison value
11438      */
11439     asInt : function(s) {
11440         var val = parseInt(String(s).replace(/,/g, ""));
11441         if(isNaN(val)) {
11442             val = 0;
11443         }
11444         return val;
11445     }
11446 };/*
11447  * Based on:
11448  * Ext JS Library 1.1.1
11449  * Copyright(c) 2006-2007, Ext JS, LLC.
11450  *
11451  * Originally Released Under LGPL - original licence link has changed is not relivant.
11452  *
11453  * Fork - LGPL
11454  * <script type="text/javascript">
11455  */
11456
11457 /**
11458 * @class Roo.data.Record
11459  * Instances of this class encapsulate both record <em>definition</em> information, and record
11460  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11461  * to access Records cached in an {@link Roo.data.Store} object.<br>
11462  * <p>
11463  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11464  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11465  * objects.<br>
11466  * <p>
11467  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11468  * @constructor
11469  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11470  * {@link #create}. The parameters are the same.
11471  * @param {Array} data An associative Array of data values keyed by the field name.
11472  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11473  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11474  * not specified an integer id is generated.
11475  */
11476 Roo.data.Record = function(data, id){
11477     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11478     this.data = data;
11479 };
11480
11481 /**
11482  * Generate a constructor for a specific record layout.
11483  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11484  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11485  * Each field definition object may contain the following properties: <ul>
11486  * <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,
11487  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11488  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11489  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11490  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11491  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11492  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11493  * this may be omitted.</p></li>
11494  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11495  * <ul><li>auto (Default, implies no conversion)</li>
11496  * <li>string</li>
11497  * <li>int</li>
11498  * <li>float</li>
11499  * <li>boolean</li>
11500  * <li>date</li></ul></p></li>
11501  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11502  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11503  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11504  * by the Reader into an object that will be stored in the Record. It is passed the
11505  * following parameters:<ul>
11506  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11507  * </ul></p></li>
11508  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11509  * </ul>
11510  * <br>usage:<br><pre><code>
11511 var TopicRecord = Roo.data.Record.create(
11512     {name: 'title', mapping: 'topic_title'},
11513     {name: 'author', mapping: 'username'},
11514     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11515     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11516     {name: 'lastPoster', mapping: 'user2'},
11517     {name: 'excerpt', mapping: 'post_text'}
11518 );
11519
11520 var myNewRecord = new TopicRecord({
11521     title: 'Do my job please',
11522     author: 'noobie',
11523     totalPosts: 1,
11524     lastPost: new Date(),
11525     lastPoster: 'Animal',
11526     excerpt: 'No way dude!'
11527 });
11528 myStore.add(myNewRecord);
11529 </code></pre>
11530  * @method create
11531  * @static
11532  */
11533 Roo.data.Record.create = function(o){
11534     var f = function(){
11535         f.superclass.constructor.apply(this, arguments);
11536     };
11537     Roo.extend(f, Roo.data.Record);
11538     var p = f.prototype;
11539     p.fields = new Roo.util.MixedCollection(false, function(field){
11540         return field.name;
11541     });
11542     for(var i = 0, len = o.length; i < len; i++){
11543         p.fields.add(new Roo.data.Field(o[i]));
11544     }
11545     f.getField = function(name){
11546         return p.fields.get(name);  
11547     };
11548     return f;
11549 };
11550
11551 Roo.data.Record.AUTO_ID = 1000;
11552 Roo.data.Record.EDIT = 'edit';
11553 Roo.data.Record.REJECT = 'reject';
11554 Roo.data.Record.COMMIT = 'commit';
11555
11556 Roo.data.Record.prototype = {
11557     /**
11558      * Readonly flag - true if this record has been modified.
11559      * @type Boolean
11560      */
11561     dirty : false,
11562     editing : false,
11563     error: null,
11564     modified: null,
11565
11566     // private
11567     join : function(store){
11568         this.store = store;
11569     },
11570
11571     /**
11572      * Set the named field to the specified value.
11573      * @param {String} name The name of the field to set.
11574      * @param {Object} value The value to set the field to.
11575      */
11576     set : function(name, value){
11577         if(this.data[name] == value){
11578             return;
11579         }
11580         this.dirty = true;
11581         if(!this.modified){
11582             this.modified = {};
11583         }
11584         if(typeof this.modified[name] == 'undefined'){
11585             this.modified[name] = this.data[name];
11586         }
11587         this.data[name] = value;
11588         if(!this.editing && this.store){
11589             this.store.afterEdit(this);
11590         }       
11591     },
11592
11593     /**
11594      * Get the value of the named field.
11595      * @param {String} name The name of the field to get the value of.
11596      * @return {Object} The value of the field.
11597      */
11598     get : function(name){
11599         return this.data[name]; 
11600     },
11601
11602     // private
11603     beginEdit : function(){
11604         this.editing = true;
11605         this.modified = {}; 
11606     },
11607
11608     // private
11609     cancelEdit : function(){
11610         this.editing = false;
11611         delete this.modified;
11612     },
11613
11614     // private
11615     endEdit : function(){
11616         this.editing = false;
11617         if(this.dirty && this.store){
11618             this.store.afterEdit(this);
11619         }
11620     },
11621
11622     /**
11623      * Usually called by the {@link Roo.data.Store} which owns the Record.
11624      * Rejects all changes made to the Record since either creation, or the last commit operation.
11625      * Modified fields are reverted to their original values.
11626      * <p>
11627      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11628      * of reject operations.
11629      */
11630     reject : function(){
11631         var m = this.modified;
11632         for(var n in m){
11633             if(typeof m[n] != "function"){
11634                 this.data[n] = m[n];
11635             }
11636         }
11637         this.dirty = false;
11638         delete this.modified;
11639         this.editing = false;
11640         if(this.store){
11641             this.store.afterReject(this);
11642         }
11643     },
11644
11645     /**
11646      * Usually called by the {@link Roo.data.Store} which owns the Record.
11647      * Commits all changes made to the Record since either creation, or the last commit operation.
11648      * <p>
11649      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11650      * of commit operations.
11651      */
11652     commit : function(){
11653         this.dirty = false;
11654         delete this.modified;
11655         this.editing = false;
11656         if(this.store){
11657             this.store.afterCommit(this);
11658         }
11659     },
11660
11661     // private
11662     hasError : function(){
11663         return this.error != null;
11664     },
11665
11666     // private
11667     clearError : function(){
11668         this.error = null;
11669     },
11670
11671     /**
11672      * Creates a copy of this record.
11673      * @param {String} id (optional) A new record id if you don't want to use this record's id
11674      * @return {Record}
11675      */
11676     copy : function(newId) {
11677         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11678     }
11679 };/*
11680  * Based on:
11681  * Ext JS Library 1.1.1
11682  * Copyright(c) 2006-2007, Ext JS, LLC.
11683  *
11684  * Originally Released Under LGPL - original licence link has changed is not relivant.
11685  *
11686  * Fork - LGPL
11687  * <script type="text/javascript">
11688  */
11689
11690
11691
11692 /**
11693  * @class Roo.data.Store
11694  * @extends Roo.util.Observable
11695  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11696  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11697  * <p>
11698  * 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
11699  * has no knowledge of the format of the data returned by the Proxy.<br>
11700  * <p>
11701  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11702  * instances from the data object. These records are cached and made available through accessor functions.
11703  * @constructor
11704  * Creates a new Store.
11705  * @param {Object} config A config object containing the objects needed for the Store to access data,
11706  * and read the data into Records.
11707  */
11708 Roo.data.Store = function(config){
11709     this.data = new Roo.util.MixedCollection(false);
11710     this.data.getKey = function(o){
11711         return o.id;
11712     };
11713     this.baseParams = {};
11714     // private
11715     this.paramNames = {
11716         "start" : "start",
11717         "limit" : "limit",
11718         "sort" : "sort",
11719         "dir" : "dir",
11720         "multisort" : "_multisort"
11721     };
11722
11723     if(config && config.data){
11724         this.inlineData = config.data;
11725         delete config.data;
11726     }
11727
11728     Roo.apply(this, config);
11729     
11730     if(this.reader){ // reader passed
11731         this.reader = Roo.factory(this.reader, Roo.data);
11732         this.reader.xmodule = this.xmodule || false;
11733         if(!this.recordType){
11734             this.recordType = this.reader.recordType;
11735         }
11736         if(this.reader.onMetaChange){
11737             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11738         }
11739     }
11740
11741     if(this.recordType){
11742         this.fields = this.recordType.prototype.fields;
11743     }
11744     this.modified = [];
11745
11746     this.addEvents({
11747         /**
11748          * @event datachanged
11749          * Fires when the data cache has changed, and a widget which is using this Store
11750          * as a Record cache should refresh its view.
11751          * @param {Store} this
11752          */
11753         datachanged : true,
11754         /**
11755          * @event metachange
11756          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11757          * @param {Store} this
11758          * @param {Object} meta The JSON metadata
11759          */
11760         metachange : true,
11761         /**
11762          * @event add
11763          * Fires when Records have been added to the Store
11764          * @param {Store} this
11765          * @param {Roo.data.Record[]} records The array of Records added
11766          * @param {Number} index The index at which the record(s) were added
11767          */
11768         add : true,
11769         /**
11770          * @event remove
11771          * Fires when a Record has been removed from the Store
11772          * @param {Store} this
11773          * @param {Roo.data.Record} record The Record that was removed
11774          * @param {Number} index The index at which the record was removed
11775          */
11776         remove : true,
11777         /**
11778          * @event update
11779          * Fires when a Record has been updated
11780          * @param {Store} this
11781          * @param {Roo.data.Record} record The Record that was updated
11782          * @param {String} operation The update operation being performed.  Value may be one of:
11783          * <pre><code>
11784  Roo.data.Record.EDIT
11785  Roo.data.Record.REJECT
11786  Roo.data.Record.COMMIT
11787          * </code></pre>
11788          */
11789         update : true,
11790         /**
11791          * @event clear
11792          * Fires when the data cache has been cleared.
11793          * @param {Store} this
11794          */
11795         clear : true,
11796         /**
11797          * @event beforeload
11798          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11799          * the load action will be canceled.
11800          * @param {Store} this
11801          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11802          */
11803         beforeload : true,
11804         /**
11805          * @event beforeloadadd
11806          * Fires after a new set of Records has been loaded.
11807          * @param {Store} this
11808          * @param {Roo.data.Record[]} records The Records that were loaded
11809          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11810          */
11811         beforeloadadd : true,
11812         /**
11813          * @event load
11814          * Fires after a new set of Records has been loaded, before they are added to the store.
11815          * @param {Store} this
11816          * @param {Roo.data.Record[]} records The Records that were loaded
11817          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11818          * @params {Object} return from reader
11819          */
11820         load : true,
11821         /**
11822          * @event loadexception
11823          * Fires if an exception occurs in the Proxy during loading.
11824          * Called with the signature of the Proxy's "loadexception" event.
11825          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11826          * 
11827          * @param {Proxy} 
11828          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11829          * @param {Object} load options 
11830          * @param {Object} jsonData from your request (normally this contains the Exception)
11831          */
11832         loadexception : true
11833     });
11834     
11835     if(this.proxy){
11836         this.proxy = Roo.factory(this.proxy, Roo.data);
11837         this.proxy.xmodule = this.xmodule || false;
11838         this.relayEvents(this.proxy,  ["loadexception"]);
11839     }
11840     this.sortToggle = {};
11841     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11842
11843     Roo.data.Store.superclass.constructor.call(this);
11844
11845     if(this.inlineData){
11846         this.loadData(this.inlineData);
11847         delete this.inlineData;
11848     }
11849 };
11850
11851 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11852      /**
11853     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11854     * without a remote query - used by combo/forms at present.
11855     */
11856     
11857     /**
11858     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11859     */
11860     /**
11861     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11862     */
11863     /**
11864     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11865     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11866     */
11867     /**
11868     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11869     * on any HTTP request
11870     */
11871     /**
11872     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11873     */
11874     /**
11875     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11876     */
11877     multiSort: false,
11878     /**
11879     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11880     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11881     */
11882     remoteSort : false,
11883
11884     /**
11885     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11886      * loaded or when a record is removed. (defaults to false).
11887     */
11888     pruneModifiedRecords : false,
11889
11890     // private
11891     lastOptions : null,
11892
11893     /**
11894      * Add Records to the Store and fires the add event.
11895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11896      */
11897     add : function(records){
11898         records = [].concat(records);
11899         for(var i = 0, len = records.length; i < len; i++){
11900             records[i].join(this);
11901         }
11902         var index = this.data.length;
11903         this.data.addAll(records);
11904         this.fireEvent("add", this, records, index);
11905     },
11906
11907     /**
11908      * Remove a Record from the Store and fires the remove event.
11909      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11910      */
11911     remove : function(record){
11912         var index = this.data.indexOf(record);
11913         this.data.removeAt(index);
11914  
11915         if(this.pruneModifiedRecords){
11916             this.modified.remove(record);
11917         }
11918         this.fireEvent("remove", this, record, index);
11919     },
11920
11921     /**
11922      * Remove all Records from the Store and fires the clear event.
11923      */
11924     removeAll : function(){
11925         this.data.clear();
11926         if(this.pruneModifiedRecords){
11927             this.modified = [];
11928         }
11929         this.fireEvent("clear", this);
11930     },
11931
11932     /**
11933      * Inserts Records to the Store at the given index and fires the add event.
11934      * @param {Number} index The start index at which to insert the passed Records.
11935      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11936      */
11937     insert : function(index, records){
11938         records = [].concat(records);
11939         for(var i = 0, len = records.length; i < len; i++){
11940             this.data.insert(index, records[i]);
11941             records[i].join(this);
11942         }
11943         this.fireEvent("add", this, records, index);
11944     },
11945
11946     /**
11947      * Get the index within the cache of the passed Record.
11948      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11949      * @return {Number} The index of the passed Record. Returns -1 if not found.
11950      */
11951     indexOf : function(record){
11952         return this.data.indexOf(record);
11953     },
11954
11955     /**
11956      * Get the index within the cache of the Record with the passed id.
11957      * @param {String} id The id of the Record to find.
11958      * @return {Number} The index of the Record. Returns -1 if not found.
11959      */
11960     indexOfId : function(id){
11961         return this.data.indexOfKey(id);
11962     },
11963
11964     /**
11965      * Get the Record with the specified id.
11966      * @param {String} id The id of the Record to find.
11967      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11968      */
11969     getById : function(id){
11970         return this.data.key(id);
11971     },
11972
11973     /**
11974      * Get the Record at the specified index.
11975      * @param {Number} index The index of the Record to find.
11976      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11977      */
11978     getAt : function(index){
11979         return this.data.itemAt(index);
11980     },
11981
11982     /**
11983      * Returns a range of Records between specified indices.
11984      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11985      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11986      * @return {Roo.data.Record[]} An array of Records
11987      */
11988     getRange : function(start, end){
11989         return this.data.getRange(start, end);
11990     },
11991
11992     // private
11993     storeOptions : function(o){
11994         o = Roo.apply({}, o);
11995         delete o.callback;
11996         delete o.scope;
11997         this.lastOptions = o;
11998     },
11999
12000     /**
12001      * Loads the Record cache from the configured Proxy using the configured Reader.
12002      * <p>
12003      * If using remote paging, then the first load call must specify the <em>start</em>
12004      * and <em>limit</em> properties in the options.params property to establish the initial
12005      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12006      * <p>
12007      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12008      * and this call will return before the new data has been loaded. Perform any post-processing
12009      * in a callback function, or in a "load" event handler.</strong>
12010      * <p>
12011      * @param {Object} options An object containing properties which control loading options:<ul>
12012      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12013      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12014      * passed the following arguments:<ul>
12015      * <li>r : Roo.data.Record[]</li>
12016      * <li>options: Options object from the load call</li>
12017      * <li>success: Boolean success indicator</li></ul></li>
12018      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12019      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12020      * </ul>
12021      */
12022     load : function(options){
12023         options = options || {};
12024         if(this.fireEvent("beforeload", this, options) !== false){
12025             this.storeOptions(options);
12026             var p = Roo.apply(options.params || {}, this.baseParams);
12027             // if meta was not loaded from remote source.. try requesting it.
12028             if (!this.reader.metaFromRemote) {
12029                 p._requestMeta = 1;
12030             }
12031             if(this.sortInfo && this.remoteSort){
12032                 var pn = this.paramNames;
12033                 p[pn["sort"]] = this.sortInfo.field;
12034                 p[pn["dir"]] = this.sortInfo.direction;
12035             }
12036             if (this.multiSort) {
12037                 var pn = this.paramNames;
12038                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12039             }
12040             
12041             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12042         }
12043     },
12044
12045     /**
12046      * Reloads the Record cache from the configured Proxy using the configured Reader and
12047      * the options from the last load operation performed.
12048      * @param {Object} options (optional) An object containing properties which may override the options
12049      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12050      * the most recently used options are reused).
12051      */
12052     reload : function(options){
12053         this.load(Roo.applyIf(options||{}, this.lastOptions));
12054     },
12055
12056     // private
12057     // Called as a callback by the Reader during a load operation.
12058     loadRecords : function(o, options, success){
12059         if(!o || success === false){
12060             if(success !== false){
12061                 this.fireEvent("load", this, [], options, o);
12062             }
12063             if(options.callback){
12064                 options.callback.call(options.scope || this, [], options, false);
12065             }
12066             return;
12067         }
12068         // if data returned failure - throw an exception.
12069         if (o.success === false) {
12070             // show a message if no listener is registered.
12071             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12072                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12073             }
12074             // loadmask wil be hooked into this..
12075             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12076             return;
12077         }
12078         var r = o.records, t = o.totalRecords || r.length;
12079         
12080         this.fireEvent("beforeloadadd", this, r, options, o);
12081         
12082         if(!options || options.add !== true){
12083             if(this.pruneModifiedRecords){
12084                 this.modified = [];
12085             }
12086             for(var i = 0, len = r.length; i < len; i++){
12087                 r[i].join(this);
12088             }
12089             if(this.snapshot){
12090                 this.data = this.snapshot;
12091                 delete this.snapshot;
12092             }
12093             this.data.clear();
12094             this.data.addAll(r);
12095             this.totalLength = t;
12096             this.applySort();
12097             this.fireEvent("datachanged", this);
12098         }else{
12099             this.totalLength = Math.max(t, this.data.length+r.length);
12100             this.add(r);
12101         }
12102         
12103         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12104                 
12105             var e = new Roo.data.Record({});
12106
12107             e.set(this.parent.displayField, this.parent.emptyTitle);
12108             e.set(this.parent.valueField, '');
12109
12110             this.insert(0, e);
12111         }
12112             
12113         this.fireEvent("load", this, r, options, o);
12114         if(options.callback){
12115             options.callback.call(options.scope || this, r, options, true);
12116         }
12117     },
12118
12119
12120     /**
12121      * Loads data from a passed data block. A Reader which understands the format of the data
12122      * must have been configured in the constructor.
12123      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12124      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12125      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12126      */
12127     loadData : function(o, append){
12128         var r = this.reader.readRecords(o);
12129         this.loadRecords(r, {add: append}, true);
12130     },
12131     
12132      /**
12133      * using 'cn' the nested child reader read the child array into it's child stores.
12134      * @param {Object} rec The record with a 'children array
12135      */
12136     loadDataFromChildren : function(rec)
12137     {
12138         this.loadData(this.reader.toLoadData(rec));
12139     },
12140     
12141
12142     /**
12143      * Gets the number of cached records.
12144      * <p>
12145      * <em>If using paging, this may not be the total size of the dataset. If the data object
12146      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12147      * the data set size</em>
12148      */
12149     getCount : function(){
12150         return this.data.length || 0;
12151     },
12152
12153     /**
12154      * Gets the total number of records in the dataset as returned by the server.
12155      * <p>
12156      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12157      * the dataset size</em>
12158      */
12159     getTotalCount : function(){
12160         return this.totalLength || 0;
12161     },
12162
12163     /**
12164      * Returns the sort state of the Store as an object with two properties:
12165      * <pre><code>
12166  field {String} The name of the field by which the Records are sorted
12167  direction {String} The sort order, "ASC" or "DESC"
12168      * </code></pre>
12169      */
12170     getSortState : function(){
12171         return this.sortInfo;
12172     },
12173
12174     // private
12175     applySort : function(){
12176         if(this.sortInfo && !this.remoteSort){
12177             var s = this.sortInfo, f = s.field;
12178             var st = this.fields.get(f).sortType;
12179             var fn = function(r1, r2){
12180                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12181                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12182             };
12183             this.data.sort(s.direction, fn);
12184             if(this.snapshot && this.snapshot != this.data){
12185                 this.snapshot.sort(s.direction, fn);
12186             }
12187         }
12188     },
12189
12190     /**
12191      * Sets the default sort column and order to be used by the next load operation.
12192      * @param {String} fieldName The name of the field to sort by.
12193      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12194      */
12195     setDefaultSort : function(field, dir){
12196         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12197     },
12198
12199     /**
12200      * Sort the Records.
12201      * If remote sorting is used, the sort is performed on the server, and the cache is
12202      * reloaded. If local sorting is used, the cache is sorted internally.
12203      * @param {String} fieldName The name of the field to sort by.
12204      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12205      */
12206     sort : function(fieldName, dir){
12207         var f = this.fields.get(fieldName);
12208         if(!dir){
12209             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12210             
12211             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12212                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12213             }else{
12214                 dir = f.sortDir;
12215             }
12216         }
12217         this.sortToggle[f.name] = dir;
12218         this.sortInfo = {field: f.name, direction: dir};
12219         if(!this.remoteSort){
12220             this.applySort();
12221             this.fireEvent("datachanged", this);
12222         }else{
12223             this.load(this.lastOptions);
12224         }
12225     },
12226
12227     /**
12228      * Calls the specified function for each of the Records in the cache.
12229      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12230      * Returning <em>false</em> aborts and exits the iteration.
12231      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12232      */
12233     each : function(fn, scope){
12234         this.data.each(fn, scope);
12235     },
12236
12237     /**
12238      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12239      * (e.g., during paging).
12240      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12241      */
12242     getModifiedRecords : function(){
12243         return this.modified;
12244     },
12245
12246     // private
12247     createFilterFn : function(property, value, anyMatch){
12248         if(!value.exec){ // not a regex
12249             value = String(value);
12250             if(value.length == 0){
12251                 return false;
12252             }
12253             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12254         }
12255         return function(r){
12256             return value.test(r.data[property]);
12257         };
12258     },
12259
12260     /**
12261      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12262      * @param {String} property A field on your records
12263      * @param {Number} start The record index to start at (defaults to 0)
12264      * @param {Number} end The last record index to include (defaults to length - 1)
12265      * @return {Number} The sum
12266      */
12267     sum : function(property, start, end){
12268         var rs = this.data.items, v = 0;
12269         start = start || 0;
12270         end = (end || end === 0) ? end : rs.length-1;
12271
12272         for(var i = start; i <= end; i++){
12273             v += (rs[i].data[property] || 0);
12274         }
12275         return v;
12276     },
12277
12278     /**
12279      * Filter the records by a specified property.
12280      * @param {String} field A field on your records
12281      * @param {String/RegExp} value Either a string that the field
12282      * should start with or a RegExp to test against the field
12283      * @param {Boolean} anyMatch True to match any part not just the beginning
12284      */
12285     filter : function(property, value, anyMatch){
12286         var fn = this.createFilterFn(property, value, anyMatch);
12287         return fn ? this.filterBy(fn) : this.clearFilter();
12288     },
12289
12290     /**
12291      * Filter by a function. The specified function will be called with each
12292      * record in this data source. If the function returns true the record is included,
12293      * otherwise it is filtered.
12294      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12295      * @param {Object} scope (optional) The scope of the function (defaults to this)
12296      */
12297     filterBy : function(fn, scope){
12298         this.snapshot = this.snapshot || this.data;
12299         this.data = this.queryBy(fn, scope||this);
12300         this.fireEvent("datachanged", this);
12301     },
12302
12303     /**
12304      * Query the records by a specified property.
12305      * @param {String} field A field on your records
12306      * @param {String/RegExp} value Either a string that the field
12307      * should start with or a RegExp to test against the field
12308      * @param {Boolean} anyMatch True to match any part not just the beginning
12309      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12310      */
12311     query : function(property, value, anyMatch){
12312         var fn = this.createFilterFn(property, value, anyMatch);
12313         return fn ? this.queryBy(fn) : this.data.clone();
12314     },
12315
12316     /**
12317      * Query by a function. The specified function will be called with each
12318      * record in this data source. If the function returns true the record is included
12319      * in the results.
12320      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12321      * @param {Object} scope (optional) The scope of the function (defaults to this)
12322       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12323      **/
12324     queryBy : function(fn, scope){
12325         var data = this.snapshot || this.data;
12326         return data.filterBy(fn, scope||this);
12327     },
12328
12329     /**
12330      * Collects unique values for a particular dataIndex from this store.
12331      * @param {String} dataIndex The property to collect
12332      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12333      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12334      * @return {Array} An array of the unique values
12335      **/
12336     collect : function(dataIndex, allowNull, bypassFilter){
12337         var d = (bypassFilter === true && this.snapshot) ?
12338                 this.snapshot.items : this.data.items;
12339         var v, sv, r = [], l = {};
12340         for(var i = 0, len = d.length; i < len; i++){
12341             v = d[i].data[dataIndex];
12342             sv = String(v);
12343             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12344                 l[sv] = true;
12345                 r[r.length] = v;
12346             }
12347         }
12348         return r;
12349     },
12350
12351     /**
12352      * Revert to a view of the Record cache with no filtering applied.
12353      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12354      */
12355     clearFilter : function(suppressEvent){
12356         if(this.snapshot && this.snapshot != this.data){
12357             this.data = this.snapshot;
12358             delete this.snapshot;
12359             if(suppressEvent !== true){
12360                 this.fireEvent("datachanged", this);
12361             }
12362         }
12363     },
12364
12365     // private
12366     afterEdit : function(record){
12367         if(this.modified.indexOf(record) == -1){
12368             this.modified.push(record);
12369         }
12370         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12371     },
12372     
12373     // private
12374     afterReject : function(record){
12375         this.modified.remove(record);
12376         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12377     },
12378
12379     // private
12380     afterCommit : function(record){
12381         this.modified.remove(record);
12382         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12383     },
12384
12385     /**
12386      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12387      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12388      */
12389     commitChanges : function(){
12390         var m = this.modified.slice(0);
12391         this.modified = [];
12392         for(var i = 0, len = m.length; i < len; i++){
12393             m[i].commit();
12394         }
12395     },
12396
12397     /**
12398      * Cancel outstanding changes on all changed records.
12399      */
12400     rejectChanges : function(){
12401         var m = this.modified.slice(0);
12402         this.modified = [];
12403         for(var i = 0, len = m.length; i < len; i++){
12404             m[i].reject();
12405         }
12406     },
12407
12408     onMetaChange : function(meta, rtype, o){
12409         this.recordType = rtype;
12410         this.fields = rtype.prototype.fields;
12411         delete this.snapshot;
12412         this.sortInfo = meta.sortInfo || this.sortInfo;
12413         this.modified = [];
12414         this.fireEvent('metachange', this, this.reader.meta);
12415     },
12416     
12417     moveIndex : function(data, type)
12418     {
12419         var index = this.indexOf(data);
12420         
12421         var newIndex = index + type;
12422         
12423         this.remove(data);
12424         
12425         this.insert(newIndex, data);
12426         
12427     }
12428 });/*
12429  * Based on:
12430  * Ext JS Library 1.1.1
12431  * Copyright(c) 2006-2007, Ext JS, LLC.
12432  *
12433  * Originally Released Under LGPL - original licence link has changed is not relivant.
12434  *
12435  * Fork - LGPL
12436  * <script type="text/javascript">
12437  */
12438
12439 /**
12440  * @class Roo.data.SimpleStore
12441  * @extends Roo.data.Store
12442  * Small helper class to make creating Stores from Array data easier.
12443  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12444  * @cfg {Array} fields An array of field definition objects, or field name strings.
12445  * @cfg {Object} an existing reader (eg. copied from another store)
12446  * @cfg {Array} data The multi-dimensional array of data
12447  * @constructor
12448  * @param {Object} config
12449  */
12450 Roo.data.SimpleStore = function(config)
12451 {
12452     Roo.data.SimpleStore.superclass.constructor.call(this, {
12453         isLocal : true,
12454         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12455                 id: config.id
12456             },
12457             Roo.data.Record.create(config.fields)
12458         ),
12459         proxy : new Roo.data.MemoryProxy(config.data)
12460     });
12461     this.load();
12462 };
12463 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12464  * Based on:
12465  * Ext JS Library 1.1.1
12466  * Copyright(c) 2006-2007, Ext JS, LLC.
12467  *
12468  * Originally Released Under LGPL - original licence link has changed is not relivant.
12469  *
12470  * Fork - LGPL
12471  * <script type="text/javascript">
12472  */
12473
12474 /**
12475 /**
12476  * @extends Roo.data.Store
12477  * @class Roo.data.JsonStore
12478  * Small helper class to make creating Stores for JSON data easier. <br/>
12479 <pre><code>
12480 var store = new Roo.data.JsonStore({
12481     url: 'get-images.php',
12482     root: 'images',
12483     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12484 });
12485 </code></pre>
12486  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12487  * JsonReader and HttpProxy (unless inline data is provided).</b>
12488  * @cfg {Array} fields An array of field definition objects, or field name strings.
12489  * @constructor
12490  * @param {Object} config
12491  */
12492 Roo.data.JsonStore = function(c){
12493     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12494         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12495         reader: new Roo.data.JsonReader(c, c.fields)
12496     }));
12497 };
12498 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12499  * Based on:
12500  * Ext JS Library 1.1.1
12501  * Copyright(c) 2006-2007, Ext JS, LLC.
12502  *
12503  * Originally Released Under LGPL - original licence link has changed is not relivant.
12504  *
12505  * Fork - LGPL
12506  * <script type="text/javascript">
12507  */
12508
12509  
12510 Roo.data.Field = function(config){
12511     if(typeof config == "string"){
12512         config = {name: config};
12513     }
12514     Roo.apply(this, config);
12515     
12516     if(!this.type){
12517         this.type = "auto";
12518     }
12519     
12520     var st = Roo.data.SortTypes;
12521     // named sortTypes are supported, here we look them up
12522     if(typeof this.sortType == "string"){
12523         this.sortType = st[this.sortType];
12524     }
12525     
12526     // set default sortType for strings and dates
12527     if(!this.sortType){
12528         switch(this.type){
12529             case "string":
12530                 this.sortType = st.asUCString;
12531                 break;
12532             case "date":
12533                 this.sortType = st.asDate;
12534                 break;
12535             default:
12536                 this.sortType = st.none;
12537         }
12538     }
12539
12540     // define once
12541     var stripRe = /[\$,%]/g;
12542
12543     // prebuilt conversion function for this field, instead of
12544     // switching every time we're reading a value
12545     if(!this.convert){
12546         var cv, dateFormat = this.dateFormat;
12547         switch(this.type){
12548             case "":
12549             case "auto":
12550             case undefined:
12551                 cv = function(v){ return v; };
12552                 break;
12553             case "string":
12554                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12555                 break;
12556             case "int":
12557                 cv = function(v){
12558                     return v !== undefined && v !== null && v !== '' ?
12559                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12560                     };
12561                 break;
12562             case "float":
12563                 cv = function(v){
12564                     return v !== undefined && v !== null && v !== '' ?
12565                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12566                     };
12567                 break;
12568             case "bool":
12569             case "boolean":
12570                 cv = function(v){ return v === true || v === "true" || v == 1; };
12571                 break;
12572             case "date":
12573                 cv = function(v){
12574                     if(!v){
12575                         return '';
12576                     }
12577                     if(v instanceof Date){
12578                         return v;
12579                     }
12580                     if(dateFormat){
12581                         if(dateFormat == "timestamp"){
12582                             return new Date(v*1000);
12583                         }
12584                         return Date.parseDate(v, dateFormat);
12585                     }
12586                     var parsed = Date.parse(v);
12587                     return parsed ? new Date(parsed) : null;
12588                 };
12589              break;
12590             
12591         }
12592         this.convert = cv;
12593     }
12594 };
12595
12596 Roo.data.Field.prototype = {
12597     dateFormat: null,
12598     defaultValue: "",
12599     mapping: null,
12600     sortType : null,
12601     sortDir : "ASC"
12602 };/*
12603  * Based on:
12604  * Ext JS Library 1.1.1
12605  * Copyright(c) 2006-2007, Ext JS, LLC.
12606  *
12607  * Originally Released Under LGPL - original licence link has changed is not relivant.
12608  *
12609  * Fork - LGPL
12610  * <script type="text/javascript">
12611  */
12612  
12613 // Base class for reading structured data from a data source.  This class is intended to be
12614 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12615
12616 /**
12617  * @class Roo.data.DataReader
12618  * Base class for reading structured data from a data source.  This class is intended to be
12619  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12620  */
12621
12622 Roo.data.DataReader = function(meta, recordType){
12623     
12624     this.meta = meta;
12625     
12626     this.recordType = recordType instanceof Array ? 
12627         Roo.data.Record.create(recordType) : recordType;
12628 };
12629
12630 Roo.data.DataReader.prototype = {
12631     
12632     
12633     readerType : 'Data',
12634      /**
12635      * Create an empty record
12636      * @param {Object} data (optional) - overlay some values
12637      * @return {Roo.data.Record} record created.
12638      */
12639     newRow :  function(d) {
12640         var da =  {};
12641         this.recordType.prototype.fields.each(function(c) {
12642             switch( c.type) {
12643                 case 'int' : da[c.name] = 0; break;
12644                 case 'date' : da[c.name] = new Date(); break;
12645                 case 'float' : da[c.name] = 0.0; break;
12646                 case 'boolean' : da[c.name] = false; break;
12647                 default : da[c.name] = ""; break;
12648             }
12649             
12650         });
12651         return new this.recordType(Roo.apply(da, d));
12652     }
12653     
12654     
12655 };/*
12656  * Based on:
12657  * Ext JS Library 1.1.1
12658  * Copyright(c) 2006-2007, Ext JS, LLC.
12659  *
12660  * Originally Released Under LGPL - original licence link has changed is not relivant.
12661  *
12662  * Fork - LGPL
12663  * <script type="text/javascript">
12664  */
12665
12666 /**
12667  * @class Roo.data.DataProxy
12668  * @extends Roo.data.Observable
12669  * This class is an abstract base class for implementations which provide retrieval of
12670  * unformatted data objects.<br>
12671  * <p>
12672  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12673  * (of the appropriate type which knows how to parse the data object) to provide a block of
12674  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12675  * <p>
12676  * Custom implementations must implement the load method as described in
12677  * {@link Roo.data.HttpProxy#load}.
12678  */
12679 Roo.data.DataProxy = function(){
12680     this.addEvents({
12681         /**
12682          * @event beforeload
12683          * Fires before a network request is made to retrieve a data object.
12684          * @param {Object} This DataProxy object.
12685          * @param {Object} params The params parameter to the load function.
12686          */
12687         beforeload : true,
12688         /**
12689          * @event load
12690          * Fires before the load method's callback is called.
12691          * @param {Object} This DataProxy object.
12692          * @param {Object} o The data object.
12693          * @param {Object} arg The callback argument object passed to the load function.
12694          */
12695         load : true,
12696         /**
12697          * @event loadexception
12698          * Fires if an Exception occurs during data retrieval.
12699          * @param {Object} This DataProxy object.
12700          * @param {Object} o The data object.
12701          * @param {Object} arg The callback argument object passed to the load function.
12702          * @param {Object} e The Exception.
12703          */
12704         loadexception : true
12705     });
12706     Roo.data.DataProxy.superclass.constructor.call(this);
12707 };
12708
12709 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12710
12711     /**
12712      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12713      */
12714 /*
12715  * Based on:
12716  * Ext JS Library 1.1.1
12717  * Copyright(c) 2006-2007, Ext JS, LLC.
12718  *
12719  * Originally Released Under LGPL - original licence link has changed is not relivant.
12720  *
12721  * Fork - LGPL
12722  * <script type="text/javascript">
12723  */
12724 /**
12725  * @class Roo.data.MemoryProxy
12726  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12727  * to the Reader when its load method is called.
12728  * @constructor
12729  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12730  */
12731 Roo.data.MemoryProxy = function(data){
12732     if (data.data) {
12733         data = data.data;
12734     }
12735     Roo.data.MemoryProxy.superclass.constructor.call(this);
12736     this.data = data;
12737 };
12738
12739 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12740     
12741     /**
12742      * Load data from the requested source (in this case an in-memory
12743      * data object passed to the constructor), read the data object into
12744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12745      * process that block using the passed callback.
12746      * @param {Object} params This parameter is not used by the MemoryProxy class.
12747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12748      * object into a block of Roo.data.Records.
12749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12750      * The function must be passed <ul>
12751      * <li>The Record block object</li>
12752      * <li>The "arg" argument from the load function</li>
12753      * <li>A boolean success indicator</li>
12754      * </ul>
12755      * @param {Object} scope The scope in which to call the callback
12756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12757      */
12758     load : function(params, reader, callback, scope, arg){
12759         params = params || {};
12760         var result;
12761         try {
12762             result = reader.readRecords(params.data ? params.data :this.data);
12763         }catch(e){
12764             this.fireEvent("loadexception", this, arg, null, e);
12765             callback.call(scope, null, arg, false);
12766             return;
12767         }
12768         callback.call(scope, result, arg, true);
12769     },
12770     
12771     // private
12772     update : function(params, records){
12773         
12774     }
12775 });/*
12776  * Based on:
12777  * Ext JS Library 1.1.1
12778  * Copyright(c) 2006-2007, Ext JS, LLC.
12779  *
12780  * Originally Released Under LGPL - original licence link has changed is not relivant.
12781  *
12782  * Fork - LGPL
12783  * <script type="text/javascript">
12784  */
12785 /**
12786  * @class Roo.data.HttpProxy
12787  * @extends Roo.data.DataProxy
12788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12789  * configured to reference a certain URL.<br><br>
12790  * <p>
12791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12792  * from which the running page was served.<br><br>
12793  * <p>
12794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12795  * <p>
12796  * Be aware that to enable the browser to parse an XML document, the server must set
12797  * the Content-Type header in the HTTP response to "text/xml".
12798  * @constructor
12799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12801  * will be used to make the request.
12802  */
12803 Roo.data.HttpProxy = function(conn){
12804     Roo.data.HttpProxy.superclass.constructor.call(this);
12805     // is conn a conn config or a real conn?
12806     this.conn = conn;
12807     this.useAjax = !conn || !conn.events;
12808   
12809 };
12810
12811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12812     // thse are take from connection...
12813     
12814     /**
12815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12816      */
12817     /**
12818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12819      * extra parameters to each request made by this object. (defaults to undefined)
12820      */
12821     /**
12822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12823      *  to each request made by this object. (defaults to undefined)
12824      */
12825     /**
12826      * @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)
12827      */
12828     /**
12829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12830      */
12831      /**
12832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12833      * @type Boolean
12834      */
12835   
12836
12837     /**
12838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12839      * @type Boolean
12840      */
12841     /**
12842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12844      * a finer-grained basis than the DataProxy events.
12845      */
12846     getConnection : function(){
12847         return this.useAjax ? Roo.Ajax : this.conn;
12848     },
12849
12850     /**
12851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12853      * process that block using the passed callback.
12854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12855      * for the request to the remote server.
12856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12857      * object into a block of Roo.data.Records.
12858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12859      * The function must be passed <ul>
12860      * <li>The Record block object</li>
12861      * <li>The "arg" argument from the load function</li>
12862      * <li>A boolean success indicator</li>
12863      * </ul>
12864      * @param {Object} scope The scope in which to call the callback
12865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12866      */
12867     load : function(params, reader, callback, scope, arg){
12868         if(this.fireEvent("beforeload", this, params) !== false){
12869             var  o = {
12870                 params : params || {},
12871                 request: {
12872                     callback : callback,
12873                     scope : scope,
12874                     arg : arg
12875                 },
12876                 reader: reader,
12877                 callback : this.loadResponse,
12878                 scope: this
12879             };
12880             if(this.useAjax){
12881                 Roo.applyIf(o, this.conn);
12882                 if(this.activeRequest){
12883                     Roo.Ajax.abort(this.activeRequest);
12884                 }
12885                 this.activeRequest = Roo.Ajax.request(o);
12886             }else{
12887                 this.conn.request(o);
12888             }
12889         }else{
12890             callback.call(scope||this, null, arg, false);
12891         }
12892     },
12893
12894     // private
12895     loadResponse : function(o, success, response){
12896         delete this.activeRequest;
12897         if(!success){
12898             this.fireEvent("loadexception", this, o, response);
12899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12900             return;
12901         }
12902         var result;
12903         try {
12904             result = o.reader.read(response);
12905         }catch(e){
12906             this.fireEvent("loadexception", this, o, response, e);
12907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12908             return;
12909         }
12910         
12911         this.fireEvent("load", this, o, o.request.arg);
12912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12913     },
12914
12915     // private
12916     update : function(dataSet){
12917
12918     },
12919
12920     // private
12921     updateResponse : function(dataSet){
12922
12923     }
12924 });/*
12925  * Based on:
12926  * Ext JS Library 1.1.1
12927  * Copyright(c) 2006-2007, Ext JS, LLC.
12928  *
12929  * Originally Released Under LGPL - original licence link has changed is not relivant.
12930  *
12931  * Fork - LGPL
12932  * <script type="text/javascript">
12933  */
12934
12935 /**
12936  * @class Roo.data.ScriptTagProxy
12937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12938  * other than the originating domain of the running page.<br><br>
12939  * <p>
12940  * <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
12941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12942  * <p>
12943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12944  * source code that is used as the source inside a &lt;script> tag.<br><br>
12945  * <p>
12946  * In order for the browser to process the returned data, the server must wrap the data object
12947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12949  * depending on whether the callback name was passed:
12950  * <p>
12951  * <pre><code>
12952 boolean scriptTag = false;
12953 String cb = request.getParameter("callback");
12954 if (cb != null) {
12955     scriptTag = true;
12956     response.setContentType("text/javascript");
12957 } else {
12958     response.setContentType("application/x-json");
12959 }
12960 Writer out = response.getWriter();
12961 if (scriptTag) {
12962     out.write(cb + "(");
12963 }
12964 out.print(dataBlock.toJsonString());
12965 if (scriptTag) {
12966     out.write(");");
12967 }
12968 </pre></code>
12969  *
12970  * @constructor
12971  * @param {Object} config A configuration object.
12972  */
12973 Roo.data.ScriptTagProxy = function(config){
12974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12975     Roo.apply(this, config);
12976     this.head = document.getElementsByTagName("head")[0];
12977 };
12978
12979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12980
12981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12982     /**
12983      * @cfg {String} url The URL from which to request the data object.
12984      */
12985     /**
12986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12987      */
12988     timeout : 30000,
12989     /**
12990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12991      * the server the name of the callback function set up by the load call to process the returned data object.
12992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12993      * javascript output which calls this named function passing the data object as its only parameter.
12994      */
12995     callbackParam : "callback",
12996     /**
12997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12998      * name to the request.
12999      */
13000     nocache : true,
13001
13002     /**
13003      * Load data from the configured URL, read the data object into
13004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13005      * process that block using the passed callback.
13006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13007      * for the request to the remote server.
13008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13009      * object into a block of Roo.data.Records.
13010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13011      * The function must be passed <ul>
13012      * <li>The Record block object</li>
13013      * <li>The "arg" argument from the load function</li>
13014      * <li>A boolean success indicator</li>
13015      * </ul>
13016      * @param {Object} scope The scope in which to call the callback
13017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13018      */
13019     load : function(params, reader, callback, scope, arg){
13020         if(this.fireEvent("beforeload", this, params) !== false){
13021
13022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13023
13024             var url = this.url;
13025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13026             if(this.nocache){
13027                 url += "&_dc=" + (new Date().getTime());
13028             }
13029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13030             var trans = {
13031                 id : transId,
13032                 cb : "stcCallback"+transId,
13033                 scriptId : "stcScript"+transId,
13034                 params : params,
13035                 arg : arg,
13036                 url : url,
13037                 callback : callback,
13038                 scope : scope,
13039                 reader : reader
13040             };
13041             var conn = this;
13042
13043             window[trans.cb] = function(o){
13044                 conn.handleResponse(o, trans);
13045             };
13046
13047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13048
13049             if(this.autoAbort !== false){
13050                 this.abort();
13051             }
13052
13053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13054
13055             var script = document.createElement("script");
13056             script.setAttribute("src", url);
13057             script.setAttribute("type", "text/javascript");
13058             script.setAttribute("id", trans.scriptId);
13059             this.head.appendChild(script);
13060
13061             this.trans = trans;
13062         }else{
13063             callback.call(scope||this, null, arg, false);
13064         }
13065     },
13066
13067     // private
13068     isLoading : function(){
13069         return this.trans ? true : false;
13070     },
13071
13072     /**
13073      * Abort the current server request.
13074      */
13075     abort : function(){
13076         if(this.isLoading()){
13077             this.destroyTrans(this.trans);
13078         }
13079     },
13080
13081     // private
13082     destroyTrans : function(trans, isLoaded){
13083         this.head.removeChild(document.getElementById(trans.scriptId));
13084         clearTimeout(trans.timeoutId);
13085         if(isLoaded){
13086             window[trans.cb] = undefined;
13087             try{
13088                 delete window[trans.cb];
13089             }catch(e){}
13090         }else{
13091             // if hasn't been loaded, wait for load to remove it to prevent script error
13092             window[trans.cb] = function(){
13093                 window[trans.cb] = undefined;
13094                 try{
13095                     delete window[trans.cb];
13096                 }catch(e){}
13097             };
13098         }
13099     },
13100
13101     // private
13102     handleResponse : function(o, trans){
13103         this.trans = false;
13104         this.destroyTrans(trans, true);
13105         var result;
13106         try {
13107             result = trans.reader.readRecords(o);
13108         }catch(e){
13109             this.fireEvent("loadexception", this, o, trans.arg, e);
13110             trans.callback.call(trans.scope||window, null, trans.arg, false);
13111             return;
13112         }
13113         this.fireEvent("load", this, o, trans.arg);
13114         trans.callback.call(trans.scope||window, result, trans.arg, true);
13115     },
13116
13117     // private
13118     handleFailure : function(trans){
13119         this.trans = false;
13120         this.destroyTrans(trans, false);
13121         this.fireEvent("loadexception", this, null, trans.arg);
13122         trans.callback.call(trans.scope||window, null, trans.arg, false);
13123     }
13124 });/*
13125  * Based on:
13126  * Ext JS Library 1.1.1
13127  * Copyright(c) 2006-2007, Ext JS, LLC.
13128  *
13129  * Originally Released Under LGPL - original licence link has changed is not relivant.
13130  *
13131  * Fork - LGPL
13132  * <script type="text/javascript">
13133  */
13134
13135 /**
13136  * @class Roo.data.JsonReader
13137  * @extends Roo.data.DataReader
13138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13139  * based on mappings in a provided Roo.data.Record constructor.
13140  * 
13141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13142  * in the reply previously. 
13143  * 
13144  * <p>
13145  * Example code:
13146  * <pre><code>
13147 var RecordDef = Roo.data.Record.create([
13148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13150 ]);
13151 var myReader = new Roo.data.JsonReader({
13152     totalProperty: "results",    // The property which contains the total dataset size (optional)
13153     root: "rows",                // The property which contains an Array of row objects
13154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13155 }, RecordDef);
13156 </code></pre>
13157  * <p>
13158  * This would consume a JSON file like this:
13159  * <pre><code>
13160 { 'results': 2, 'rows': [
13161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13163 }
13164 </code></pre>
13165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13167  * paged from the remote server.
13168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13169  * @cfg {String} root name of the property which contains the Array of row objects.
13170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13171  * @cfg {Array} fields Array of field definition objects
13172  * @constructor
13173  * Create a new JsonReader
13174  * @param {Object} meta Metadata configuration options
13175  * @param {Object} recordType Either an Array of field definition objects,
13176  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13177  */
13178 Roo.data.JsonReader = function(meta, recordType){
13179     
13180     meta = meta || {};
13181     // set some defaults:
13182     Roo.applyIf(meta, {
13183         totalProperty: 'total',
13184         successProperty : 'success',
13185         root : 'data',
13186         id : 'id'
13187     });
13188     
13189     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13190 };
13191 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13192     
13193     readerType : 'Json',
13194     
13195     /**
13196      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13197      * Used by Store query builder to append _requestMeta to params.
13198      * 
13199      */
13200     metaFromRemote : false,
13201     /**
13202      * This method is only used by a DataProxy which has retrieved data from a remote server.
13203      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13204      * @return {Object} data A data block which is used by an Roo.data.Store object as
13205      * a cache of Roo.data.Records.
13206      */
13207     read : function(response){
13208         var json = response.responseText;
13209        
13210         var o = /* eval:var:o */ eval("("+json+")");
13211         if(!o) {
13212             throw {message: "JsonReader.read: Json object not found"};
13213         }
13214         
13215         if(o.metaData){
13216             
13217             delete this.ef;
13218             this.metaFromRemote = true;
13219             this.meta = o.metaData;
13220             this.recordType = Roo.data.Record.create(o.metaData.fields);
13221             this.onMetaChange(this.meta, this.recordType, o);
13222         }
13223         return this.readRecords(o);
13224     },
13225
13226     // private function a store will implement
13227     onMetaChange : function(meta, recordType, o){
13228
13229     },
13230
13231     /**
13232          * @ignore
13233          */
13234     simpleAccess: function(obj, subsc) {
13235         return obj[subsc];
13236     },
13237
13238         /**
13239          * @ignore
13240          */
13241     getJsonAccessor: function(){
13242         var re = /[\[\.]/;
13243         return function(expr) {
13244             try {
13245                 return(re.test(expr))
13246                     ? new Function("obj", "return obj." + expr)
13247                     : function(obj){
13248                         return obj[expr];
13249                     };
13250             } catch(e){}
13251             return Roo.emptyFn;
13252         };
13253     }(),
13254
13255     /**
13256      * Create a data block containing Roo.data.Records from an XML document.
13257      * @param {Object} o An object which contains an Array of row objects in the property specified
13258      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13259      * which contains the total size of the dataset.
13260      * @return {Object} data A data block which is used by an Roo.data.Store object as
13261      * a cache of Roo.data.Records.
13262      */
13263     readRecords : function(o){
13264         /**
13265          * After any data loads, the raw JSON data is available for further custom processing.
13266          * @type Object
13267          */
13268         this.o = o;
13269         var s = this.meta, Record = this.recordType,
13270             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13271
13272 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13273         if (!this.ef) {
13274             if(s.totalProperty) {
13275                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13276                 }
13277                 if(s.successProperty) {
13278                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13279                 }
13280                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13281                 if (s.id) {
13282                         var g = this.getJsonAccessor(s.id);
13283                         this.getId = function(rec) {
13284                                 var r = g(rec);  
13285                                 return (r === undefined || r === "") ? null : r;
13286                         };
13287                 } else {
13288                         this.getId = function(){return null;};
13289                 }
13290             this.ef = [];
13291             for(var jj = 0; jj < fl; jj++){
13292                 f = fi[jj];
13293                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13294                 this.ef[jj] = this.getJsonAccessor(map);
13295             }
13296         }
13297
13298         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13299         if(s.totalProperty){
13300             var vt = parseInt(this.getTotal(o), 10);
13301             if(!isNaN(vt)){
13302                 totalRecords = vt;
13303             }
13304         }
13305         if(s.successProperty){
13306             var vs = this.getSuccess(o);
13307             if(vs === false || vs === 'false'){
13308                 success = false;
13309             }
13310         }
13311         var records = [];
13312         for(var i = 0; i < c; i++){
13313                 var n = root[i];
13314             var values = {};
13315             var id = this.getId(n);
13316             for(var j = 0; j < fl; j++){
13317                 f = fi[j];
13318             var v = this.ef[j](n);
13319             if (!f.convert) {
13320                 Roo.log('missing convert for ' + f.name);
13321                 Roo.log(f);
13322                 continue;
13323             }
13324             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13325             }
13326             var record = new Record(values, id);
13327             record.json = n;
13328             records[i] = record;
13329         }
13330         return {
13331             raw : o,
13332             success : success,
13333             records : records,
13334             totalRecords : totalRecords
13335         };
13336     },
13337     // used when loading children.. @see loadDataFromChildren
13338     toLoadData: function(rec)
13339     {
13340         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13341         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13342         return { data : data, total : data.length };
13343         
13344     }
13345 });/*
13346  * Based on:
13347  * Ext JS Library 1.1.1
13348  * Copyright(c) 2006-2007, Ext JS, LLC.
13349  *
13350  * Originally Released Under LGPL - original licence link has changed is not relivant.
13351  *
13352  * Fork - LGPL
13353  * <script type="text/javascript">
13354  */
13355
13356 /**
13357  * @class Roo.data.ArrayReader
13358  * @extends Roo.data.DataReader
13359  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13360  * Each element of that Array represents a row of data fields. The
13361  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13362  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13363  * <p>
13364  * Example code:.
13365  * <pre><code>
13366 var RecordDef = Roo.data.Record.create([
13367     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13368     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13369 ]);
13370 var myReader = new Roo.data.ArrayReader({
13371     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13372 }, RecordDef);
13373 </code></pre>
13374  * <p>
13375  * This would consume an Array like this:
13376  * <pre><code>
13377 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13378   </code></pre>
13379  
13380  * @constructor
13381  * Create a new JsonReader
13382  * @param {Object} meta Metadata configuration options.
13383  * @param {Object|Array} recordType Either an Array of field definition objects
13384  * 
13385  * @cfg {Array} fields Array of field definition objects
13386  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13387  * as specified to {@link Roo.data.Record#create},
13388  * or an {@link Roo.data.Record} object
13389  *
13390  * 
13391  * created using {@link Roo.data.Record#create}.
13392  */
13393 Roo.data.ArrayReader = function(meta, recordType)
13394 {    
13395     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13396 };
13397
13398 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13399     
13400       /**
13401      * Create a data block containing Roo.data.Records from an XML document.
13402      * @param {Object} o An Array of row objects which represents the dataset.
13403      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13404      * a cache of Roo.data.Records.
13405      */
13406     readRecords : function(o)
13407     {
13408         var sid = this.meta ? this.meta.id : null;
13409         var recordType = this.recordType, fields = recordType.prototype.fields;
13410         var records = [];
13411         var root = o;
13412         for(var i = 0; i < root.length; i++){
13413                 var n = root[i];
13414             var values = {};
13415             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13416             for(var j = 0, jlen = fields.length; j < jlen; j++){
13417                 var f = fields.items[j];
13418                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13419                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13420                 v = f.convert(v);
13421                 values[f.name] = v;
13422             }
13423             var record = new recordType(values, id);
13424             record.json = n;
13425             records[records.length] = record;
13426         }
13427         return {
13428             records : records,
13429             totalRecords : records.length
13430         };
13431     },
13432     // used when loading children.. @see loadDataFromChildren
13433     toLoadData: function(rec)
13434     {
13435         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13436         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13437         
13438     }
13439     
13440     
13441 });/*
13442  * - LGPL
13443  * * 
13444  */
13445
13446 /**
13447  * @class Roo.bootstrap.ComboBox
13448  * @extends Roo.bootstrap.TriggerField
13449  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13450  * @cfg {Boolean} append (true|false) default false
13451  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13452  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13453  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13454  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13455  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13456  * @cfg {Boolean} animate default true
13457  * @cfg {Boolean} emptyResultText only for touch device
13458  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13459  * @cfg {String} emptyTitle default ''
13460  * @constructor
13461  * Create a new ComboBox.
13462  * @param {Object} config Configuration options
13463  */
13464 Roo.bootstrap.ComboBox = function(config){
13465     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13466     this.addEvents({
13467         /**
13468          * @event expand
13469          * Fires when the dropdown list is expanded
13470         * @param {Roo.bootstrap.ComboBox} combo This combo box
13471         */
13472         'expand' : true,
13473         /**
13474          * @event collapse
13475          * Fires when the dropdown list is collapsed
13476         * @param {Roo.bootstrap.ComboBox} combo This combo box
13477         */
13478         'collapse' : true,
13479         /**
13480          * @event beforeselect
13481          * Fires before a list item is selected. Return false to cancel the selection.
13482         * @param {Roo.bootstrap.ComboBox} combo This combo box
13483         * @param {Roo.data.Record} record The data record returned from the underlying store
13484         * @param {Number} index The index of the selected item in the dropdown list
13485         */
13486         'beforeselect' : true,
13487         /**
13488          * @event select
13489          * Fires when a list item is selected
13490         * @param {Roo.bootstrap.ComboBox} combo This combo box
13491         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13492         * @param {Number} index The index of the selected item in the dropdown list
13493         */
13494         'select' : true,
13495         /**
13496          * @event beforequery
13497          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13498          * The event object passed has these properties:
13499         * @param {Roo.bootstrap.ComboBox} combo This combo box
13500         * @param {String} query The query
13501         * @param {Boolean} forceAll true to force "all" query
13502         * @param {Boolean} cancel true to cancel the query
13503         * @param {Object} e The query event object
13504         */
13505         'beforequery': true,
13506          /**
13507          * @event add
13508          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13509         * @param {Roo.bootstrap.ComboBox} combo This combo box
13510         */
13511         'add' : true,
13512         /**
13513          * @event edit
13514          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13515         * @param {Roo.bootstrap.ComboBox} combo This combo box
13516         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13517         */
13518         'edit' : true,
13519         /**
13520          * @event remove
13521          * Fires when the remove value from the combobox array
13522         * @param {Roo.bootstrap.ComboBox} combo This combo box
13523         */
13524         'remove' : true,
13525         /**
13526          * @event afterremove
13527          * Fires when the remove value from the combobox array
13528         * @param {Roo.bootstrap.ComboBox} combo This combo box
13529         */
13530         'afterremove' : true,
13531         /**
13532          * @event specialfilter
13533          * Fires when specialfilter
13534             * @param {Roo.bootstrap.ComboBox} combo This combo box
13535             */
13536         'specialfilter' : true,
13537         /**
13538          * @event tick
13539          * Fires when tick the element
13540             * @param {Roo.bootstrap.ComboBox} combo This combo box
13541             */
13542         'tick' : true,
13543         /**
13544          * @event touchviewdisplay
13545          * Fires when touch view require special display (default is using displayField)
13546             * @param {Roo.bootstrap.ComboBox} combo This combo box
13547             * @param {Object} cfg set html .
13548             */
13549         'touchviewdisplay' : true
13550         
13551     });
13552     
13553     this.item = [];
13554     this.tickItems = [];
13555     
13556     this.selectedIndex = -1;
13557     if(this.mode == 'local'){
13558         if(config.queryDelay === undefined){
13559             this.queryDelay = 10;
13560         }
13561         if(config.minChars === undefined){
13562             this.minChars = 0;
13563         }
13564     }
13565 };
13566
13567 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13568      
13569     /**
13570      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13571      * rendering into an Roo.Editor, defaults to false)
13572      */
13573     /**
13574      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13575      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13576      */
13577     /**
13578      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13579      */
13580     /**
13581      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13582      * the dropdown list (defaults to undefined, with no header element)
13583      */
13584
13585      /**
13586      * @cfg {String/Roo.Template} tpl The template to use to render the output
13587      */
13588      
13589      /**
13590      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13591      */
13592     listWidth: undefined,
13593     /**
13594      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13595      * mode = 'remote' or 'text' if mode = 'local')
13596      */
13597     displayField: undefined,
13598     
13599     /**
13600      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13601      * mode = 'remote' or 'value' if mode = 'local'). 
13602      * Note: use of a valueField requires the user make a selection
13603      * in order for a value to be mapped.
13604      */
13605     valueField: undefined,
13606     /**
13607      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13608      */
13609     modalTitle : '',
13610     
13611     /**
13612      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13613      * field's data value (defaults to the underlying DOM element's name)
13614      */
13615     hiddenName: undefined,
13616     /**
13617      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13618      */
13619     listClass: '',
13620     /**
13621      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13622      */
13623     selectedClass: 'active',
13624     
13625     /**
13626      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13627      */
13628     shadow:'sides',
13629     /**
13630      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13631      * anchor positions (defaults to 'tl-bl')
13632      */
13633     listAlign: 'tl-bl?',
13634     /**
13635      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13636      */
13637     maxHeight: 300,
13638     /**
13639      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13640      * query specified by the allQuery config option (defaults to 'query')
13641      */
13642     triggerAction: 'query',
13643     /**
13644      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13645      * (defaults to 4, does not apply if editable = false)
13646      */
13647     minChars : 4,
13648     /**
13649      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13650      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13651      */
13652     typeAhead: false,
13653     /**
13654      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13655      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13656      */
13657     queryDelay: 500,
13658     /**
13659      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13660      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13661      */
13662     pageSize: 0,
13663     /**
13664      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13665      * when editable = true (defaults to false)
13666      */
13667     selectOnFocus:false,
13668     /**
13669      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13670      */
13671     queryParam: 'query',
13672     /**
13673      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13674      * when mode = 'remote' (defaults to 'Loading...')
13675      */
13676     loadingText: 'Loading...',
13677     /**
13678      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13679      */
13680     resizable: false,
13681     /**
13682      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13683      */
13684     handleHeight : 8,
13685     /**
13686      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13687      * traditional select (defaults to true)
13688      */
13689     editable: true,
13690     /**
13691      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13692      */
13693     allQuery: '',
13694     /**
13695      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13696      */
13697     mode: 'remote',
13698     /**
13699      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13700      * listWidth has a higher value)
13701      */
13702     minListWidth : 70,
13703     /**
13704      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13705      * allow the user to set arbitrary text into the field (defaults to false)
13706      */
13707     forceSelection:false,
13708     /**
13709      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13710      * if typeAhead = true (defaults to 250)
13711      */
13712     typeAheadDelay : 250,
13713     /**
13714      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13715      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13716      */
13717     valueNotFoundText : undefined,
13718     /**
13719      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13720      */
13721     blockFocus : false,
13722     
13723     /**
13724      * @cfg {Boolean} disableClear Disable showing of clear button.
13725      */
13726     disableClear : false,
13727     /**
13728      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13729      */
13730     alwaysQuery : false,
13731     
13732     /**
13733      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13734      */
13735     multiple : false,
13736     
13737     /**
13738      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13739      */
13740     invalidClass : "has-warning",
13741     
13742     /**
13743      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13744      */
13745     validClass : "has-success",
13746     
13747     /**
13748      * @cfg {Boolean} specialFilter (true|false) special filter default false
13749      */
13750     specialFilter : false,
13751     
13752     /**
13753      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13754      */
13755     mobileTouchView : true,
13756     
13757     /**
13758      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13759      */
13760     useNativeIOS : false,
13761     
13762     /**
13763      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13764      */
13765     mobile_restrict_height : false,
13766     
13767     ios_options : false,
13768     
13769     //private
13770     addicon : false,
13771     editicon: false,
13772     
13773     page: 0,
13774     hasQuery: false,
13775     append: false,
13776     loadNext: false,
13777     autoFocus : true,
13778     tickable : false,
13779     btnPosition : 'right',
13780     triggerList : true,
13781     showToggleBtn : true,
13782     animate : true,
13783     emptyResultText: 'Empty',
13784     triggerText : 'Select',
13785     emptyTitle : '',
13786     
13787     // element that contains real text value.. (when hidden is used..)
13788     
13789     getAutoCreate : function()
13790     {   
13791         var cfg = false;
13792         //render
13793         /*
13794          * Render classic select for iso
13795          */
13796         
13797         if(Roo.isIOS && this.useNativeIOS){
13798             cfg = this.getAutoCreateNativeIOS();
13799             return cfg;
13800         }
13801         
13802         /*
13803          * Touch Devices
13804          */
13805         
13806         if(Roo.isTouch && this.mobileTouchView){
13807             cfg = this.getAutoCreateTouchView();
13808             return cfg;;
13809         }
13810         
13811         /*
13812          *  Normal ComboBox
13813          */
13814         if(!this.tickable){
13815             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13816             return cfg;
13817         }
13818         
13819         /*
13820          *  ComboBox with tickable selections
13821          */
13822              
13823         var align = this.labelAlign || this.parentLabelAlign();
13824         
13825         cfg = {
13826             cls : 'form-group roo-combobox-tickable' //input-group
13827         };
13828         
13829         var btn_text_select = '';
13830         var btn_text_done = '';
13831         var btn_text_cancel = '';
13832         
13833         if (this.btn_text_show) {
13834             btn_text_select = 'Select';
13835             btn_text_done = 'Done';
13836             btn_text_cancel = 'Cancel'; 
13837         }
13838         
13839         var buttons = {
13840             tag : 'div',
13841             cls : 'tickable-buttons',
13842             cn : [
13843                 {
13844                     tag : 'button',
13845                     type : 'button',
13846                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13847                     //html : this.triggerText
13848                     html: btn_text_select
13849                 },
13850                 {
13851                     tag : 'button',
13852                     type : 'button',
13853                     name : 'ok',
13854                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13855                     //html : 'Done'
13856                     html: btn_text_done
13857                 },
13858                 {
13859                     tag : 'button',
13860                     type : 'button',
13861                     name : 'cancel',
13862                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13863                     //html : 'Cancel'
13864                     html: btn_text_cancel
13865                 }
13866             ]
13867         };
13868         
13869         if(this.editable){
13870             buttons.cn.unshift({
13871                 tag: 'input',
13872                 cls: 'roo-select2-search-field-input'
13873             });
13874         }
13875         
13876         var _this = this;
13877         
13878         Roo.each(buttons.cn, function(c){
13879             if (_this.size) {
13880                 c.cls += ' btn-' + _this.size;
13881             }
13882
13883             if (_this.disabled) {
13884                 c.disabled = true;
13885             }
13886         });
13887         
13888         var box = {
13889             tag: 'div',
13890             style : 'display: contents',
13891             cn: [
13892                 {
13893                     tag: 'input',
13894                     type : 'hidden',
13895                     cls: 'form-hidden-field'
13896                 },
13897                 {
13898                     tag: 'ul',
13899                     cls: 'roo-select2-choices',
13900                     cn:[
13901                         {
13902                             tag: 'li',
13903                             cls: 'roo-select2-search-field',
13904                             cn: [
13905                                 buttons
13906                             ]
13907                         }
13908                     ]
13909                 }
13910             ]
13911         };
13912         
13913         var combobox = {
13914             cls: 'roo-select2-container input-group roo-select2-container-multi',
13915             cn: [
13916                 
13917                 box
13918 //                {
13919 //                    tag: 'ul',
13920 //                    cls: 'typeahead typeahead-long dropdown-menu',
13921 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13922 //                }
13923             ]
13924         };
13925         
13926         if(this.hasFeedback && !this.allowBlank){
13927             
13928             var feedback = {
13929                 tag: 'span',
13930                 cls: 'glyphicon form-control-feedback'
13931             };
13932
13933             combobox.cn.push(feedback);
13934         }
13935         
13936         var indicator = {
13937             tag : 'i',
13938             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13939             tooltip : 'This field is required'
13940         };
13941         if (Roo.bootstrap.version == 4) {
13942             indicator = {
13943                 tag : 'i',
13944                 style : 'display:none'
13945             };
13946         }
13947         if (align ==='left' && this.fieldLabel.length) {
13948             
13949             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13950             
13951             cfg.cn = [
13952                 indicator,
13953                 {
13954                     tag: 'label',
13955                     'for' :  id,
13956                     cls : 'control-label col-form-label',
13957                     html : this.fieldLabel
13958
13959                 },
13960                 {
13961                     cls : "", 
13962                     cn: [
13963                         combobox
13964                     ]
13965                 }
13966
13967             ];
13968             
13969             var labelCfg = cfg.cn[1];
13970             var contentCfg = cfg.cn[2];
13971             
13972
13973             if(this.indicatorpos == 'right'){
13974                 
13975                 cfg.cn = [
13976                     {
13977                         tag: 'label',
13978                         'for' :  id,
13979                         cls : 'control-label col-form-label',
13980                         cn : [
13981                             {
13982                                 tag : 'span',
13983                                 html : this.fieldLabel
13984                             },
13985                             indicator
13986                         ]
13987                     },
13988                     {
13989                         cls : "",
13990                         cn: [
13991                             combobox
13992                         ]
13993                     }
13994
13995                 ];
13996                 
13997                 
13998                 
13999                 labelCfg = cfg.cn[0];
14000                 contentCfg = cfg.cn[1];
14001             
14002             }
14003             
14004             if(this.labelWidth > 12){
14005                 labelCfg.style = "width: " + this.labelWidth + 'px';
14006             }
14007             
14008             if(this.labelWidth < 13 && this.labelmd == 0){
14009                 this.labelmd = this.labelWidth;
14010             }
14011             
14012             if(this.labellg > 0){
14013                 labelCfg.cls += ' col-lg-' + this.labellg;
14014                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14015             }
14016             
14017             if(this.labelmd > 0){
14018                 labelCfg.cls += ' col-md-' + this.labelmd;
14019                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14020             }
14021             
14022             if(this.labelsm > 0){
14023                 labelCfg.cls += ' col-sm-' + this.labelsm;
14024                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14025             }
14026             
14027             if(this.labelxs > 0){
14028                 labelCfg.cls += ' col-xs-' + this.labelxs;
14029                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14030             }
14031                 
14032                 
14033         } else if ( this.fieldLabel.length) {
14034 //                Roo.log(" label");
14035                  cfg.cn = [
14036                    indicator,
14037                     {
14038                         tag: 'label',
14039                         //cls : 'input-group-addon',
14040                         html : this.fieldLabel
14041                     },
14042                     combobox
14043                 ];
14044                 
14045                 if(this.indicatorpos == 'right'){
14046                     cfg.cn = [
14047                         {
14048                             tag: 'label',
14049                             //cls : 'input-group-addon',
14050                             html : this.fieldLabel
14051                         },
14052                         indicator,
14053                         combobox
14054                     ];
14055                     
14056                 }
14057
14058         } else {
14059             
14060 //                Roo.log(" no label && no align");
14061                 cfg = combobox
14062                      
14063                 
14064         }
14065          
14066         var settings=this;
14067         ['xs','sm','md','lg'].map(function(size){
14068             if (settings[size]) {
14069                 cfg.cls += ' col-' + size + '-' + settings[size];
14070             }
14071         });
14072         
14073         return cfg;
14074         
14075     },
14076     
14077     _initEventsCalled : false,
14078     
14079     // private
14080     initEvents: function()
14081     {   
14082         if (this._initEventsCalled) { // as we call render... prevent looping...
14083             return;
14084         }
14085         this._initEventsCalled = true;
14086         
14087         if (!this.store) {
14088             throw "can not find store for combo";
14089         }
14090         
14091         this.indicator = this.indicatorEl();
14092         
14093         this.store = Roo.factory(this.store, Roo.data);
14094         this.store.parent = this;
14095         
14096         // if we are building from html. then this element is so complex, that we can not really
14097         // use the rendered HTML.
14098         // so we have to trash and replace the previous code.
14099         if (Roo.XComponent.build_from_html) {
14100             // remove this element....
14101             var e = this.el.dom, k=0;
14102             while (e ) { e = e.previousSibling;  ++k;}
14103
14104             this.el.remove();
14105             
14106             this.el=false;
14107             this.rendered = false;
14108             
14109             this.render(this.parent().getChildContainer(true), k);
14110         }
14111         
14112         if(Roo.isIOS && this.useNativeIOS){
14113             this.initIOSView();
14114             return;
14115         }
14116         
14117         /*
14118          * Touch Devices
14119          */
14120         
14121         if(Roo.isTouch && this.mobileTouchView){
14122             this.initTouchView();
14123             return;
14124         }
14125         
14126         if(this.tickable){
14127             this.initTickableEvents();
14128             return;
14129         }
14130         
14131         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14132         
14133         if(this.hiddenName){
14134             
14135             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14136             
14137             this.hiddenField.dom.value =
14138                 this.hiddenValue !== undefined ? this.hiddenValue :
14139                 this.value !== undefined ? this.value : '';
14140
14141             // prevent input submission
14142             this.el.dom.removeAttribute('name');
14143             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14144              
14145              
14146         }
14147         //if(Roo.isGecko){
14148         //    this.el.dom.setAttribute('autocomplete', 'off');
14149         //}
14150         
14151         var cls = 'x-combo-list';
14152         
14153         //this.list = new Roo.Layer({
14154         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14155         //});
14156         
14157         var _this = this;
14158         
14159         (function(){
14160             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14161             _this.list.setWidth(lw);
14162         }).defer(100);
14163         
14164         this.list.on('mouseover', this.onViewOver, this);
14165         this.list.on('mousemove', this.onViewMove, this);
14166         this.list.on('scroll', this.onViewScroll, this);
14167         
14168         /*
14169         this.list.swallowEvent('mousewheel');
14170         this.assetHeight = 0;
14171
14172         if(this.title){
14173             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14174             this.assetHeight += this.header.getHeight();
14175         }
14176
14177         this.innerList = this.list.createChild({cls:cls+'-inner'});
14178         this.innerList.on('mouseover', this.onViewOver, this);
14179         this.innerList.on('mousemove', this.onViewMove, this);
14180         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14181         
14182         if(this.allowBlank && !this.pageSize && !this.disableClear){
14183             this.footer = this.list.createChild({cls:cls+'-ft'});
14184             this.pageTb = new Roo.Toolbar(this.footer);
14185            
14186         }
14187         if(this.pageSize){
14188             this.footer = this.list.createChild({cls:cls+'-ft'});
14189             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14190                     {pageSize: this.pageSize});
14191             
14192         }
14193         
14194         if (this.pageTb && this.allowBlank && !this.disableClear) {
14195             var _this = this;
14196             this.pageTb.add(new Roo.Toolbar.Fill(), {
14197                 cls: 'x-btn-icon x-btn-clear',
14198                 text: '&#160;',
14199                 handler: function()
14200                 {
14201                     _this.collapse();
14202                     _this.clearValue();
14203                     _this.onSelect(false, -1);
14204                 }
14205             });
14206         }
14207         if (this.footer) {
14208             this.assetHeight += this.footer.getHeight();
14209         }
14210         */
14211             
14212         if(!this.tpl){
14213             this.tpl = Roo.bootstrap.version == 4 ?
14214                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14215                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14216         }
14217
14218         this.view = new Roo.View(this.list, this.tpl, {
14219             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14220         });
14221         //this.view.wrapEl.setDisplayed(false);
14222         this.view.on('click', this.onViewClick, this);
14223         
14224         
14225         this.store.on('beforeload', this.onBeforeLoad, this);
14226         this.store.on('load', this.onLoad, this);
14227         this.store.on('loadexception', this.onLoadException, this);
14228         /*
14229         if(this.resizable){
14230             this.resizer = new Roo.Resizable(this.list,  {
14231                pinned:true, handles:'se'
14232             });
14233             this.resizer.on('resize', function(r, w, h){
14234                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14235                 this.listWidth = w;
14236                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14237                 this.restrictHeight();
14238             }, this);
14239             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14240         }
14241         */
14242         if(!this.editable){
14243             this.editable = true;
14244             this.setEditable(false);
14245         }
14246         
14247         /*
14248         
14249         if (typeof(this.events.add.listeners) != 'undefined') {
14250             
14251             this.addicon = this.wrap.createChild(
14252                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14253        
14254             this.addicon.on('click', function(e) {
14255                 this.fireEvent('add', this);
14256             }, this);
14257         }
14258         if (typeof(this.events.edit.listeners) != 'undefined') {
14259             
14260             this.editicon = this.wrap.createChild(
14261                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14262             if (this.addicon) {
14263                 this.editicon.setStyle('margin-left', '40px');
14264             }
14265             this.editicon.on('click', function(e) {
14266                 
14267                 // we fire even  if inothing is selected..
14268                 this.fireEvent('edit', this, this.lastData );
14269                 
14270             }, this);
14271         }
14272         */
14273         
14274         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14275             "up" : function(e){
14276                 this.inKeyMode = true;
14277                 this.selectPrev();
14278             },
14279
14280             "down" : function(e){
14281                 if(!this.isExpanded()){
14282                     this.onTriggerClick();
14283                 }else{
14284                     this.inKeyMode = true;
14285                     this.selectNext();
14286                 }
14287             },
14288
14289             "enter" : function(e){
14290 //                this.onViewClick();
14291                 //return true;
14292                 this.collapse();
14293                 
14294                 if(this.fireEvent("specialkey", this, e)){
14295                     this.onViewClick(false);
14296                 }
14297                 
14298                 return true;
14299             },
14300
14301             "esc" : function(e){
14302                 this.collapse();
14303             },
14304
14305             "tab" : function(e){
14306                 this.collapse();
14307                 
14308                 if(this.fireEvent("specialkey", this, e)){
14309                     this.onViewClick(false);
14310                 }
14311                 
14312                 return true;
14313             },
14314
14315             scope : this,
14316
14317             doRelay : function(foo, bar, hname){
14318                 if(hname == 'down' || this.scope.isExpanded()){
14319                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14320                 }
14321                 return true;
14322             },
14323
14324             forceKeyDown: true
14325         });
14326         
14327         
14328         this.queryDelay = Math.max(this.queryDelay || 10,
14329                 this.mode == 'local' ? 10 : 250);
14330         
14331         
14332         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14333         
14334         if(this.typeAhead){
14335             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14336         }
14337         if(this.editable !== false){
14338             this.inputEl().on("keyup", this.onKeyUp, this);
14339         }
14340         if(this.forceSelection){
14341             this.inputEl().on('blur', this.doForce, this);
14342         }
14343         
14344         if(this.multiple){
14345             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14346             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14347         }
14348     },
14349     
14350     initTickableEvents: function()
14351     {   
14352         this.createList();
14353         
14354         if(this.hiddenName){
14355             
14356             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14357             
14358             this.hiddenField.dom.value =
14359                 this.hiddenValue !== undefined ? this.hiddenValue :
14360                 this.value !== undefined ? this.value : '';
14361
14362             // prevent input submission
14363             this.el.dom.removeAttribute('name');
14364             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14365              
14366              
14367         }
14368         
14369 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14370         
14371         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14372         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14373         if(this.triggerList){
14374             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14375         }
14376          
14377         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14378         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14379         
14380         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14381         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14382         
14383         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14384         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14385         
14386         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14387         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14388         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14389         
14390         this.okBtn.hide();
14391         this.cancelBtn.hide();
14392         
14393         var _this = this;
14394         
14395         (function(){
14396             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14397             _this.list.setWidth(lw);
14398         }).defer(100);
14399         
14400         this.list.on('mouseover', this.onViewOver, this);
14401         this.list.on('mousemove', this.onViewMove, this);
14402         
14403         this.list.on('scroll', this.onViewScroll, this);
14404         
14405         if(!this.tpl){
14406             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14407                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14408         }
14409
14410         this.view = new Roo.View(this.list, this.tpl, {
14411             singleSelect:true,
14412             tickable:true,
14413             parent:this,
14414             store: this.store,
14415             selectedClass: this.selectedClass
14416         });
14417         
14418         //this.view.wrapEl.setDisplayed(false);
14419         this.view.on('click', this.onViewClick, this);
14420         
14421         
14422         
14423         this.store.on('beforeload', this.onBeforeLoad, this);
14424         this.store.on('load', this.onLoad, this);
14425         this.store.on('loadexception', this.onLoadException, this);
14426         
14427         if(this.editable){
14428             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14429                 "up" : function(e){
14430                     this.inKeyMode = true;
14431                     this.selectPrev();
14432                 },
14433
14434                 "down" : function(e){
14435                     this.inKeyMode = true;
14436                     this.selectNext();
14437                 },
14438
14439                 "enter" : function(e){
14440                     if(this.fireEvent("specialkey", this, e)){
14441                         this.onViewClick(false);
14442                     }
14443                     
14444                     return true;
14445                 },
14446
14447                 "esc" : function(e){
14448                     this.onTickableFooterButtonClick(e, false, false);
14449                 },
14450
14451                 "tab" : function(e){
14452                     this.fireEvent("specialkey", this, e);
14453                     
14454                     this.onTickableFooterButtonClick(e, false, false);
14455                     
14456                     return true;
14457                 },
14458
14459                 scope : this,
14460
14461                 doRelay : function(e, fn, key){
14462                     if(this.scope.isExpanded()){
14463                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14464                     }
14465                     return true;
14466                 },
14467
14468                 forceKeyDown: true
14469             });
14470         }
14471         
14472         this.queryDelay = Math.max(this.queryDelay || 10,
14473                 this.mode == 'local' ? 10 : 250);
14474         
14475         
14476         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14477         
14478         if(this.typeAhead){
14479             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14480         }
14481         
14482         if(this.editable !== false){
14483             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14484         }
14485         
14486         this.indicator = this.indicatorEl();
14487         
14488         if(this.indicator){
14489             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14490             this.indicator.hide();
14491         }
14492         
14493     },
14494
14495     onDestroy : function(){
14496         if(this.view){
14497             this.view.setStore(null);
14498             this.view.el.removeAllListeners();
14499             this.view.el.remove();
14500             this.view.purgeListeners();
14501         }
14502         if(this.list){
14503             this.list.dom.innerHTML  = '';
14504         }
14505         
14506         if(this.store){
14507             this.store.un('beforeload', this.onBeforeLoad, this);
14508             this.store.un('load', this.onLoad, this);
14509             this.store.un('loadexception', this.onLoadException, this);
14510         }
14511         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14512     },
14513
14514     // private
14515     fireKey : function(e){
14516         if(e.isNavKeyPress() && !this.list.isVisible()){
14517             this.fireEvent("specialkey", this, e);
14518         }
14519     },
14520
14521     // private
14522     onResize: function(w, h){
14523 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14524 //        
14525 //        if(typeof w != 'number'){
14526 //            // we do not handle it!?!?
14527 //            return;
14528 //        }
14529 //        var tw = this.trigger.getWidth();
14530 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14531 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14532 //        var x = w - tw;
14533 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14534 //            
14535 //        //this.trigger.setStyle('left', x+'px');
14536 //        
14537 //        if(this.list && this.listWidth === undefined){
14538 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14539 //            this.list.setWidth(lw);
14540 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14541 //        }
14542         
14543     
14544         
14545     },
14546
14547     /**
14548      * Allow or prevent the user from directly editing the field text.  If false is passed,
14549      * the user will only be able to select from the items defined in the dropdown list.  This method
14550      * is the runtime equivalent of setting the 'editable' config option at config time.
14551      * @param {Boolean} value True to allow the user to directly edit the field text
14552      */
14553     setEditable : function(value){
14554         if(value == this.editable){
14555             return;
14556         }
14557         this.editable = value;
14558         if(!value){
14559             this.inputEl().dom.setAttribute('readOnly', true);
14560             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14561             this.inputEl().addClass('x-combo-noedit');
14562         }else{
14563             this.inputEl().dom.setAttribute('readOnly', false);
14564             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14565             this.inputEl().removeClass('x-combo-noedit');
14566         }
14567     },
14568
14569     // private
14570     
14571     onBeforeLoad : function(combo,opts){
14572         if(!this.hasFocus){
14573             return;
14574         }
14575          if (!opts.add) {
14576             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14577          }
14578         this.restrictHeight();
14579         this.selectedIndex = -1;
14580     },
14581
14582     // private
14583     onLoad : function(){
14584         
14585         this.hasQuery = false;
14586         
14587         if(!this.hasFocus){
14588             return;
14589         }
14590         
14591         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14592             this.loading.hide();
14593         }
14594         
14595         if(this.store.getCount() > 0){
14596             
14597             this.expand();
14598             this.restrictHeight();
14599             if(this.lastQuery == this.allQuery){
14600                 if(this.editable && !this.tickable){
14601                     this.inputEl().dom.select();
14602                 }
14603                 
14604                 if(
14605                     !this.selectByValue(this.value, true) &&
14606                     this.autoFocus && 
14607                     (
14608                         !this.store.lastOptions ||
14609                         typeof(this.store.lastOptions.add) == 'undefined' || 
14610                         this.store.lastOptions.add != true
14611                     )
14612                 ){
14613                     this.select(0, true);
14614                 }
14615             }else{
14616                 if(this.autoFocus){
14617                     this.selectNext();
14618                 }
14619                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14620                     this.taTask.delay(this.typeAheadDelay);
14621                 }
14622             }
14623         }else{
14624             this.onEmptyResults();
14625         }
14626         
14627         //this.el.focus();
14628     },
14629     // private
14630     onLoadException : function()
14631     {
14632         this.hasQuery = false;
14633         
14634         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14635             this.loading.hide();
14636         }
14637         
14638         if(this.tickable && this.editable){
14639             return;
14640         }
14641         
14642         this.collapse();
14643         // only causes errors at present
14644         //Roo.log(this.store.reader.jsonData);
14645         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14646             // fixme
14647             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14648         //}
14649         
14650         
14651     },
14652     // private
14653     onTypeAhead : function(){
14654         if(this.store.getCount() > 0){
14655             var r = this.store.getAt(0);
14656             var newValue = r.data[this.displayField];
14657             var len = newValue.length;
14658             var selStart = this.getRawValue().length;
14659             
14660             if(selStart != len){
14661                 this.setRawValue(newValue);
14662                 this.selectText(selStart, newValue.length);
14663             }
14664         }
14665     },
14666
14667     // private
14668     onSelect : function(record, index){
14669         
14670         if(this.fireEvent('beforeselect', this, record, index) !== false){
14671         
14672             this.setFromData(index > -1 ? record.data : false);
14673             
14674             this.collapse();
14675             this.fireEvent('select', this, record, index);
14676         }
14677     },
14678
14679     /**
14680      * Returns the currently selected field value or empty string if no value is set.
14681      * @return {String} value The selected value
14682      */
14683     getValue : function()
14684     {
14685         if(Roo.isIOS && this.useNativeIOS){
14686             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14687         }
14688         
14689         if(this.multiple){
14690             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14691         }
14692         
14693         if(this.valueField){
14694             return typeof this.value != 'undefined' ? this.value : '';
14695         }else{
14696             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14697         }
14698     },
14699     
14700     getRawValue : function()
14701     {
14702         if(Roo.isIOS && this.useNativeIOS){
14703             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14704         }
14705         
14706         var v = this.inputEl().getValue();
14707         
14708         return v;
14709     },
14710
14711     /**
14712      * Clears any text/value currently set in the field
14713      */
14714     clearValue : function(){
14715         
14716         if(this.hiddenField){
14717             this.hiddenField.dom.value = '';
14718         }
14719         this.value = '';
14720         this.setRawValue('');
14721         this.lastSelectionText = '';
14722         this.lastData = false;
14723         
14724         var close = this.closeTriggerEl();
14725         
14726         if(close){
14727             close.hide();
14728         }
14729         
14730         this.validate();
14731         
14732     },
14733
14734     /**
14735      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14736      * will be displayed in the field.  If the value does not match the data value of an existing item,
14737      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14738      * Otherwise the field will be blank (although the value will still be set).
14739      * @param {String} value The value to match
14740      */
14741     setValue : function(v)
14742     {
14743         if(Roo.isIOS && this.useNativeIOS){
14744             this.setIOSValue(v);
14745             return;
14746         }
14747         
14748         if(this.multiple){
14749             this.syncValue();
14750             return;
14751         }
14752         
14753         var text = v;
14754         if(this.valueField){
14755             var r = this.findRecord(this.valueField, v);
14756             if(r){
14757                 text = r.data[this.displayField];
14758             }else if(this.valueNotFoundText !== undefined){
14759                 text = this.valueNotFoundText;
14760             }
14761         }
14762         this.lastSelectionText = text;
14763         if(this.hiddenField){
14764             this.hiddenField.dom.value = v;
14765         }
14766         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14767         this.value = v;
14768         
14769         var close = this.closeTriggerEl();
14770         
14771         if(close){
14772             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14773         }
14774         
14775         this.validate();
14776     },
14777     /**
14778      * @property {Object} the last set data for the element
14779      */
14780     
14781     lastData : false,
14782     /**
14783      * Sets the value of the field based on a object which is related to the record format for the store.
14784      * @param {Object} value the value to set as. or false on reset?
14785      */
14786     setFromData : function(o){
14787         
14788         if(this.multiple){
14789             this.addItem(o);
14790             return;
14791         }
14792             
14793         var dv = ''; // display value
14794         var vv = ''; // value value..
14795         this.lastData = o;
14796         if (this.displayField) {
14797             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14798         } else {
14799             // this is an error condition!!!
14800             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14801         }
14802         
14803         if(this.valueField){
14804             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14805         }
14806         
14807         var close = this.closeTriggerEl();
14808         
14809         if(close){
14810             if(dv.length || vv * 1 > 0){
14811                 close.show() ;
14812                 this.blockFocus=true;
14813             } else {
14814                 close.hide();
14815             }             
14816         }
14817         
14818         if(this.hiddenField){
14819             this.hiddenField.dom.value = vv;
14820             
14821             this.lastSelectionText = dv;
14822             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14823             this.value = vv;
14824             return;
14825         }
14826         // no hidden field.. - we store the value in 'value', but still display
14827         // display field!!!!
14828         this.lastSelectionText = dv;
14829         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14830         this.value = vv;
14831         
14832         
14833         
14834     },
14835     // private
14836     reset : function(){
14837         // overridden so that last data is reset..
14838         
14839         if(this.multiple){
14840             this.clearItem();
14841             return;
14842         }
14843         
14844         this.setValue(this.originalValue);
14845         //this.clearInvalid();
14846         this.lastData = false;
14847         if (this.view) {
14848             this.view.clearSelections();
14849         }
14850         
14851         this.validate();
14852     },
14853     // private
14854     findRecord : function(prop, value){
14855         var record;
14856         if(this.store.getCount() > 0){
14857             this.store.each(function(r){
14858                 if(r.data[prop] == value){
14859                     record = r;
14860                     return false;
14861                 }
14862                 return true;
14863             });
14864         }
14865         return record;
14866     },
14867     
14868     getName: function()
14869     {
14870         // returns hidden if it's set..
14871         if (!this.rendered) {return ''};
14872         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14873         
14874     },
14875     // private
14876     onViewMove : function(e, t){
14877         this.inKeyMode = false;
14878     },
14879
14880     // private
14881     onViewOver : function(e, t){
14882         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14883             return;
14884         }
14885         var item = this.view.findItemFromChild(t);
14886         
14887         if(item){
14888             var index = this.view.indexOf(item);
14889             this.select(index, false);
14890         }
14891     },
14892
14893     // private
14894     onViewClick : function(view, doFocus, el, e)
14895     {
14896         var index = this.view.getSelectedIndexes()[0];
14897         
14898         var r = this.store.getAt(index);
14899         
14900         if(this.tickable){
14901             
14902             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14903                 return;
14904             }
14905             
14906             var rm = false;
14907             var _this = this;
14908             
14909             Roo.each(this.tickItems, function(v,k){
14910                 
14911                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14912                     Roo.log(v);
14913                     _this.tickItems.splice(k, 1);
14914                     
14915                     if(typeof(e) == 'undefined' && view == false){
14916                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14917                     }
14918                     
14919                     rm = true;
14920                     return;
14921                 }
14922             });
14923             
14924             if(rm){
14925                 return;
14926             }
14927             
14928             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14929                 this.tickItems.push(r.data);
14930             }
14931             
14932             if(typeof(e) == 'undefined' && view == false){
14933                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14934             }
14935                     
14936             return;
14937         }
14938         
14939         if(r){
14940             this.onSelect(r, index);
14941         }
14942         if(doFocus !== false && !this.blockFocus){
14943             this.inputEl().focus();
14944         }
14945     },
14946
14947     // private
14948     restrictHeight : function(){
14949         //this.innerList.dom.style.height = '';
14950         //var inner = this.innerList.dom;
14951         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14952         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14953         //this.list.beginUpdate();
14954         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14955         this.list.alignTo(this.inputEl(), this.listAlign);
14956         this.list.alignTo(this.inputEl(), this.listAlign);
14957         //this.list.endUpdate();
14958     },
14959
14960     // private
14961     onEmptyResults : function(){
14962         
14963         if(this.tickable && this.editable){
14964             this.hasFocus = false;
14965             this.restrictHeight();
14966             return;
14967         }
14968         
14969         this.collapse();
14970     },
14971
14972     /**
14973      * Returns true if the dropdown list is expanded, else false.
14974      */
14975     isExpanded : function(){
14976         return this.list.isVisible();
14977     },
14978
14979     /**
14980      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14981      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14982      * @param {String} value The data value of the item to select
14983      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14984      * selected item if it is not currently in view (defaults to true)
14985      * @return {Boolean} True if the value matched an item in the list, else false
14986      */
14987     selectByValue : function(v, scrollIntoView){
14988         if(v !== undefined && v !== null){
14989             var r = this.findRecord(this.valueField || this.displayField, v);
14990             if(r){
14991                 this.select(this.store.indexOf(r), scrollIntoView);
14992                 return true;
14993             }
14994         }
14995         return false;
14996     },
14997
14998     /**
14999      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15000      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15001      * @param {Number} index The zero-based index of the list item to select
15002      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15003      * selected item if it is not currently in view (defaults to true)
15004      */
15005     select : function(index, scrollIntoView){
15006         this.selectedIndex = index;
15007         this.view.select(index);
15008         if(scrollIntoView !== false){
15009             var el = this.view.getNode(index);
15010             /*
15011              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15012              */
15013             if(el){
15014                 this.list.scrollChildIntoView(el, false);
15015             }
15016         }
15017     },
15018
15019     // private
15020     selectNext : function(){
15021         var ct = this.store.getCount();
15022         if(ct > 0){
15023             if(this.selectedIndex == -1){
15024                 this.select(0);
15025             }else if(this.selectedIndex < ct-1){
15026                 this.select(this.selectedIndex+1);
15027             }
15028         }
15029     },
15030
15031     // private
15032     selectPrev : function(){
15033         var ct = this.store.getCount();
15034         if(ct > 0){
15035             if(this.selectedIndex == -1){
15036                 this.select(0);
15037             }else if(this.selectedIndex != 0){
15038                 this.select(this.selectedIndex-1);
15039             }
15040         }
15041     },
15042
15043     // private
15044     onKeyUp : function(e){
15045         if(this.editable !== false && !e.isSpecialKey()){
15046             this.lastKey = e.getKey();
15047             this.dqTask.delay(this.queryDelay);
15048         }
15049     },
15050
15051     // private
15052     validateBlur : function(){
15053         return !this.list || !this.list.isVisible();   
15054     },
15055
15056     // private
15057     initQuery : function(){
15058         
15059         var v = this.getRawValue();
15060         
15061         if(this.tickable && this.editable){
15062             v = this.tickableInputEl().getValue();
15063         }
15064         
15065         this.doQuery(v);
15066     },
15067
15068     // private
15069     doForce : function(){
15070         if(this.inputEl().dom.value.length > 0){
15071             this.inputEl().dom.value =
15072                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15073              
15074         }
15075     },
15076
15077     /**
15078      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15079      * query allowing the query action to be canceled if needed.
15080      * @param {String} query The SQL query to execute
15081      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15082      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15083      * saved in the current store (defaults to false)
15084      */
15085     doQuery : function(q, forceAll){
15086         
15087         if(q === undefined || q === null){
15088             q = '';
15089         }
15090         var qe = {
15091             query: q,
15092             forceAll: forceAll,
15093             combo: this,
15094             cancel:false
15095         };
15096         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15097             return false;
15098         }
15099         q = qe.query;
15100         
15101         forceAll = qe.forceAll;
15102         if(forceAll === true || (q.length >= this.minChars)){
15103             
15104             this.hasQuery = true;
15105             
15106             if(this.lastQuery != q || this.alwaysQuery){
15107                 this.lastQuery = q;
15108                 if(this.mode == 'local'){
15109                     this.selectedIndex = -1;
15110                     if(forceAll){
15111                         this.store.clearFilter();
15112                     }else{
15113                         
15114                         if(this.specialFilter){
15115                             this.fireEvent('specialfilter', this);
15116                             this.onLoad();
15117                             return;
15118                         }
15119                         
15120                         this.store.filter(this.displayField, q);
15121                     }
15122                     
15123                     this.store.fireEvent("datachanged", this.store);
15124                     
15125                     this.onLoad();
15126                     
15127                     
15128                 }else{
15129                     
15130                     this.store.baseParams[this.queryParam] = q;
15131                     
15132                     var options = {params : this.getParams(q)};
15133                     
15134                     if(this.loadNext){
15135                         options.add = true;
15136                         options.params.start = this.page * this.pageSize;
15137                     }
15138                     
15139                     this.store.load(options);
15140                     
15141                     /*
15142                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15143                      *  we should expand the list on onLoad
15144                      *  so command out it
15145                      */
15146 //                    this.expand();
15147                 }
15148             }else{
15149                 this.selectedIndex = -1;
15150                 this.onLoad();   
15151             }
15152         }
15153         
15154         this.loadNext = false;
15155     },
15156     
15157     // private
15158     getParams : function(q){
15159         var p = {};
15160         //p[this.queryParam] = q;
15161         
15162         if(this.pageSize){
15163             p.start = 0;
15164             p.limit = this.pageSize;
15165         }
15166         return p;
15167     },
15168
15169     /**
15170      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15171      */
15172     collapse : function(){
15173         if(!this.isExpanded()){
15174             return;
15175         }
15176         
15177         this.list.hide();
15178         
15179         this.hasFocus = false;
15180         
15181         if(this.tickable){
15182             this.okBtn.hide();
15183             this.cancelBtn.hide();
15184             this.trigger.show();
15185             
15186             if(this.editable){
15187                 this.tickableInputEl().dom.value = '';
15188                 this.tickableInputEl().blur();
15189             }
15190             
15191         }
15192         
15193         Roo.get(document).un('mousedown', this.collapseIf, this);
15194         Roo.get(document).un('mousewheel', this.collapseIf, this);
15195         if (!this.editable) {
15196             Roo.get(document).un('keydown', this.listKeyPress, this);
15197         }
15198         this.fireEvent('collapse', this);
15199         
15200         this.validate();
15201     },
15202
15203     // private
15204     collapseIf : function(e){
15205         var in_combo  = e.within(this.el);
15206         var in_list =  e.within(this.list);
15207         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15208         
15209         if (in_combo || in_list || is_list) {
15210             //e.stopPropagation();
15211             return;
15212         }
15213         
15214         if(this.tickable){
15215             this.onTickableFooterButtonClick(e, false, false);
15216         }
15217
15218         this.collapse();
15219         
15220     },
15221
15222     /**
15223      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15224      */
15225     expand : function(){
15226        
15227         if(this.isExpanded() || !this.hasFocus){
15228             return;
15229         }
15230         
15231         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15232         this.list.setWidth(lw);
15233         
15234         Roo.log('expand');
15235         
15236         this.list.show();
15237         
15238         this.restrictHeight();
15239         
15240         if(this.tickable){
15241             
15242             this.tickItems = Roo.apply([], this.item);
15243             
15244             this.okBtn.show();
15245             this.cancelBtn.show();
15246             this.trigger.hide();
15247             
15248             if(this.editable){
15249                 this.tickableInputEl().focus();
15250             }
15251             
15252         }
15253         
15254         Roo.get(document).on('mousedown', this.collapseIf, this);
15255         Roo.get(document).on('mousewheel', this.collapseIf, this);
15256         if (!this.editable) {
15257             Roo.get(document).on('keydown', this.listKeyPress, this);
15258         }
15259         
15260         this.fireEvent('expand', this);
15261     },
15262
15263     // private
15264     // Implements the default empty TriggerField.onTriggerClick function
15265     onTriggerClick : function(e)
15266     {
15267         Roo.log('trigger click');
15268         
15269         if(this.disabled || !this.triggerList){
15270             return;
15271         }
15272         
15273         this.page = 0;
15274         this.loadNext = false;
15275         
15276         if(this.isExpanded()){
15277             this.collapse();
15278             if (!this.blockFocus) {
15279                 this.inputEl().focus();
15280             }
15281             
15282         }else {
15283             this.hasFocus = true;
15284             if(this.triggerAction == 'all') {
15285                 this.doQuery(this.allQuery, true);
15286             } else {
15287                 this.doQuery(this.getRawValue());
15288             }
15289             if (!this.blockFocus) {
15290                 this.inputEl().focus();
15291             }
15292         }
15293     },
15294     
15295     onTickableTriggerClick : function(e)
15296     {
15297         if(this.disabled){
15298             return;
15299         }
15300         
15301         this.page = 0;
15302         this.loadNext = false;
15303         this.hasFocus = true;
15304         
15305         if(this.triggerAction == 'all') {
15306             this.doQuery(this.allQuery, true);
15307         } else {
15308             this.doQuery(this.getRawValue());
15309         }
15310     },
15311     
15312     onSearchFieldClick : function(e)
15313     {
15314         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15315             this.onTickableFooterButtonClick(e, false, false);
15316             return;
15317         }
15318         
15319         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15320             return;
15321         }
15322         
15323         this.page = 0;
15324         this.loadNext = false;
15325         this.hasFocus = true;
15326         
15327         if(this.triggerAction == 'all') {
15328             this.doQuery(this.allQuery, true);
15329         } else {
15330             this.doQuery(this.getRawValue());
15331         }
15332     },
15333     
15334     listKeyPress : function(e)
15335     {
15336         //Roo.log('listkeypress');
15337         // scroll to first matching element based on key pres..
15338         if (e.isSpecialKey()) {
15339             return false;
15340         }
15341         var k = String.fromCharCode(e.getKey()).toUpperCase();
15342         //Roo.log(k);
15343         var match  = false;
15344         var csel = this.view.getSelectedNodes();
15345         var cselitem = false;
15346         if (csel.length) {
15347             var ix = this.view.indexOf(csel[0]);
15348             cselitem  = this.store.getAt(ix);
15349             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15350                 cselitem = false;
15351             }
15352             
15353         }
15354         
15355         this.store.each(function(v) { 
15356             if (cselitem) {
15357                 // start at existing selection.
15358                 if (cselitem.id == v.id) {
15359                     cselitem = false;
15360                 }
15361                 return true;
15362             }
15363                 
15364             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15365                 match = this.store.indexOf(v);
15366                 return false;
15367             }
15368             return true;
15369         }, this);
15370         
15371         if (match === false) {
15372             return true; // no more action?
15373         }
15374         // scroll to?
15375         this.view.select(match);
15376         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15377         sn.scrollIntoView(sn.dom.parentNode, false);
15378     },
15379     
15380     onViewScroll : function(e, t){
15381         
15382         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){
15383             return;
15384         }
15385         
15386         this.hasQuery = true;
15387         
15388         this.loading = this.list.select('.loading', true).first();
15389         
15390         if(this.loading === null){
15391             this.list.createChild({
15392                 tag: 'div',
15393                 cls: 'loading roo-select2-more-results roo-select2-active',
15394                 html: 'Loading more results...'
15395             });
15396             
15397             this.loading = this.list.select('.loading', true).first();
15398             
15399             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15400             
15401             this.loading.hide();
15402         }
15403         
15404         this.loading.show();
15405         
15406         var _combo = this;
15407         
15408         this.page++;
15409         this.loadNext = true;
15410         
15411         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15412         
15413         return;
15414     },
15415     
15416     addItem : function(o)
15417     {   
15418         var dv = ''; // display value
15419         
15420         if (this.displayField) {
15421             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15422         } else {
15423             // this is an error condition!!!
15424             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15425         }
15426         
15427         if(!dv.length){
15428             return;
15429         }
15430         
15431         var choice = this.choices.createChild({
15432             tag: 'li',
15433             cls: 'roo-select2-search-choice',
15434             cn: [
15435                 {
15436                     tag: 'div',
15437                     html: dv
15438                 },
15439                 {
15440                     tag: 'a',
15441                     href: '#',
15442                     cls: 'roo-select2-search-choice-close fa fa-times',
15443                     tabindex: '-1'
15444                 }
15445             ]
15446             
15447         }, this.searchField);
15448         
15449         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15450         
15451         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15452         
15453         this.item.push(o);
15454         
15455         this.lastData = o;
15456         
15457         this.syncValue();
15458         
15459         this.inputEl().dom.value = '';
15460         
15461         this.validate();
15462     },
15463     
15464     onRemoveItem : function(e, _self, o)
15465     {
15466         e.preventDefault();
15467         
15468         this.lastItem = Roo.apply([], this.item);
15469         
15470         var index = this.item.indexOf(o.data) * 1;
15471         
15472         if( index < 0){
15473             Roo.log('not this item?!');
15474             return;
15475         }
15476         
15477         this.item.splice(index, 1);
15478         o.item.remove();
15479         
15480         this.syncValue();
15481         
15482         this.fireEvent('remove', this, e);
15483         
15484         this.validate();
15485         
15486     },
15487     
15488     syncValue : function()
15489     {
15490         if(!this.item.length){
15491             this.clearValue();
15492             return;
15493         }
15494             
15495         var value = [];
15496         var _this = this;
15497         Roo.each(this.item, function(i){
15498             if(_this.valueField){
15499                 value.push(i[_this.valueField]);
15500                 return;
15501             }
15502
15503             value.push(i);
15504         });
15505
15506         this.value = value.join(',');
15507
15508         if(this.hiddenField){
15509             this.hiddenField.dom.value = this.value;
15510         }
15511         
15512         this.store.fireEvent("datachanged", this.store);
15513         
15514         this.validate();
15515     },
15516     
15517     clearItem : function()
15518     {
15519         if(!this.multiple){
15520             return;
15521         }
15522         
15523         this.item = [];
15524         
15525         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15526            c.remove();
15527         });
15528         
15529         this.syncValue();
15530         
15531         this.validate();
15532         
15533         if(this.tickable && !Roo.isTouch){
15534             this.view.refresh();
15535         }
15536     },
15537     
15538     inputEl: function ()
15539     {
15540         if(Roo.isIOS && this.useNativeIOS){
15541             return this.el.select('select.roo-ios-select', true).first();
15542         }
15543         
15544         if(Roo.isTouch && this.mobileTouchView){
15545             return this.el.select('input.form-control',true).first();
15546         }
15547         
15548         if(this.tickable){
15549             return this.searchField;
15550         }
15551         
15552         return this.el.select('input.form-control',true).first();
15553     },
15554     
15555     onTickableFooterButtonClick : function(e, btn, el)
15556     {
15557         e.preventDefault();
15558         
15559         this.lastItem = Roo.apply([], this.item);
15560         
15561         if(btn && btn.name == 'cancel'){
15562             this.tickItems = Roo.apply([], this.item);
15563             this.collapse();
15564             return;
15565         }
15566         
15567         this.clearItem();
15568         
15569         var _this = this;
15570         
15571         Roo.each(this.tickItems, function(o){
15572             _this.addItem(o);
15573         });
15574         
15575         this.collapse();
15576         
15577     },
15578     
15579     validate : function()
15580     {
15581         if(this.getVisibilityEl().hasClass('hidden')){
15582             return true;
15583         }
15584         
15585         var v = this.getRawValue();
15586         
15587         if(this.multiple){
15588             v = this.getValue();
15589         }
15590         
15591         if(this.disabled || this.allowBlank || v.length){
15592             this.markValid();
15593             return true;
15594         }
15595         
15596         this.markInvalid();
15597         return false;
15598     },
15599     
15600     tickableInputEl : function()
15601     {
15602         if(!this.tickable || !this.editable){
15603             return this.inputEl();
15604         }
15605         
15606         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15607     },
15608     
15609     
15610     getAutoCreateTouchView : function()
15611     {
15612         var id = Roo.id();
15613         
15614         var cfg = {
15615             cls: 'form-group' //input-group
15616         };
15617         
15618         var input =  {
15619             tag: 'input',
15620             id : id,
15621             type : this.inputType,
15622             cls : 'form-control x-combo-noedit',
15623             autocomplete: 'new-password',
15624             placeholder : this.placeholder || '',
15625             readonly : true
15626         };
15627         
15628         if (this.name) {
15629             input.name = this.name;
15630         }
15631         
15632         if (this.size) {
15633             input.cls += ' input-' + this.size;
15634         }
15635         
15636         if (this.disabled) {
15637             input.disabled = true;
15638         }
15639         
15640         var inputblock = {
15641             cls : '',
15642             cn : [
15643                 input
15644             ]
15645         };
15646         
15647         if(this.before){
15648             inputblock.cls += ' input-group';
15649             
15650             inputblock.cn.unshift({
15651                 tag :'span',
15652                 cls : 'input-group-addon input-group-prepend input-group-text',
15653                 html : this.before
15654             });
15655         }
15656         
15657         if(this.removable && !this.multiple){
15658             inputblock.cls += ' roo-removable';
15659             
15660             inputblock.cn.push({
15661                 tag: 'button',
15662                 html : 'x',
15663                 cls : 'roo-combo-removable-btn close'
15664             });
15665         }
15666
15667         if(this.hasFeedback && !this.allowBlank){
15668             
15669             inputblock.cls += ' has-feedback';
15670             
15671             inputblock.cn.push({
15672                 tag: 'span',
15673                 cls: 'glyphicon form-control-feedback'
15674             });
15675             
15676         }
15677         
15678         if (this.after) {
15679             
15680             inputblock.cls += (this.before) ? '' : ' input-group';
15681             
15682             inputblock.cn.push({
15683                 tag :'span',
15684                 cls : 'input-group-addon input-group-append input-group-text',
15685                 html : this.after
15686             });
15687         }
15688
15689         
15690         var ibwrap = inputblock;
15691         
15692         if(this.multiple){
15693             ibwrap = {
15694                 tag: 'ul',
15695                 cls: 'roo-select2-choices',
15696                 cn:[
15697                     {
15698                         tag: 'li',
15699                         cls: 'roo-select2-search-field',
15700                         cn: [
15701
15702                             inputblock
15703                         ]
15704                     }
15705                 ]
15706             };
15707         
15708             
15709         }
15710         
15711         var combobox = {
15712             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15713             cn: [
15714                 {
15715                     tag: 'input',
15716                     type : 'hidden',
15717                     cls: 'form-hidden-field'
15718                 },
15719                 ibwrap
15720             ]
15721         };
15722         
15723         if(!this.multiple && this.showToggleBtn){
15724             
15725             var caret = {
15726                 cls: 'caret'
15727             };
15728             
15729             if (this.caret != false) {
15730                 caret = {
15731                      tag: 'i',
15732                      cls: 'fa fa-' + this.caret
15733                 };
15734                 
15735             }
15736             
15737             combobox.cn.push({
15738                 tag :'span',
15739                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15740                 cn : [
15741                     Roo.bootstrap.version == 3 ? caret : '',
15742                     {
15743                         tag: 'span',
15744                         cls: 'combobox-clear',
15745                         cn  : [
15746                             {
15747                                 tag : 'i',
15748                                 cls: 'icon-remove'
15749                             }
15750                         ]
15751                     }
15752                 ]
15753
15754             })
15755         }
15756         
15757         if(this.multiple){
15758             combobox.cls += ' roo-select2-container-multi';
15759         }
15760         
15761         var align = this.labelAlign || this.parentLabelAlign();
15762         
15763         if (align ==='left' && this.fieldLabel.length) {
15764
15765             cfg.cn = [
15766                 {
15767                    tag : 'i',
15768                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15769                    tooltip : 'This field is required'
15770                 },
15771                 {
15772                     tag: 'label',
15773                     cls : 'control-label col-form-label',
15774                     html : this.fieldLabel
15775
15776                 },
15777                 {
15778                     cls : '', 
15779                     cn: [
15780                         combobox
15781                     ]
15782                 }
15783             ];
15784             
15785             var labelCfg = cfg.cn[1];
15786             var contentCfg = cfg.cn[2];
15787             
15788
15789             if(this.indicatorpos == 'right'){
15790                 cfg.cn = [
15791                     {
15792                         tag: 'label',
15793                         'for' :  id,
15794                         cls : 'control-label col-form-label',
15795                         cn : [
15796                             {
15797                                 tag : 'span',
15798                                 html : this.fieldLabel
15799                             },
15800                             {
15801                                 tag : 'i',
15802                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15803                                 tooltip : 'This field is required'
15804                             }
15805                         ]
15806                     },
15807                     {
15808                         cls : "",
15809                         cn: [
15810                             combobox
15811                         ]
15812                     }
15813
15814                 ];
15815                 
15816                 labelCfg = cfg.cn[0];
15817                 contentCfg = cfg.cn[1];
15818             }
15819             
15820            
15821             
15822             if(this.labelWidth > 12){
15823                 labelCfg.style = "width: " + this.labelWidth + 'px';
15824             }
15825             
15826             if(this.labelWidth < 13 && this.labelmd == 0){
15827                 this.labelmd = this.labelWidth;
15828             }
15829             
15830             if(this.labellg > 0){
15831                 labelCfg.cls += ' col-lg-' + this.labellg;
15832                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15833             }
15834             
15835             if(this.labelmd > 0){
15836                 labelCfg.cls += ' col-md-' + this.labelmd;
15837                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15838             }
15839             
15840             if(this.labelsm > 0){
15841                 labelCfg.cls += ' col-sm-' + this.labelsm;
15842                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15843             }
15844             
15845             if(this.labelxs > 0){
15846                 labelCfg.cls += ' col-xs-' + this.labelxs;
15847                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15848             }
15849                 
15850                 
15851         } else if ( this.fieldLabel.length) {
15852             cfg.cn = [
15853                 {
15854                    tag : 'i',
15855                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15856                    tooltip : 'This field is required'
15857                 },
15858                 {
15859                     tag: 'label',
15860                     cls : 'control-label',
15861                     html : this.fieldLabel
15862
15863                 },
15864                 {
15865                     cls : '', 
15866                     cn: [
15867                         combobox
15868                     ]
15869                 }
15870             ];
15871             
15872             if(this.indicatorpos == 'right'){
15873                 cfg.cn = [
15874                     {
15875                         tag: 'label',
15876                         cls : 'control-label',
15877                         html : this.fieldLabel,
15878                         cn : [
15879                             {
15880                                tag : 'i',
15881                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15882                                tooltip : 'This field is required'
15883                             }
15884                         ]
15885                     },
15886                     {
15887                         cls : '', 
15888                         cn: [
15889                             combobox
15890                         ]
15891                     }
15892                 ];
15893             }
15894         } else {
15895             cfg.cn = combobox;    
15896         }
15897         
15898         
15899         var settings = this;
15900         
15901         ['xs','sm','md','lg'].map(function(size){
15902             if (settings[size]) {
15903                 cfg.cls += ' col-' + size + '-' + settings[size];
15904             }
15905         });
15906         
15907         return cfg;
15908     },
15909     
15910     initTouchView : function()
15911     {
15912         this.renderTouchView();
15913         
15914         this.touchViewEl.on('scroll', function(){
15915             this.el.dom.scrollTop = 0;
15916         }, this);
15917         
15918         this.originalValue = this.getValue();
15919         
15920         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15921         
15922         this.inputEl().on("click", this.showTouchView, this);
15923         if (this.triggerEl) {
15924             this.triggerEl.on("click", this.showTouchView, this);
15925         }
15926         
15927         
15928         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15929         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15930         
15931         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15932         
15933         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15934         this.store.on('load', this.onTouchViewLoad, this);
15935         this.store.on('loadexception', this.onTouchViewLoadException, this);
15936         
15937         if(this.hiddenName){
15938             
15939             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15940             
15941             this.hiddenField.dom.value =
15942                 this.hiddenValue !== undefined ? this.hiddenValue :
15943                 this.value !== undefined ? this.value : '';
15944         
15945             this.el.dom.removeAttribute('name');
15946             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15947         }
15948         
15949         if(this.multiple){
15950             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15951             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15952         }
15953         
15954         if(this.removable && !this.multiple){
15955             var close = this.closeTriggerEl();
15956             if(close){
15957                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15958                 close.on('click', this.removeBtnClick, this, close);
15959             }
15960         }
15961         /*
15962          * fix the bug in Safari iOS8
15963          */
15964         this.inputEl().on("focus", function(e){
15965             document.activeElement.blur();
15966         }, this);
15967         
15968         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15969         
15970         return;
15971         
15972         
15973     },
15974     
15975     renderTouchView : function()
15976     {
15977         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15978         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15979         
15980         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15981         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15982         
15983         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15984         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15985         this.touchViewBodyEl.setStyle('overflow', 'auto');
15986         
15987         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15988         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15989         
15990         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15991         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15992         
15993     },
15994     
15995     showTouchView : function()
15996     {
15997         if(this.disabled){
15998             return;
15999         }
16000         
16001         this.touchViewHeaderEl.hide();
16002
16003         if(this.modalTitle.length){
16004             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16005             this.touchViewHeaderEl.show();
16006         }
16007
16008         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16009         this.touchViewEl.show();
16010
16011         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16012         
16013         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16014         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16015
16016         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16017
16018         if(this.modalTitle.length){
16019             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16020         }
16021         
16022         this.touchViewBodyEl.setHeight(bodyHeight);
16023
16024         if(this.animate){
16025             var _this = this;
16026             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16027         }else{
16028             this.touchViewEl.addClass('in');
16029         }
16030         
16031         if(this._touchViewMask){
16032             Roo.get(document.body).addClass("x-body-masked");
16033             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16034             this._touchViewMask.setStyle('z-index', 10000);
16035             this._touchViewMask.addClass('show');
16036         }
16037         
16038         this.doTouchViewQuery();
16039         
16040     },
16041     
16042     hideTouchView : function()
16043     {
16044         this.touchViewEl.removeClass('in');
16045
16046         if(this.animate){
16047             var _this = this;
16048             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16049         }else{
16050             this.touchViewEl.setStyle('display', 'none');
16051         }
16052         
16053         if(this._touchViewMask){
16054             this._touchViewMask.removeClass('show');
16055             Roo.get(document.body).removeClass("x-body-masked");
16056         }
16057     },
16058     
16059     setTouchViewValue : function()
16060     {
16061         if(this.multiple){
16062             this.clearItem();
16063         
16064             var _this = this;
16065
16066             Roo.each(this.tickItems, function(o){
16067                 this.addItem(o);
16068             }, this);
16069         }
16070         
16071         this.hideTouchView();
16072     },
16073     
16074     doTouchViewQuery : function()
16075     {
16076         var qe = {
16077             query: '',
16078             forceAll: true,
16079             combo: this,
16080             cancel:false
16081         };
16082         
16083         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16084             return false;
16085         }
16086         
16087         if(!this.alwaysQuery || this.mode == 'local'){
16088             this.onTouchViewLoad();
16089             return;
16090         }
16091         
16092         this.store.load();
16093     },
16094     
16095     onTouchViewBeforeLoad : function(combo,opts)
16096     {
16097         return;
16098     },
16099
16100     // private
16101     onTouchViewLoad : function()
16102     {
16103         if(this.store.getCount() < 1){
16104             this.onTouchViewEmptyResults();
16105             return;
16106         }
16107         
16108         this.clearTouchView();
16109         
16110         var rawValue = this.getRawValue();
16111         
16112         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16113         
16114         this.tickItems = [];
16115         
16116         this.store.data.each(function(d, rowIndex){
16117             var row = this.touchViewListGroup.createChild(template);
16118             
16119             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16120                 row.addClass(d.data.cls);
16121             }
16122             
16123             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16124                 var cfg = {
16125                     data : d.data,
16126                     html : d.data[this.displayField]
16127                 };
16128                 
16129                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16130                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16131                 }
16132             }
16133             row.removeClass('selected');
16134             if(!this.multiple && this.valueField &&
16135                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16136             {
16137                 // radio buttons..
16138                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16139                 row.addClass('selected');
16140             }
16141             
16142             if(this.multiple && this.valueField &&
16143                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16144             {
16145                 
16146                 // checkboxes...
16147                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16148                 this.tickItems.push(d.data);
16149             }
16150             
16151             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16152             
16153         }, this);
16154         
16155         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16156         
16157         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16158
16159         if(this.modalTitle.length){
16160             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16161         }
16162
16163         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16164         
16165         if(this.mobile_restrict_height && listHeight < bodyHeight){
16166             this.touchViewBodyEl.setHeight(listHeight);
16167         }
16168         
16169         var _this = this;
16170         
16171         if(firstChecked && listHeight > bodyHeight){
16172             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16173         }
16174         
16175     },
16176     
16177     onTouchViewLoadException : function()
16178     {
16179         this.hideTouchView();
16180     },
16181     
16182     onTouchViewEmptyResults : function()
16183     {
16184         this.clearTouchView();
16185         
16186         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16187         
16188         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16189         
16190     },
16191     
16192     clearTouchView : function()
16193     {
16194         this.touchViewListGroup.dom.innerHTML = '';
16195     },
16196     
16197     onTouchViewClick : function(e, el, o)
16198     {
16199         e.preventDefault();
16200         
16201         var row = o.row;
16202         var rowIndex = o.rowIndex;
16203         
16204         var r = this.store.getAt(rowIndex);
16205         
16206         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16207             
16208             if(!this.multiple){
16209                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16210                     c.dom.removeAttribute('checked');
16211                 }, this);
16212
16213                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16214
16215                 this.setFromData(r.data);
16216
16217                 var close = this.closeTriggerEl();
16218
16219                 if(close){
16220                     close.show();
16221                 }
16222
16223                 this.hideTouchView();
16224
16225                 this.fireEvent('select', this, r, rowIndex);
16226
16227                 return;
16228             }
16229
16230             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16231                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16232                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16233                 return;
16234             }
16235
16236             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16237             this.addItem(r.data);
16238             this.tickItems.push(r.data);
16239         }
16240     },
16241     
16242     getAutoCreateNativeIOS : function()
16243     {
16244         var cfg = {
16245             cls: 'form-group' //input-group,
16246         };
16247         
16248         var combobox =  {
16249             tag: 'select',
16250             cls : 'roo-ios-select'
16251         };
16252         
16253         if (this.name) {
16254             combobox.name = this.name;
16255         }
16256         
16257         if (this.disabled) {
16258             combobox.disabled = true;
16259         }
16260         
16261         var settings = this;
16262         
16263         ['xs','sm','md','lg'].map(function(size){
16264             if (settings[size]) {
16265                 cfg.cls += ' col-' + size + '-' + settings[size];
16266             }
16267         });
16268         
16269         cfg.cn = combobox;
16270         
16271         return cfg;
16272         
16273     },
16274     
16275     initIOSView : function()
16276     {
16277         this.store.on('load', this.onIOSViewLoad, this);
16278         
16279         return;
16280     },
16281     
16282     onIOSViewLoad : function()
16283     {
16284         if(this.store.getCount() < 1){
16285             return;
16286         }
16287         
16288         this.clearIOSView();
16289         
16290         if(this.allowBlank) {
16291             
16292             var default_text = '-- SELECT --';
16293             
16294             if(this.placeholder.length){
16295                 default_text = this.placeholder;
16296             }
16297             
16298             if(this.emptyTitle.length){
16299                 default_text += ' - ' + this.emptyTitle + ' -';
16300             }
16301             
16302             var opt = this.inputEl().createChild({
16303                 tag: 'option',
16304                 value : 0,
16305                 html : default_text
16306             });
16307             
16308             var o = {};
16309             o[this.valueField] = 0;
16310             o[this.displayField] = default_text;
16311             
16312             this.ios_options.push({
16313                 data : o,
16314                 el : opt
16315             });
16316             
16317         }
16318         
16319         this.store.data.each(function(d, rowIndex){
16320             
16321             var html = '';
16322             
16323             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16324                 html = d.data[this.displayField];
16325             }
16326             
16327             var value = '';
16328             
16329             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16330                 value = d.data[this.valueField];
16331             }
16332             
16333             var option = {
16334                 tag: 'option',
16335                 value : value,
16336                 html : html
16337             };
16338             
16339             if(this.value == d.data[this.valueField]){
16340                 option['selected'] = true;
16341             }
16342             
16343             var opt = this.inputEl().createChild(option);
16344             
16345             this.ios_options.push({
16346                 data : d.data,
16347                 el : opt
16348             });
16349             
16350         }, this);
16351         
16352         this.inputEl().on('change', function(){
16353            this.fireEvent('select', this);
16354         }, this);
16355         
16356     },
16357     
16358     clearIOSView: function()
16359     {
16360         this.inputEl().dom.innerHTML = '';
16361         
16362         this.ios_options = [];
16363     },
16364     
16365     setIOSValue: function(v)
16366     {
16367         this.value = v;
16368         
16369         if(!this.ios_options){
16370             return;
16371         }
16372         
16373         Roo.each(this.ios_options, function(opts){
16374            
16375            opts.el.dom.removeAttribute('selected');
16376            
16377            if(opts.data[this.valueField] != v){
16378                return;
16379            }
16380            
16381            opts.el.dom.setAttribute('selected', true);
16382            
16383         }, this);
16384     }
16385
16386     /** 
16387     * @cfg {Boolean} grow 
16388     * @hide 
16389     */
16390     /** 
16391     * @cfg {Number} growMin 
16392     * @hide 
16393     */
16394     /** 
16395     * @cfg {Number} growMax 
16396     * @hide 
16397     */
16398     /**
16399      * @hide
16400      * @method autoSize
16401      */
16402 });
16403
16404 Roo.apply(Roo.bootstrap.ComboBox,  {
16405     
16406     header : {
16407         tag: 'div',
16408         cls: 'modal-header',
16409         cn: [
16410             {
16411                 tag: 'h4',
16412                 cls: 'modal-title'
16413             }
16414         ]
16415     },
16416     
16417     body : {
16418         tag: 'div',
16419         cls: 'modal-body',
16420         cn: [
16421             {
16422                 tag: 'ul',
16423                 cls: 'list-group'
16424             }
16425         ]
16426     },
16427     
16428     listItemRadio : {
16429         tag: 'li',
16430         cls: 'list-group-item',
16431         cn: [
16432             {
16433                 tag: 'span',
16434                 cls: 'roo-combobox-list-group-item-value'
16435             },
16436             {
16437                 tag: 'div',
16438                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16439                 cn: [
16440                     {
16441                         tag: 'input',
16442                         type: 'radio'
16443                     },
16444                     {
16445                         tag: 'label'
16446                     }
16447                 ]
16448             }
16449         ]
16450     },
16451     
16452     listItemCheckbox : {
16453         tag: 'li',
16454         cls: 'list-group-item',
16455         cn: [
16456             {
16457                 tag: 'span',
16458                 cls: 'roo-combobox-list-group-item-value'
16459             },
16460             {
16461                 tag: 'div',
16462                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16463                 cn: [
16464                     {
16465                         tag: 'input',
16466                         type: 'checkbox'
16467                     },
16468                     {
16469                         tag: 'label'
16470                     }
16471                 ]
16472             }
16473         ]
16474     },
16475     
16476     emptyResult : {
16477         tag: 'div',
16478         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16479     },
16480     
16481     footer : {
16482         tag: 'div',
16483         cls: 'modal-footer',
16484         cn: [
16485             {
16486                 tag: 'div',
16487                 cls: 'row',
16488                 cn: [
16489                     {
16490                         tag: 'div',
16491                         cls: 'col-xs-6 text-left',
16492                         cn: {
16493                             tag: 'button',
16494                             cls: 'btn btn-danger roo-touch-view-cancel',
16495                             html: 'Cancel'
16496                         }
16497                     },
16498                     {
16499                         tag: 'div',
16500                         cls: 'col-xs-6 text-right',
16501                         cn: {
16502                             tag: 'button',
16503                             cls: 'btn btn-success roo-touch-view-ok',
16504                             html: 'OK'
16505                         }
16506                     }
16507                 ]
16508             }
16509         ]
16510         
16511     }
16512 });
16513
16514 Roo.apply(Roo.bootstrap.ComboBox,  {
16515     
16516     touchViewTemplate : {
16517         tag: 'div',
16518         cls: 'modal fade roo-combobox-touch-view',
16519         cn: [
16520             {
16521                 tag: 'div',
16522                 cls: 'modal-dialog',
16523                 style : 'position:fixed', // we have to fix position....
16524                 cn: [
16525                     {
16526                         tag: 'div',
16527                         cls: 'modal-content',
16528                         cn: [
16529                             Roo.bootstrap.ComboBox.header,
16530                             Roo.bootstrap.ComboBox.body,
16531                             Roo.bootstrap.ComboBox.footer
16532                         ]
16533                     }
16534                 ]
16535             }
16536         ]
16537     }
16538 });/*
16539  * Based on:
16540  * Ext JS Library 1.1.1
16541  * Copyright(c) 2006-2007, Ext JS, LLC.
16542  *
16543  * Originally Released Under LGPL - original licence link has changed is not relivant.
16544  *
16545  * Fork - LGPL
16546  * <script type="text/javascript">
16547  */
16548
16549 /**
16550  * @class Roo.View
16551  * @extends Roo.util.Observable
16552  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16553  * This class also supports single and multi selection modes. <br>
16554  * Create a data model bound view:
16555  <pre><code>
16556  var store = new Roo.data.Store(...);
16557
16558  var view = new Roo.View({
16559     el : "my-element",
16560     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16561  
16562     singleSelect: true,
16563     selectedClass: "ydataview-selected",
16564     store: store
16565  });
16566
16567  // listen for node click?
16568  view.on("click", function(vw, index, node, e){
16569  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16570  });
16571
16572  // load XML data
16573  dataModel.load("foobar.xml");
16574  </code></pre>
16575  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16576  * <br><br>
16577  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16578  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16579  * 
16580  * Note: old style constructor is still suported (container, template, config)
16581  * 
16582  * @constructor
16583  * Create a new View
16584  * @param {Object} config The config object
16585  * 
16586  */
16587 Roo.View = function(config, depreciated_tpl, depreciated_config){
16588     
16589     this.parent = false;
16590     
16591     if (typeof(depreciated_tpl) == 'undefined') {
16592         // new way.. - universal constructor.
16593         Roo.apply(this, config);
16594         this.el  = Roo.get(this.el);
16595     } else {
16596         // old format..
16597         this.el  = Roo.get(config);
16598         this.tpl = depreciated_tpl;
16599         Roo.apply(this, depreciated_config);
16600     }
16601     this.wrapEl  = this.el.wrap().wrap();
16602     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16603     
16604     
16605     if(typeof(this.tpl) == "string"){
16606         this.tpl = new Roo.Template(this.tpl);
16607     } else {
16608         // support xtype ctors..
16609         this.tpl = new Roo.factory(this.tpl, Roo);
16610     }
16611     
16612     
16613     this.tpl.compile();
16614     
16615     /** @private */
16616     this.addEvents({
16617         /**
16618          * @event beforeclick
16619          * Fires before a click is processed. Returns false to cancel the default action.
16620          * @param {Roo.View} this
16621          * @param {Number} index The index of the target node
16622          * @param {HTMLElement} node The target node
16623          * @param {Roo.EventObject} e The raw event object
16624          */
16625             "beforeclick" : true,
16626         /**
16627          * @event click
16628          * Fires when a template node is clicked.
16629          * @param {Roo.View} this
16630          * @param {Number} index The index of the target node
16631          * @param {HTMLElement} node The target node
16632          * @param {Roo.EventObject} e The raw event object
16633          */
16634             "click" : true,
16635         /**
16636          * @event dblclick
16637          * Fires when a template node is double clicked.
16638          * @param {Roo.View} this
16639          * @param {Number} index The index of the target node
16640          * @param {HTMLElement} node The target node
16641          * @param {Roo.EventObject} e The raw event object
16642          */
16643             "dblclick" : true,
16644         /**
16645          * @event contextmenu
16646          * Fires when a template node is right clicked.
16647          * @param {Roo.View} this
16648          * @param {Number} index The index of the target node
16649          * @param {HTMLElement} node The target node
16650          * @param {Roo.EventObject} e The raw event object
16651          */
16652             "contextmenu" : true,
16653         /**
16654          * @event selectionchange
16655          * Fires when the selected nodes change.
16656          * @param {Roo.View} this
16657          * @param {Array} selections Array of the selected nodes
16658          */
16659             "selectionchange" : true,
16660     
16661         /**
16662          * @event beforeselect
16663          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16664          * @param {Roo.View} this
16665          * @param {HTMLElement} node The node to be selected
16666          * @param {Array} selections Array of currently selected nodes
16667          */
16668             "beforeselect" : true,
16669         /**
16670          * @event preparedata
16671          * Fires on every row to render, to allow you to change the data.
16672          * @param {Roo.View} this
16673          * @param {Object} data to be rendered (change this)
16674          */
16675           "preparedata" : true
16676           
16677           
16678         });
16679
16680
16681
16682     this.el.on({
16683         "click": this.onClick,
16684         "dblclick": this.onDblClick,
16685         "contextmenu": this.onContextMenu,
16686         scope:this
16687     });
16688
16689     this.selections = [];
16690     this.nodes = [];
16691     this.cmp = new Roo.CompositeElementLite([]);
16692     if(this.store){
16693         this.store = Roo.factory(this.store, Roo.data);
16694         this.setStore(this.store, true);
16695     }
16696     
16697     if ( this.footer && this.footer.xtype) {
16698            
16699          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16700         
16701         this.footer.dataSource = this.store;
16702         this.footer.container = fctr;
16703         this.footer = Roo.factory(this.footer, Roo);
16704         fctr.insertFirst(this.el);
16705         
16706         // this is a bit insane - as the paging toolbar seems to detach the el..
16707 //        dom.parentNode.parentNode.parentNode
16708          // they get detached?
16709     }
16710     
16711     
16712     Roo.View.superclass.constructor.call(this);
16713     
16714     
16715 };
16716
16717 Roo.extend(Roo.View, Roo.util.Observable, {
16718     
16719      /**
16720      * @cfg {Roo.data.Store} store Data store to load data from.
16721      */
16722     store : false,
16723     
16724     /**
16725      * @cfg {String|Roo.Element} el The container element.
16726      */
16727     el : '',
16728     
16729     /**
16730      * @cfg {String|Roo.Template} tpl The template used by this View 
16731      */
16732     tpl : false,
16733     /**
16734      * @cfg {String} dataName the named area of the template to use as the data area
16735      *                          Works with domtemplates roo-name="name"
16736      */
16737     dataName: false,
16738     /**
16739      * @cfg {String} selectedClass The css class to add to selected nodes
16740      */
16741     selectedClass : "x-view-selected",
16742      /**
16743      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16744      */
16745     emptyText : "",
16746     
16747     /**
16748      * @cfg {String} text to display on mask (default Loading)
16749      */
16750     mask : false,
16751     /**
16752      * @cfg {Boolean} multiSelect Allow multiple selection
16753      */
16754     multiSelect : false,
16755     /**
16756      * @cfg {Boolean} singleSelect Allow single selection
16757      */
16758     singleSelect:  false,
16759     
16760     /**
16761      * @cfg {Boolean} toggleSelect - selecting 
16762      */
16763     toggleSelect : false,
16764     
16765     /**
16766      * @cfg {Boolean} tickable - selecting 
16767      */
16768     tickable : false,
16769     
16770     /**
16771      * Returns the element this view is bound to.
16772      * @return {Roo.Element}
16773      */
16774     getEl : function(){
16775         return this.wrapEl;
16776     },
16777     
16778     
16779
16780     /**
16781      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16782      */
16783     refresh : function(){
16784         //Roo.log('refresh');
16785         var t = this.tpl;
16786         
16787         // if we are using something like 'domtemplate', then
16788         // the what gets used is:
16789         // t.applySubtemplate(NAME, data, wrapping data..)
16790         // the outer template then get' applied with
16791         //     the store 'extra data'
16792         // and the body get's added to the
16793         //      roo-name="data" node?
16794         //      <span class='roo-tpl-{name}'></span> ?????
16795         
16796         
16797         
16798         this.clearSelections();
16799         this.el.update("");
16800         var html = [];
16801         var records = this.store.getRange();
16802         if(records.length < 1) {
16803             
16804             // is this valid??  = should it render a template??
16805             
16806             this.el.update(this.emptyText);
16807             return;
16808         }
16809         var el = this.el;
16810         if (this.dataName) {
16811             this.el.update(t.apply(this.store.meta)); //????
16812             el = this.el.child('.roo-tpl-' + this.dataName);
16813         }
16814         
16815         for(var i = 0, len = records.length; i < len; i++){
16816             var data = this.prepareData(records[i].data, i, records[i]);
16817             this.fireEvent("preparedata", this, data, i, records[i]);
16818             
16819             var d = Roo.apply({}, data);
16820             
16821             if(this.tickable){
16822                 Roo.apply(d, {'roo-id' : Roo.id()});
16823                 
16824                 var _this = this;
16825             
16826                 Roo.each(this.parent.item, function(item){
16827                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16828                         return;
16829                     }
16830                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16831                 });
16832             }
16833             
16834             html[html.length] = Roo.util.Format.trim(
16835                 this.dataName ?
16836                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16837                     t.apply(d)
16838             );
16839         }
16840         
16841         
16842         
16843         el.update(html.join(""));
16844         this.nodes = el.dom.childNodes;
16845         this.updateIndexes(0);
16846     },
16847     
16848
16849     /**
16850      * Function to override to reformat the data that is sent to
16851      * the template for each node.
16852      * DEPRICATED - use the preparedata event handler.
16853      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16854      * a JSON object for an UpdateManager bound view).
16855      */
16856     prepareData : function(data, index, record)
16857     {
16858         this.fireEvent("preparedata", this, data, index, record);
16859         return data;
16860     },
16861
16862     onUpdate : function(ds, record){
16863         // Roo.log('on update');   
16864         this.clearSelections();
16865         var index = this.store.indexOf(record);
16866         var n = this.nodes[index];
16867         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16868         n.parentNode.removeChild(n);
16869         this.updateIndexes(index, index);
16870     },
16871
16872     
16873     
16874 // --------- FIXME     
16875     onAdd : function(ds, records, index)
16876     {
16877         //Roo.log(['on Add', ds, records, index] );        
16878         this.clearSelections();
16879         if(this.nodes.length == 0){
16880             this.refresh();
16881             return;
16882         }
16883         var n = this.nodes[index];
16884         for(var i = 0, len = records.length; i < len; i++){
16885             var d = this.prepareData(records[i].data, i, records[i]);
16886             if(n){
16887                 this.tpl.insertBefore(n, d);
16888             }else{
16889                 
16890                 this.tpl.append(this.el, d);
16891             }
16892         }
16893         this.updateIndexes(index);
16894     },
16895
16896     onRemove : function(ds, record, index){
16897        // Roo.log('onRemove');
16898         this.clearSelections();
16899         var el = this.dataName  ?
16900             this.el.child('.roo-tpl-' + this.dataName) :
16901             this.el; 
16902         
16903         el.dom.removeChild(this.nodes[index]);
16904         this.updateIndexes(index);
16905     },
16906
16907     /**
16908      * Refresh an individual node.
16909      * @param {Number} index
16910      */
16911     refreshNode : function(index){
16912         this.onUpdate(this.store, this.store.getAt(index));
16913     },
16914
16915     updateIndexes : function(startIndex, endIndex){
16916         var ns = this.nodes;
16917         startIndex = startIndex || 0;
16918         endIndex = endIndex || ns.length - 1;
16919         for(var i = startIndex; i <= endIndex; i++){
16920             ns[i].nodeIndex = i;
16921         }
16922     },
16923
16924     /**
16925      * Changes the data store this view uses and refresh the view.
16926      * @param {Store} store
16927      */
16928     setStore : function(store, initial){
16929         if(!initial && this.store){
16930             this.store.un("datachanged", this.refresh);
16931             this.store.un("add", this.onAdd);
16932             this.store.un("remove", this.onRemove);
16933             this.store.un("update", this.onUpdate);
16934             this.store.un("clear", this.refresh);
16935             this.store.un("beforeload", this.onBeforeLoad);
16936             this.store.un("load", this.onLoad);
16937             this.store.un("loadexception", this.onLoad);
16938         }
16939         if(store){
16940           
16941             store.on("datachanged", this.refresh, this);
16942             store.on("add", this.onAdd, this);
16943             store.on("remove", this.onRemove, this);
16944             store.on("update", this.onUpdate, this);
16945             store.on("clear", this.refresh, this);
16946             store.on("beforeload", this.onBeforeLoad, this);
16947             store.on("load", this.onLoad, this);
16948             store.on("loadexception", this.onLoad, this);
16949         }
16950         
16951         if(store){
16952             this.refresh();
16953         }
16954     },
16955     /**
16956      * onbeforeLoad - masks the loading area.
16957      *
16958      */
16959     onBeforeLoad : function(store,opts)
16960     {
16961          //Roo.log('onBeforeLoad');   
16962         if (!opts.add) {
16963             this.el.update("");
16964         }
16965         this.el.mask(this.mask ? this.mask : "Loading" ); 
16966     },
16967     onLoad : function ()
16968     {
16969         this.el.unmask();
16970     },
16971     
16972
16973     /**
16974      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16975      * @param {HTMLElement} node
16976      * @return {HTMLElement} The template node
16977      */
16978     findItemFromChild : function(node){
16979         var el = this.dataName  ?
16980             this.el.child('.roo-tpl-' + this.dataName,true) :
16981             this.el.dom; 
16982         
16983         if(!node || node.parentNode == el){
16984                     return node;
16985             }
16986             var p = node.parentNode;
16987             while(p && p != el){
16988             if(p.parentNode == el){
16989                 return p;
16990             }
16991             p = p.parentNode;
16992         }
16993             return null;
16994     },
16995
16996     /** @ignore */
16997     onClick : function(e){
16998         var item = this.findItemFromChild(e.getTarget());
16999         if(item){
17000             var index = this.indexOf(item);
17001             if(this.onItemClick(item, index, e) !== false){
17002                 this.fireEvent("click", this, index, item, e);
17003             }
17004         }else{
17005             this.clearSelections();
17006         }
17007     },
17008
17009     /** @ignore */
17010     onContextMenu : function(e){
17011         var item = this.findItemFromChild(e.getTarget());
17012         if(item){
17013             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17014         }
17015     },
17016
17017     /** @ignore */
17018     onDblClick : function(e){
17019         var item = this.findItemFromChild(e.getTarget());
17020         if(item){
17021             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17022         }
17023     },
17024
17025     onItemClick : function(item, index, e)
17026     {
17027         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17028             return false;
17029         }
17030         if (this.toggleSelect) {
17031             var m = this.isSelected(item) ? 'unselect' : 'select';
17032             //Roo.log(m);
17033             var _t = this;
17034             _t[m](item, true, false);
17035             return true;
17036         }
17037         if(this.multiSelect || this.singleSelect){
17038             if(this.multiSelect && e.shiftKey && this.lastSelection){
17039                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17040             }else{
17041                 this.select(item, this.multiSelect && e.ctrlKey);
17042                 this.lastSelection = item;
17043             }
17044             
17045             if(!this.tickable){
17046                 e.preventDefault();
17047             }
17048             
17049         }
17050         return true;
17051     },
17052
17053     /**
17054      * Get the number of selected nodes.
17055      * @return {Number}
17056      */
17057     getSelectionCount : function(){
17058         return this.selections.length;
17059     },
17060
17061     /**
17062      * Get the currently selected nodes.
17063      * @return {Array} An array of HTMLElements
17064      */
17065     getSelectedNodes : function(){
17066         return this.selections;
17067     },
17068
17069     /**
17070      * Get the indexes of the selected nodes.
17071      * @return {Array}
17072      */
17073     getSelectedIndexes : function(){
17074         var indexes = [], s = this.selections;
17075         for(var i = 0, len = s.length; i < len; i++){
17076             indexes.push(s[i].nodeIndex);
17077         }
17078         return indexes;
17079     },
17080
17081     /**
17082      * Clear all selections
17083      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17084      */
17085     clearSelections : function(suppressEvent){
17086         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17087             this.cmp.elements = this.selections;
17088             this.cmp.removeClass(this.selectedClass);
17089             this.selections = [];
17090             if(!suppressEvent){
17091                 this.fireEvent("selectionchange", this, this.selections);
17092             }
17093         }
17094     },
17095
17096     /**
17097      * Returns true if the passed node is selected
17098      * @param {HTMLElement/Number} node The node or node index
17099      * @return {Boolean}
17100      */
17101     isSelected : function(node){
17102         var s = this.selections;
17103         if(s.length < 1){
17104             return false;
17105         }
17106         node = this.getNode(node);
17107         return s.indexOf(node) !== -1;
17108     },
17109
17110     /**
17111      * Selects nodes.
17112      * @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
17113      * @param {Boolean} keepExisting (optional) true to keep existing selections
17114      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17115      */
17116     select : function(nodeInfo, keepExisting, suppressEvent){
17117         if(nodeInfo instanceof Array){
17118             if(!keepExisting){
17119                 this.clearSelections(true);
17120             }
17121             for(var i = 0, len = nodeInfo.length; i < len; i++){
17122                 this.select(nodeInfo[i], true, true);
17123             }
17124             return;
17125         } 
17126         var node = this.getNode(nodeInfo);
17127         if(!node || this.isSelected(node)){
17128             return; // already selected.
17129         }
17130         if(!keepExisting){
17131             this.clearSelections(true);
17132         }
17133         
17134         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17135             Roo.fly(node).addClass(this.selectedClass);
17136             this.selections.push(node);
17137             if(!suppressEvent){
17138                 this.fireEvent("selectionchange", this, this.selections);
17139             }
17140         }
17141         
17142         
17143     },
17144       /**
17145      * Unselects nodes.
17146      * @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
17147      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17148      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17149      */
17150     unselect : function(nodeInfo, keepExisting, suppressEvent)
17151     {
17152         if(nodeInfo instanceof Array){
17153             Roo.each(this.selections, function(s) {
17154                 this.unselect(s, nodeInfo);
17155             }, this);
17156             return;
17157         }
17158         var node = this.getNode(nodeInfo);
17159         if(!node || !this.isSelected(node)){
17160             //Roo.log("not selected");
17161             return; // not selected.
17162         }
17163         // fireevent???
17164         var ns = [];
17165         Roo.each(this.selections, function(s) {
17166             if (s == node ) {
17167                 Roo.fly(node).removeClass(this.selectedClass);
17168
17169                 return;
17170             }
17171             ns.push(s);
17172         },this);
17173         
17174         this.selections= ns;
17175         this.fireEvent("selectionchange", this, this.selections);
17176     },
17177
17178     /**
17179      * Gets a template node.
17180      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17181      * @return {HTMLElement} The node or null if it wasn't found
17182      */
17183     getNode : function(nodeInfo){
17184         if(typeof nodeInfo == "string"){
17185             return document.getElementById(nodeInfo);
17186         }else if(typeof nodeInfo == "number"){
17187             return this.nodes[nodeInfo];
17188         }
17189         return nodeInfo;
17190     },
17191
17192     /**
17193      * Gets a range template nodes.
17194      * @param {Number} startIndex
17195      * @param {Number} endIndex
17196      * @return {Array} An array of nodes
17197      */
17198     getNodes : function(start, end){
17199         var ns = this.nodes;
17200         start = start || 0;
17201         end = typeof end == "undefined" ? ns.length - 1 : end;
17202         var nodes = [];
17203         if(start <= end){
17204             for(var i = start; i <= end; i++){
17205                 nodes.push(ns[i]);
17206             }
17207         } else{
17208             for(var i = start; i >= end; i--){
17209                 nodes.push(ns[i]);
17210             }
17211         }
17212         return nodes;
17213     },
17214
17215     /**
17216      * Finds the index of the passed node
17217      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17218      * @return {Number} The index of the node or -1
17219      */
17220     indexOf : function(node){
17221         node = this.getNode(node);
17222         if(typeof node.nodeIndex == "number"){
17223             return node.nodeIndex;
17224         }
17225         var ns = this.nodes;
17226         for(var i = 0, len = ns.length; i < len; i++){
17227             if(ns[i] == node){
17228                 return i;
17229             }
17230         }
17231         return -1;
17232     }
17233 });
17234 /*
17235  * - LGPL
17236  *
17237  * based on jquery fullcalendar
17238  * 
17239  */
17240
17241 Roo.bootstrap = Roo.bootstrap || {};
17242 /**
17243  * @class Roo.bootstrap.Calendar
17244  * @extends Roo.bootstrap.Component
17245  * Bootstrap Calendar class
17246  * @cfg {Boolean} loadMask (true|false) default false
17247  * @cfg {Object} header generate the user specific header of the calendar, default false
17248
17249  * @constructor
17250  * Create a new Container
17251  * @param {Object} config The config object
17252  */
17253
17254
17255
17256 Roo.bootstrap.Calendar = function(config){
17257     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17258      this.addEvents({
17259         /**
17260              * @event select
17261              * Fires when a date is selected
17262              * @param {DatePicker} this
17263              * @param {Date} date The selected date
17264              */
17265         'select': true,
17266         /**
17267              * @event monthchange
17268              * Fires when the displayed month changes 
17269              * @param {DatePicker} this
17270              * @param {Date} date The selected month
17271              */
17272         'monthchange': true,
17273         /**
17274              * @event evententer
17275              * Fires when mouse over an event
17276              * @param {Calendar} this
17277              * @param {event} Event
17278              */
17279         'evententer': true,
17280         /**
17281              * @event eventleave
17282              * Fires when the mouse leaves an
17283              * @param {Calendar} this
17284              * @param {event}
17285              */
17286         'eventleave': true,
17287         /**
17288              * @event eventclick
17289              * Fires when the mouse click an
17290              * @param {Calendar} this
17291              * @param {event}
17292              */
17293         'eventclick': true
17294         
17295     });
17296
17297 };
17298
17299 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17300     
17301      /**
17302      * @cfg {Number} startDay
17303      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17304      */
17305     startDay : 0,
17306     
17307     loadMask : false,
17308     
17309     header : false,
17310       
17311     getAutoCreate : function(){
17312         
17313         
17314         var fc_button = function(name, corner, style, content ) {
17315             return Roo.apply({},{
17316                 tag : 'span',
17317                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17318                          (corner.length ?
17319                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17320                             ''
17321                         ),
17322                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17323                 unselectable: 'on'
17324             });
17325         };
17326         
17327         var header = {};
17328         
17329         if(!this.header){
17330             header = {
17331                 tag : 'table',
17332                 cls : 'fc-header',
17333                 style : 'width:100%',
17334                 cn : [
17335                     {
17336                         tag: 'tr',
17337                         cn : [
17338                             {
17339                                 tag : 'td',
17340                                 cls : 'fc-header-left',
17341                                 cn : [
17342                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17343                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17344                                     { tag: 'span', cls: 'fc-header-space' },
17345                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17346
17347
17348                                 ]
17349                             },
17350
17351                             {
17352                                 tag : 'td',
17353                                 cls : 'fc-header-center',
17354                                 cn : [
17355                                     {
17356                                         tag: 'span',
17357                                         cls: 'fc-header-title',
17358                                         cn : {
17359                                             tag: 'H2',
17360                                             html : 'month / year'
17361                                         }
17362                                     }
17363
17364                                 ]
17365                             },
17366                             {
17367                                 tag : 'td',
17368                                 cls : 'fc-header-right',
17369                                 cn : [
17370                               /*      fc_button('month', 'left', '', 'month' ),
17371                                     fc_button('week', '', '', 'week' ),
17372                                     fc_button('day', 'right', '', 'day' )
17373                                 */    
17374
17375                                 ]
17376                             }
17377
17378                         ]
17379                     }
17380                 ]
17381             };
17382         }
17383         
17384         header = this.header;
17385         
17386        
17387         var cal_heads = function() {
17388             var ret = [];
17389             // fixme - handle this.
17390             
17391             for (var i =0; i < Date.dayNames.length; i++) {
17392                 var d = Date.dayNames[i];
17393                 ret.push({
17394                     tag: 'th',
17395                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17396                     html : d.substring(0,3)
17397                 });
17398                 
17399             }
17400             ret[0].cls += ' fc-first';
17401             ret[6].cls += ' fc-last';
17402             return ret;
17403         };
17404         var cal_cell = function(n) {
17405             return  {
17406                 tag: 'td',
17407                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17408                 cn : [
17409                     {
17410                         cn : [
17411                             {
17412                                 cls: 'fc-day-number',
17413                                 html: 'D'
17414                             },
17415                             {
17416                                 cls: 'fc-day-content',
17417                              
17418                                 cn : [
17419                                      {
17420                                         style: 'position: relative;' // height: 17px;
17421                                     }
17422                                 ]
17423                             }
17424                             
17425                             
17426                         ]
17427                     }
17428                 ]
17429                 
17430             }
17431         };
17432         var cal_rows = function() {
17433             
17434             var ret = [];
17435             for (var r = 0; r < 6; r++) {
17436                 var row= {
17437                     tag : 'tr',
17438                     cls : 'fc-week',
17439                     cn : []
17440                 };
17441                 
17442                 for (var i =0; i < Date.dayNames.length; i++) {
17443                     var d = Date.dayNames[i];
17444                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17445
17446                 }
17447                 row.cn[0].cls+=' fc-first';
17448                 row.cn[0].cn[0].style = 'min-height:90px';
17449                 row.cn[6].cls+=' fc-last';
17450                 ret.push(row);
17451                 
17452             }
17453             ret[0].cls += ' fc-first';
17454             ret[4].cls += ' fc-prev-last';
17455             ret[5].cls += ' fc-last';
17456             return ret;
17457             
17458         };
17459         
17460         var cal_table = {
17461             tag: 'table',
17462             cls: 'fc-border-separate',
17463             style : 'width:100%',
17464             cellspacing  : 0,
17465             cn : [
17466                 { 
17467                     tag: 'thead',
17468                     cn : [
17469                         { 
17470                             tag: 'tr',
17471                             cls : 'fc-first fc-last',
17472                             cn : cal_heads()
17473                         }
17474                     ]
17475                 },
17476                 { 
17477                     tag: 'tbody',
17478                     cn : cal_rows()
17479                 }
17480                   
17481             ]
17482         };
17483          
17484          var cfg = {
17485             cls : 'fc fc-ltr',
17486             cn : [
17487                 header,
17488                 {
17489                     cls : 'fc-content',
17490                     style : "position: relative;",
17491                     cn : [
17492                         {
17493                             cls : 'fc-view fc-view-month fc-grid',
17494                             style : 'position: relative',
17495                             unselectable : 'on',
17496                             cn : [
17497                                 {
17498                                     cls : 'fc-event-container',
17499                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17500                                 },
17501                                 cal_table
17502                             ]
17503                         }
17504                     ]
17505     
17506                 }
17507            ] 
17508             
17509         };
17510         
17511          
17512         
17513         return cfg;
17514     },
17515     
17516     
17517     initEvents : function()
17518     {
17519         if(!this.store){
17520             throw "can not find store for calendar";
17521         }
17522         
17523         var mark = {
17524             tag: "div",
17525             cls:"x-dlg-mask",
17526             style: "text-align:center",
17527             cn: [
17528                 {
17529                     tag: "div",
17530                     style: "background-color:white;width:50%;margin:250 auto",
17531                     cn: [
17532                         {
17533                             tag: "img",
17534                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17535                         },
17536                         {
17537                             tag: "span",
17538                             html: "Loading"
17539                         }
17540                         
17541                     ]
17542                 }
17543             ]
17544         };
17545         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17546         
17547         var size = this.el.select('.fc-content', true).first().getSize();
17548         this.maskEl.setSize(size.width, size.height);
17549         this.maskEl.enableDisplayMode("block");
17550         if(!this.loadMask){
17551             this.maskEl.hide();
17552         }
17553         
17554         this.store = Roo.factory(this.store, Roo.data);
17555         this.store.on('load', this.onLoad, this);
17556         this.store.on('beforeload', this.onBeforeLoad, this);
17557         
17558         this.resize();
17559         
17560         this.cells = this.el.select('.fc-day',true);
17561         //Roo.log(this.cells);
17562         this.textNodes = this.el.query('.fc-day-number');
17563         this.cells.addClassOnOver('fc-state-hover');
17564         
17565         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17566         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17567         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17568         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17569         
17570         this.on('monthchange', this.onMonthChange, this);
17571         
17572         this.update(new Date().clearTime());
17573     },
17574     
17575     resize : function() {
17576         var sz  = this.el.getSize();
17577         
17578         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17579         this.el.select('.fc-day-content div',true).setHeight(34);
17580     },
17581     
17582     
17583     // private
17584     showPrevMonth : function(e){
17585         this.update(this.activeDate.add("mo", -1));
17586     },
17587     showToday : function(e){
17588         this.update(new Date().clearTime());
17589     },
17590     // private
17591     showNextMonth : function(e){
17592         this.update(this.activeDate.add("mo", 1));
17593     },
17594
17595     // private
17596     showPrevYear : function(){
17597         this.update(this.activeDate.add("y", -1));
17598     },
17599
17600     // private
17601     showNextYear : function(){
17602         this.update(this.activeDate.add("y", 1));
17603     },
17604
17605     
17606    // private
17607     update : function(date)
17608     {
17609         var vd = this.activeDate;
17610         this.activeDate = date;
17611 //        if(vd && this.el){
17612 //            var t = date.getTime();
17613 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17614 //                Roo.log('using add remove');
17615 //                
17616 //                this.fireEvent('monthchange', this, date);
17617 //                
17618 //                this.cells.removeClass("fc-state-highlight");
17619 //                this.cells.each(function(c){
17620 //                   if(c.dateValue == t){
17621 //                       c.addClass("fc-state-highlight");
17622 //                       setTimeout(function(){
17623 //                            try{c.dom.firstChild.focus();}catch(e){}
17624 //                       }, 50);
17625 //                       return false;
17626 //                   }
17627 //                   return true;
17628 //                });
17629 //                return;
17630 //            }
17631 //        }
17632         
17633         var days = date.getDaysInMonth();
17634         
17635         var firstOfMonth = date.getFirstDateOfMonth();
17636         var startingPos = firstOfMonth.getDay()-this.startDay;
17637         
17638         if(startingPos < this.startDay){
17639             startingPos += 7;
17640         }
17641         
17642         var pm = date.add(Date.MONTH, -1);
17643         var prevStart = pm.getDaysInMonth()-startingPos;
17644 //        
17645         this.cells = this.el.select('.fc-day',true);
17646         this.textNodes = this.el.query('.fc-day-number');
17647         this.cells.addClassOnOver('fc-state-hover');
17648         
17649         var cells = this.cells.elements;
17650         var textEls = this.textNodes;
17651         
17652         Roo.each(cells, function(cell){
17653             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17654         });
17655         
17656         days += startingPos;
17657
17658         // convert everything to numbers so it's fast
17659         var day = 86400000;
17660         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17661         //Roo.log(d);
17662         //Roo.log(pm);
17663         //Roo.log(prevStart);
17664         
17665         var today = new Date().clearTime().getTime();
17666         var sel = date.clearTime().getTime();
17667         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17668         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17669         var ddMatch = this.disabledDatesRE;
17670         var ddText = this.disabledDatesText;
17671         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17672         var ddaysText = this.disabledDaysText;
17673         var format = this.format;
17674         
17675         var setCellClass = function(cal, cell){
17676             cell.row = 0;
17677             cell.events = [];
17678             cell.more = [];
17679             //Roo.log('set Cell Class');
17680             cell.title = "";
17681             var t = d.getTime();
17682             
17683             //Roo.log(d);
17684             
17685             cell.dateValue = t;
17686             if(t == today){
17687                 cell.className += " fc-today";
17688                 cell.className += " fc-state-highlight";
17689                 cell.title = cal.todayText;
17690             }
17691             if(t == sel){
17692                 // disable highlight in other month..
17693                 //cell.className += " fc-state-highlight";
17694                 
17695             }
17696             // disabling
17697             if(t < min) {
17698                 cell.className = " fc-state-disabled";
17699                 cell.title = cal.minText;
17700                 return;
17701             }
17702             if(t > max) {
17703                 cell.className = " fc-state-disabled";
17704                 cell.title = cal.maxText;
17705                 return;
17706             }
17707             if(ddays){
17708                 if(ddays.indexOf(d.getDay()) != -1){
17709                     cell.title = ddaysText;
17710                     cell.className = " fc-state-disabled";
17711                 }
17712             }
17713             if(ddMatch && format){
17714                 var fvalue = d.dateFormat(format);
17715                 if(ddMatch.test(fvalue)){
17716                     cell.title = ddText.replace("%0", fvalue);
17717                     cell.className = " fc-state-disabled";
17718                 }
17719             }
17720             
17721             if (!cell.initialClassName) {
17722                 cell.initialClassName = cell.dom.className;
17723             }
17724             
17725             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17726         };
17727
17728         var i = 0;
17729         
17730         for(; i < startingPos; i++) {
17731             textEls[i].innerHTML = (++prevStart);
17732             d.setDate(d.getDate()+1);
17733             
17734             cells[i].className = "fc-past fc-other-month";
17735             setCellClass(this, cells[i]);
17736         }
17737         
17738         var intDay = 0;
17739         
17740         for(; i < days; i++){
17741             intDay = i - startingPos + 1;
17742             textEls[i].innerHTML = (intDay);
17743             d.setDate(d.getDate()+1);
17744             
17745             cells[i].className = ''; // "x-date-active";
17746             setCellClass(this, cells[i]);
17747         }
17748         var extraDays = 0;
17749         
17750         for(; i < 42; i++) {
17751             textEls[i].innerHTML = (++extraDays);
17752             d.setDate(d.getDate()+1);
17753             
17754             cells[i].className = "fc-future fc-other-month";
17755             setCellClass(this, cells[i]);
17756         }
17757         
17758         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17759         
17760         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17761         
17762         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17763         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17764         
17765         if(totalRows != 6){
17766             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17767             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17768         }
17769         
17770         this.fireEvent('monthchange', this, date);
17771         
17772         
17773         /*
17774         if(!this.internalRender){
17775             var main = this.el.dom.firstChild;
17776             var w = main.offsetWidth;
17777             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17778             Roo.fly(main).setWidth(w);
17779             this.internalRender = true;
17780             // opera does not respect the auto grow header center column
17781             // then, after it gets a width opera refuses to recalculate
17782             // without a second pass
17783             if(Roo.isOpera && !this.secondPass){
17784                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17785                 this.secondPass = true;
17786                 this.update.defer(10, this, [date]);
17787             }
17788         }
17789         */
17790         
17791     },
17792     
17793     findCell : function(dt) {
17794         dt = dt.clearTime().getTime();
17795         var ret = false;
17796         this.cells.each(function(c){
17797             //Roo.log("check " +c.dateValue + '?=' + dt);
17798             if(c.dateValue == dt){
17799                 ret = c;
17800                 return false;
17801             }
17802             return true;
17803         });
17804         
17805         return ret;
17806     },
17807     
17808     findCells : function(ev) {
17809         var s = ev.start.clone().clearTime().getTime();
17810        // Roo.log(s);
17811         var e= ev.end.clone().clearTime().getTime();
17812        // Roo.log(e);
17813         var ret = [];
17814         this.cells.each(function(c){
17815              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17816             
17817             if(c.dateValue > e){
17818                 return ;
17819             }
17820             if(c.dateValue < s){
17821                 return ;
17822             }
17823             ret.push(c);
17824         });
17825         
17826         return ret;    
17827     },
17828     
17829 //    findBestRow: function(cells)
17830 //    {
17831 //        var ret = 0;
17832 //        
17833 //        for (var i =0 ; i < cells.length;i++) {
17834 //            ret  = Math.max(cells[i].rows || 0,ret);
17835 //        }
17836 //        return ret;
17837 //        
17838 //    },
17839     
17840     
17841     addItem : function(ev)
17842     {
17843         // look for vertical location slot in
17844         var cells = this.findCells(ev);
17845         
17846 //        ev.row = this.findBestRow(cells);
17847         
17848         // work out the location.
17849         
17850         var crow = false;
17851         var rows = [];
17852         for(var i =0; i < cells.length; i++) {
17853             
17854             cells[i].row = cells[0].row;
17855             
17856             if(i == 0){
17857                 cells[i].row = cells[i].row + 1;
17858             }
17859             
17860             if (!crow) {
17861                 crow = {
17862                     start : cells[i],
17863                     end :  cells[i]
17864                 };
17865                 continue;
17866             }
17867             if (crow.start.getY() == cells[i].getY()) {
17868                 // on same row.
17869                 crow.end = cells[i];
17870                 continue;
17871             }
17872             // different row.
17873             rows.push(crow);
17874             crow = {
17875                 start: cells[i],
17876                 end : cells[i]
17877             };
17878             
17879         }
17880         
17881         rows.push(crow);
17882         ev.els = [];
17883         ev.rows = rows;
17884         ev.cells = cells;
17885         
17886         cells[0].events.push(ev);
17887         
17888         this.calevents.push(ev);
17889     },
17890     
17891     clearEvents: function() {
17892         
17893         if(!this.calevents){
17894             return;
17895         }
17896         
17897         Roo.each(this.cells.elements, function(c){
17898             c.row = 0;
17899             c.events = [];
17900             c.more = [];
17901         });
17902         
17903         Roo.each(this.calevents, function(e) {
17904             Roo.each(e.els, function(el) {
17905                 el.un('mouseenter' ,this.onEventEnter, this);
17906                 el.un('mouseleave' ,this.onEventLeave, this);
17907                 el.remove();
17908             },this);
17909         },this);
17910         
17911         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17912             e.remove();
17913         });
17914         
17915     },
17916     
17917     renderEvents: function()
17918     {   
17919         var _this = this;
17920         
17921         this.cells.each(function(c) {
17922             
17923             if(c.row < 5){
17924                 return;
17925             }
17926             
17927             var ev = c.events;
17928             
17929             var r = 4;
17930             if(c.row != c.events.length){
17931                 r = 4 - (4 - (c.row - c.events.length));
17932             }
17933             
17934             c.events = ev.slice(0, r);
17935             c.more = ev.slice(r);
17936             
17937             if(c.more.length && c.more.length == 1){
17938                 c.events.push(c.more.pop());
17939             }
17940             
17941             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17942             
17943         });
17944             
17945         this.cells.each(function(c) {
17946             
17947             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17948             
17949             
17950             for (var e = 0; e < c.events.length; e++){
17951                 var ev = c.events[e];
17952                 var rows = ev.rows;
17953                 
17954                 for(var i = 0; i < rows.length; i++) {
17955                 
17956                     // how many rows should it span..
17957
17958                     var  cfg = {
17959                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17960                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17961
17962                         unselectable : "on",
17963                         cn : [
17964                             {
17965                                 cls: 'fc-event-inner',
17966                                 cn : [
17967     //                                {
17968     //                                  tag:'span',
17969     //                                  cls: 'fc-event-time',
17970     //                                  html : cells.length > 1 ? '' : ev.time
17971     //                                },
17972                                     {
17973                                       tag:'span',
17974                                       cls: 'fc-event-title',
17975                                       html : String.format('{0}', ev.title)
17976                                     }
17977
17978
17979                                 ]
17980                             },
17981                             {
17982                                 cls: 'ui-resizable-handle ui-resizable-e',
17983                                 html : '&nbsp;&nbsp;&nbsp'
17984                             }
17985
17986                         ]
17987                     };
17988
17989                     if (i == 0) {
17990                         cfg.cls += ' fc-event-start';
17991                     }
17992                     if ((i+1) == rows.length) {
17993                         cfg.cls += ' fc-event-end';
17994                     }
17995
17996                     var ctr = _this.el.select('.fc-event-container',true).first();
17997                     var cg = ctr.createChild(cfg);
17998
17999                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18000                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18001
18002                     var r = (c.more.length) ? 1 : 0;
18003                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18004                     cg.setWidth(ebox.right - sbox.x -2);
18005
18006                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18007                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18008                     cg.on('click', _this.onEventClick, _this, ev);
18009
18010                     ev.els.push(cg);
18011                     
18012                 }
18013                 
18014             }
18015             
18016             
18017             if(c.more.length){
18018                 var  cfg = {
18019                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18020                     style : 'position: absolute',
18021                     unselectable : "on",
18022                     cn : [
18023                         {
18024                             cls: 'fc-event-inner',
18025                             cn : [
18026                                 {
18027                                   tag:'span',
18028                                   cls: 'fc-event-title',
18029                                   html : 'More'
18030                                 }
18031
18032
18033                             ]
18034                         },
18035                         {
18036                             cls: 'ui-resizable-handle ui-resizable-e',
18037                             html : '&nbsp;&nbsp;&nbsp'
18038                         }
18039
18040                     ]
18041                 };
18042
18043                 var ctr = _this.el.select('.fc-event-container',true).first();
18044                 var cg = ctr.createChild(cfg);
18045
18046                 var sbox = c.select('.fc-day-content',true).first().getBox();
18047                 var ebox = c.select('.fc-day-content',true).first().getBox();
18048                 //Roo.log(cg);
18049                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18050                 cg.setWidth(ebox.right - sbox.x -2);
18051
18052                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18053                 
18054             }
18055             
18056         });
18057         
18058         
18059         
18060     },
18061     
18062     onEventEnter: function (e, el,event,d) {
18063         this.fireEvent('evententer', this, el, event);
18064     },
18065     
18066     onEventLeave: function (e, el,event,d) {
18067         this.fireEvent('eventleave', this, el, event);
18068     },
18069     
18070     onEventClick: function (e, el,event,d) {
18071         this.fireEvent('eventclick', this, el, event);
18072     },
18073     
18074     onMonthChange: function () {
18075         this.store.load();
18076     },
18077     
18078     onMoreEventClick: function(e, el, more)
18079     {
18080         var _this = this;
18081         
18082         this.calpopover.placement = 'right';
18083         this.calpopover.setTitle('More');
18084         
18085         this.calpopover.setContent('');
18086         
18087         var ctr = this.calpopover.el.select('.popover-content', true).first();
18088         
18089         Roo.each(more, function(m){
18090             var cfg = {
18091                 cls : 'fc-event-hori fc-event-draggable',
18092                 html : m.title
18093             };
18094             var cg = ctr.createChild(cfg);
18095             
18096             cg.on('click', _this.onEventClick, _this, m);
18097         });
18098         
18099         this.calpopover.show(el);
18100         
18101         
18102     },
18103     
18104     onLoad: function () 
18105     {   
18106         this.calevents = [];
18107         var cal = this;
18108         
18109         if(this.store.getCount() > 0){
18110             this.store.data.each(function(d){
18111                cal.addItem({
18112                     id : d.data.id,
18113                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18114                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18115                     time : d.data.start_time,
18116                     title : d.data.title,
18117                     description : d.data.description,
18118                     venue : d.data.venue
18119                 });
18120             });
18121         }
18122         
18123         this.renderEvents();
18124         
18125         if(this.calevents.length && this.loadMask){
18126             this.maskEl.hide();
18127         }
18128     },
18129     
18130     onBeforeLoad: function()
18131     {
18132         this.clearEvents();
18133         if(this.loadMask){
18134             this.maskEl.show();
18135         }
18136     }
18137 });
18138
18139  
18140  /*
18141  * - LGPL
18142  *
18143  * element
18144  * 
18145  */
18146
18147 /**
18148  * @class Roo.bootstrap.Popover
18149  * @extends Roo.bootstrap.Component
18150  * Bootstrap Popover class
18151  * @cfg {String} html contents of the popover   (or false to use children..)
18152  * @cfg {String} title of popover (or false to hide)
18153  * @cfg {String} placement how it is placed
18154  * @cfg {String} trigger click || hover (or false to trigger manually)
18155  * @cfg {String} over what (parent or false to trigger manually.)
18156  * @cfg {Number} delay - delay before showing
18157  
18158  * @constructor
18159  * Create a new Popover
18160  * @param {Object} config The config object
18161  */
18162
18163 Roo.bootstrap.Popover = function(config){
18164     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18165     
18166     this.addEvents({
18167         // raw events
18168          /**
18169          * @event show
18170          * After the popover show
18171          * 
18172          * @param {Roo.bootstrap.Popover} this
18173          */
18174         "show" : true,
18175         /**
18176          * @event hide
18177          * After the popover hide
18178          * 
18179          * @param {Roo.bootstrap.Popover} this
18180          */
18181         "hide" : true
18182     });
18183 };
18184
18185 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18186     
18187     title: 'Fill in a title',
18188     html: false,
18189     
18190     placement : 'right',
18191     trigger : 'hover', // hover
18192     
18193     delay : 0,
18194     
18195     over: 'parent',
18196     
18197     can_build_overlaid : false,
18198     
18199     getChildContainer : function()
18200     {
18201         return this.el.select('.popover-content',true).first();
18202     },
18203     
18204     getAutoCreate : function(){
18205          
18206         var cfg = {
18207            cls : 'popover roo-dynamic',
18208            style: 'display:block',
18209            cn : [
18210                 {
18211                     cls : 'arrow'
18212                 },
18213                 {
18214                     cls : 'popover-inner',
18215                     cn : [
18216                         {
18217                             tag: 'h3',
18218                             cls: 'popover-title popover-header',
18219                             html : this.title
18220                         },
18221                         {
18222                             cls : 'popover-content popover-body',
18223                             html : this.html
18224                         }
18225                     ]
18226                     
18227                 }
18228            ]
18229         };
18230         
18231         return cfg;
18232     },
18233     setTitle: function(str)
18234     {
18235         this.title = str;
18236         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18237     },
18238     setContent: function(str)
18239     {
18240         this.html = str;
18241         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18242     },
18243     // as it get's added to the bottom of the page.
18244     onRender : function(ct, position)
18245     {
18246         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18247         if(!this.el){
18248             var cfg = Roo.apply({},  this.getAutoCreate());
18249             cfg.id = Roo.id();
18250             
18251             if (this.cls) {
18252                 cfg.cls += ' ' + this.cls;
18253             }
18254             if (this.style) {
18255                 cfg.style = this.style;
18256             }
18257             //Roo.log("adding to ");
18258             this.el = Roo.get(document.body).createChild(cfg, position);
18259 //            Roo.log(this.el);
18260         }
18261         this.initEvents();
18262     },
18263     
18264     initEvents : function()
18265     {
18266         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18267         this.el.enableDisplayMode('block');
18268         this.el.hide();
18269         if (this.over === false) {
18270             return; 
18271         }
18272         if (this.triggers === false) {
18273             return;
18274         }
18275         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18276         var triggers = this.trigger ? this.trigger.split(' ') : [];
18277         Roo.each(triggers, function(trigger) {
18278         
18279             if (trigger == 'click') {
18280                 on_el.on('click', this.toggle, this);
18281             } else if (trigger != 'manual') {
18282                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18283                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18284       
18285                 on_el.on(eventIn  ,this.enter, this);
18286                 on_el.on(eventOut, this.leave, this);
18287             }
18288         }, this);
18289         
18290     },
18291     
18292     
18293     // private
18294     timeout : null,
18295     hoverState : null,
18296     
18297     toggle : function () {
18298         this.hoverState == 'in' ? this.leave() : this.enter();
18299     },
18300     
18301     enter : function () {
18302         
18303         clearTimeout(this.timeout);
18304     
18305         this.hoverState = 'in';
18306     
18307         if (!this.delay || !this.delay.show) {
18308             this.show();
18309             return;
18310         }
18311         var _t = this;
18312         this.timeout = setTimeout(function () {
18313             if (_t.hoverState == 'in') {
18314                 _t.show();
18315             }
18316         }, this.delay.show)
18317     },
18318     
18319     leave : function() {
18320         clearTimeout(this.timeout);
18321     
18322         this.hoverState = 'out';
18323     
18324         if (!this.delay || !this.delay.hide) {
18325             this.hide();
18326             return;
18327         }
18328         var _t = this;
18329         this.timeout = setTimeout(function () {
18330             if (_t.hoverState == 'out') {
18331                 _t.hide();
18332             }
18333         }, this.delay.hide)
18334     },
18335     
18336     show : function (on_el)
18337     {
18338         if (!on_el) {
18339             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18340         }
18341         
18342         // set content.
18343         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18344         if (this.html !== false) {
18345             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18346         }
18347         this.el.removeClass([
18348             'fade','top','bottom', 'left', 'right','in',
18349             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18350         ]);
18351         if (!this.title.length) {
18352             this.el.select('.popover-title',true).hide();
18353         }
18354         
18355         var placement = typeof this.placement == 'function' ?
18356             this.placement.call(this, this.el, on_el) :
18357             this.placement;
18358             
18359         var autoToken = /\s?auto?\s?/i;
18360         var autoPlace = autoToken.test(placement);
18361         if (autoPlace) {
18362             placement = placement.replace(autoToken, '') || 'top';
18363         }
18364         
18365         //this.el.detach()
18366         //this.el.setXY([0,0]);
18367         this.el.show();
18368         this.el.dom.style.display='block';
18369         this.el.addClass(placement);
18370         
18371         //this.el.appendTo(on_el);
18372         
18373         var p = this.getPosition();
18374         var box = this.el.getBox();
18375         
18376         if (autoPlace) {
18377             // fixme..
18378         }
18379         var align = Roo.bootstrap.Popover.alignment[placement];
18380         
18381 //        Roo.log(align);
18382         this.el.alignTo(on_el, align[0],align[1]);
18383         //var arrow = this.el.select('.arrow',true).first();
18384         //arrow.set(align[2], 
18385         
18386         this.el.addClass('in');
18387         
18388         
18389         if (this.el.hasClass('fade')) {
18390             // fade it?
18391         }
18392         
18393         this.hoverState = 'in';
18394         
18395         this.fireEvent('show', this);
18396         
18397     },
18398     hide : function()
18399     {
18400         this.el.setXY([0,0]);
18401         this.el.removeClass('in');
18402         this.el.hide();
18403         this.hoverState = null;
18404         
18405         this.fireEvent('hide', this);
18406     }
18407     
18408 });
18409
18410 Roo.bootstrap.Popover.alignment = {
18411     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18412     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18413     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18414     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18415 };
18416
18417  /*
18418  * - LGPL
18419  *
18420  * Progress
18421  * 
18422  */
18423
18424 /**
18425  * @class Roo.bootstrap.Progress
18426  * @extends Roo.bootstrap.Component
18427  * Bootstrap Progress class
18428  * @cfg {Boolean} striped striped of the progress bar
18429  * @cfg {Boolean} active animated of the progress bar
18430  * 
18431  * 
18432  * @constructor
18433  * Create a new Progress
18434  * @param {Object} config The config object
18435  */
18436
18437 Roo.bootstrap.Progress = function(config){
18438     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18439 };
18440
18441 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18442     
18443     striped : false,
18444     active: false,
18445     
18446     getAutoCreate : function(){
18447         var cfg = {
18448             tag: 'div',
18449             cls: 'progress'
18450         };
18451         
18452         
18453         if(this.striped){
18454             cfg.cls += ' progress-striped';
18455         }
18456       
18457         if(this.active){
18458             cfg.cls += ' active';
18459         }
18460         
18461         
18462         return cfg;
18463     }
18464    
18465 });
18466
18467  
18468
18469  /*
18470  * - LGPL
18471  *
18472  * ProgressBar
18473  * 
18474  */
18475
18476 /**
18477  * @class Roo.bootstrap.ProgressBar
18478  * @extends Roo.bootstrap.Component
18479  * Bootstrap ProgressBar class
18480  * @cfg {Number} aria_valuenow aria-value now
18481  * @cfg {Number} aria_valuemin aria-value min
18482  * @cfg {Number} aria_valuemax aria-value max
18483  * @cfg {String} label label for the progress bar
18484  * @cfg {String} panel (success | info | warning | danger )
18485  * @cfg {String} role role of the progress bar
18486  * @cfg {String} sr_only text
18487  * 
18488  * 
18489  * @constructor
18490  * Create a new ProgressBar
18491  * @param {Object} config The config object
18492  */
18493
18494 Roo.bootstrap.ProgressBar = function(config){
18495     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18496 };
18497
18498 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18499     
18500     aria_valuenow : 0,
18501     aria_valuemin : 0,
18502     aria_valuemax : 100,
18503     label : false,
18504     panel : false,
18505     role : false,
18506     sr_only: false,
18507     
18508     getAutoCreate : function()
18509     {
18510         
18511         var cfg = {
18512             tag: 'div',
18513             cls: 'progress-bar',
18514             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18515         };
18516         
18517         if(this.sr_only){
18518             cfg.cn = {
18519                 tag: 'span',
18520                 cls: 'sr-only',
18521                 html: this.sr_only
18522             }
18523         }
18524         
18525         if(this.role){
18526             cfg.role = this.role;
18527         }
18528         
18529         if(this.aria_valuenow){
18530             cfg['aria-valuenow'] = this.aria_valuenow;
18531         }
18532         
18533         if(this.aria_valuemin){
18534             cfg['aria-valuemin'] = this.aria_valuemin;
18535         }
18536         
18537         if(this.aria_valuemax){
18538             cfg['aria-valuemax'] = this.aria_valuemax;
18539         }
18540         
18541         if(this.label && !this.sr_only){
18542             cfg.html = this.label;
18543         }
18544         
18545         if(this.panel){
18546             cfg.cls += ' progress-bar-' + this.panel;
18547         }
18548         
18549         return cfg;
18550     },
18551     
18552     update : function(aria_valuenow)
18553     {
18554         this.aria_valuenow = aria_valuenow;
18555         
18556         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18557     }
18558    
18559 });
18560
18561  
18562
18563  /*
18564  * - LGPL
18565  *
18566  * column
18567  * 
18568  */
18569
18570 /**
18571  * @class Roo.bootstrap.TabGroup
18572  * @extends Roo.bootstrap.Column
18573  * Bootstrap Column class
18574  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18575  * @cfg {Boolean} carousel true to make the group behave like a carousel
18576  * @cfg {Boolean} bullets show bullets for the panels
18577  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18578  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18579  * @cfg {Boolean} showarrow (true|false) show arrow default true
18580  * 
18581  * @constructor
18582  * Create a new TabGroup
18583  * @param {Object} config The config object
18584  */
18585
18586 Roo.bootstrap.TabGroup = function(config){
18587     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18588     if (!this.navId) {
18589         this.navId = Roo.id();
18590     }
18591     this.tabs = [];
18592     Roo.bootstrap.TabGroup.register(this);
18593     
18594 };
18595
18596 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18597     
18598     carousel : false,
18599     transition : false,
18600     bullets : 0,
18601     timer : 0,
18602     autoslide : false,
18603     slideFn : false,
18604     slideOnTouch : false,
18605     showarrow : true,
18606     
18607     getAutoCreate : function()
18608     {
18609         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18610         
18611         cfg.cls += ' tab-content';
18612         
18613         if (this.carousel) {
18614             cfg.cls += ' carousel slide';
18615             
18616             cfg.cn = [{
18617                cls : 'carousel-inner',
18618                cn : []
18619             }];
18620         
18621             if(this.bullets  && !Roo.isTouch){
18622                 
18623                 var bullets = {
18624                     cls : 'carousel-bullets',
18625                     cn : []
18626                 };
18627                
18628                 if(this.bullets_cls){
18629                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18630                 }
18631                 
18632                 bullets.cn.push({
18633                     cls : 'clear'
18634                 });
18635                 
18636                 cfg.cn[0].cn.push(bullets);
18637             }
18638             
18639             if(this.showarrow){
18640                 cfg.cn[0].cn.push({
18641                     tag : 'div',
18642                     class : 'carousel-arrow',
18643                     cn : [
18644                         {
18645                             tag : 'div',
18646                             class : 'carousel-prev',
18647                             cn : [
18648                                 {
18649                                     tag : 'i',
18650                                     class : 'fa fa-chevron-left'
18651                                 }
18652                             ]
18653                         },
18654                         {
18655                             tag : 'div',
18656                             class : 'carousel-next',
18657                             cn : [
18658                                 {
18659                                     tag : 'i',
18660                                     class : 'fa fa-chevron-right'
18661                                 }
18662                             ]
18663                         }
18664                     ]
18665                 });
18666             }
18667             
18668         }
18669         
18670         return cfg;
18671     },
18672     
18673     initEvents:  function()
18674     {
18675 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18676 //            this.el.on("touchstart", this.onTouchStart, this);
18677 //        }
18678         
18679         if(this.autoslide){
18680             var _this = this;
18681             
18682             this.slideFn = window.setInterval(function() {
18683                 _this.showPanelNext();
18684             }, this.timer);
18685         }
18686         
18687         if(this.showarrow){
18688             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18689             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18690         }
18691         
18692         
18693     },
18694     
18695 //    onTouchStart : function(e, el, o)
18696 //    {
18697 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18698 //            return;
18699 //        }
18700 //        
18701 //        this.showPanelNext();
18702 //    },
18703     
18704     
18705     getChildContainer : function()
18706     {
18707         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18708     },
18709     
18710     /**
18711     * register a Navigation item
18712     * @param {Roo.bootstrap.NavItem} the navitem to add
18713     */
18714     register : function(item)
18715     {
18716         this.tabs.push( item);
18717         item.navId = this.navId; // not really needed..
18718         this.addBullet();
18719     
18720     },
18721     
18722     getActivePanel : function()
18723     {
18724         var r = false;
18725         Roo.each(this.tabs, function(t) {
18726             if (t.active) {
18727                 r = t;
18728                 return false;
18729             }
18730             return null;
18731         });
18732         return r;
18733         
18734     },
18735     getPanelByName : function(n)
18736     {
18737         var r = false;
18738         Roo.each(this.tabs, function(t) {
18739             if (t.tabId == n) {
18740                 r = t;
18741                 return false;
18742             }
18743             return null;
18744         });
18745         return r;
18746     },
18747     indexOfPanel : function(p)
18748     {
18749         var r = false;
18750         Roo.each(this.tabs, function(t,i) {
18751             if (t.tabId == p.tabId) {
18752                 r = i;
18753                 return false;
18754             }
18755             return null;
18756         });
18757         return r;
18758     },
18759     /**
18760      * show a specific panel
18761      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18762      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18763      */
18764     showPanel : function (pan)
18765     {
18766         if(this.transition || typeof(pan) == 'undefined'){
18767             Roo.log("waiting for the transitionend");
18768             return false;
18769         }
18770         
18771         if (typeof(pan) == 'number') {
18772             pan = this.tabs[pan];
18773         }
18774         
18775         if (typeof(pan) == 'string') {
18776             pan = this.getPanelByName(pan);
18777         }
18778         
18779         var cur = this.getActivePanel();
18780         
18781         if(!pan || !cur){
18782             Roo.log('pan or acitve pan is undefined');
18783             return false;
18784         }
18785         
18786         if (pan.tabId == this.getActivePanel().tabId) {
18787             return true;
18788         }
18789         
18790         if (false === cur.fireEvent('beforedeactivate')) {
18791             return false;
18792         }
18793         
18794         if(this.bullets > 0 && !Roo.isTouch){
18795             this.setActiveBullet(this.indexOfPanel(pan));
18796         }
18797         
18798         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18799             
18800             //class="carousel-item carousel-item-next carousel-item-left"
18801             
18802             this.transition = true;
18803             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18804             var lr = dir == 'next' ? 'left' : 'right';
18805             pan.el.addClass(dir); // or prev
18806             pan.el.addClass('carousel-item-' + dir); // or prev
18807             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18808             cur.el.addClass(lr); // or right
18809             pan.el.addClass(lr);
18810             cur.el.addClass('carousel-item-' +lr); // or right
18811             pan.el.addClass('carousel-item-' +lr);
18812             
18813             
18814             var _this = this;
18815             cur.el.on('transitionend', function() {
18816                 Roo.log("trans end?");
18817                 
18818                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18819                 pan.setActive(true);
18820                 
18821                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18822                 cur.setActive(false);
18823                 
18824                 _this.transition = false;
18825                 
18826             }, this, { single:  true } );
18827             
18828             return true;
18829         }
18830         
18831         cur.setActive(false);
18832         pan.setActive(true);
18833         
18834         return true;
18835         
18836     },
18837     showPanelNext : function()
18838     {
18839         var i = this.indexOfPanel(this.getActivePanel());
18840         
18841         if (i >= this.tabs.length - 1 && !this.autoslide) {
18842             return;
18843         }
18844         
18845         if (i >= this.tabs.length - 1 && this.autoslide) {
18846             i = -1;
18847         }
18848         
18849         this.showPanel(this.tabs[i+1]);
18850     },
18851     
18852     showPanelPrev : function()
18853     {
18854         var i = this.indexOfPanel(this.getActivePanel());
18855         
18856         if (i  < 1 && !this.autoslide) {
18857             return;
18858         }
18859         
18860         if (i < 1 && this.autoslide) {
18861             i = this.tabs.length;
18862         }
18863         
18864         this.showPanel(this.tabs[i-1]);
18865     },
18866     
18867     
18868     addBullet: function()
18869     {
18870         if(!this.bullets || Roo.isTouch){
18871             return;
18872         }
18873         var ctr = this.el.select('.carousel-bullets',true).first();
18874         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18875         var bullet = ctr.createChild({
18876             cls : 'bullet bullet-' + i
18877         },ctr.dom.lastChild);
18878         
18879         
18880         var _this = this;
18881         
18882         bullet.on('click', (function(e, el, o, ii, t){
18883
18884             e.preventDefault();
18885
18886             this.showPanel(ii);
18887
18888             if(this.autoslide && this.slideFn){
18889                 clearInterval(this.slideFn);
18890                 this.slideFn = window.setInterval(function() {
18891                     _this.showPanelNext();
18892                 }, this.timer);
18893             }
18894
18895         }).createDelegate(this, [i, bullet], true));
18896                 
18897         
18898     },
18899      
18900     setActiveBullet : function(i)
18901     {
18902         if(Roo.isTouch){
18903             return;
18904         }
18905         
18906         Roo.each(this.el.select('.bullet', true).elements, function(el){
18907             el.removeClass('selected');
18908         });
18909
18910         var bullet = this.el.select('.bullet-' + i, true).first();
18911         
18912         if(!bullet){
18913             return;
18914         }
18915         
18916         bullet.addClass('selected');
18917     }
18918     
18919     
18920   
18921 });
18922
18923  
18924
18925  
18926  
18927 Roo.apply(Roo.bootstrap.TabGroup, {
18928     
18929     groups: {},
18930      /**
18931     * register a Navigation Group
18932     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18933     */
18934     register : function(navgrp)
18935     {
18936         this.groups[navgrp.navId] = navgrp;
18937         
18938     },
18939     /**
18940     * fetch a Navigation Group based on the navigation ID
18941     * if one does not exist , it will get created.
18942     * @param {string} the navgroup to add
18943     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18944     */
18945     get: function(navId) {
18946         if (typeof(this.groups[navId]) == 'undefined') {
18947             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18948         }
18949         return this.groups[navId] ;
18950     }
18951     
18952     
18953     
18954 });
18955
18956  /*
18957  * - LGPL
18958  *
18959  * TabPanel
18960  * 
18961  */
18962
18963 /**
18964  * @class Roo.bootstrap.TabPanel
18965  * @extends Roo.bootstrap.Component
18966  * Bootstrap TabPanel class
18967  * @cfg {Boolean} active panel active
18968  * @cfg {String} html panel content
18969  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18970  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18971  * @cfg {String} href click to link..
18972  * 
18973  * 
18974  * @constructor
18975  * Create a new TabPanel
18976  * @param {Object} config The config object
18977  */
18978
18979 Roo.bootstrap.TabPanel = function(config){
18980     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18981     this.addEvents({
18982         /**
18983              * @event changed
18984              * Fires when the active status changes
18985              * @param {Roo.bootstrap.TabPanel} this
18986              * @param {Boolean} state the new state
18987             
18988          */
18989         'changed': true,
18990         /**
18991              * @event beforedeactivate
18992              * Fires before a tab is de-activated - can be used to do validation on a form.
18993              * @param {Roo.bootstrap.TabPanel} this
18994              * @return {Boolean} false if there is an error
18995             
18996          */
18997         'beforedeactivate': true
18998      });
18999     
19000     this.tabId = this.tabId || Roo.id();
19001   
19002 };
19003
19004 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19005     
19006     active: false,
19007     html: false,
19008     tabId: false,
19009     navId : false,
19010     href : '',
19011     
19012     getAutoCreate : function(){
19013         
19014         
19015         var cfg = {
19016             tag: 'div',
19017             // item is needed for carousel - not sure if it has any effect otherwise
19018             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19019             html: this.html || ''
19020         };
19021         
19022         if(this.active){
19023             cfg.cls += ' active';
19024         }
19025         
19026         if(this.tabId){
19027             cfg.tabId = this.tabId;
19028         }
19029         
19030         
19031         
19032         return cfg;
19033     },
19034     
19035     initEvents:  function()
19036     {
19037         var p = this.parent();
19038         
19039         this.navId = this.navId || p.navId;
19040         
19041         if (typeof(this.navId) != 'undefined') {
19042             // not really needed.. but just in case.. parent should be a NavGroup.
19043             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19044             
19045             tg.register(this);
19046             
19047             var i = tg.tabs.length - 1;
19048             
19049             if(this.active && tg.bullets > 0 && i < tg.bullets){
19050                 tg.setActiveBullet(i);
19051             }
19052         }
19053         
19054         this.el.on('click', this.onClick, this);
19055         
19056         if(Roo.isTouch){
19057             this.el.on("touchstart", this.onTouchStart, this);
19058             this.el.on("touchmove", this.onTouchMove, this);
19059             this.el.on("touchend", this.onTouchEnd, this);
19060         }
19061         
19062     },
19063     
19064     onRender : function(ct, position)
19065     {
19066         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19067     },
19068     
19069     setActive : function(state)
19070     {
19071         Roo.log("panel - set active " + this.tabId + "=" + state);
19072         
19073         this.active = state;
19074         if (!state) {
19075             this.el.removeClass('active');
19076             
19077         } else  if (!this.el.hasClass('active')) {
19078             this.el.addClass('active');
19079         }
19080         
19081         this.fireEvent('changed', this, state);
19082     },
19083     
19084     onClick : function(e)
19085     {
19086         e.preventDefault();
19087         
19088         if(!this.href.length){
19089             return;
19090         }
19091         
19092         window.location.href = this.href;
19093     },
19094     
19095     startX : 0,
19096     startY : 0,
19097     endX : 0,
19098     endY : 0,
19099     swiping : false,
19100     
19101     onTouchStart : function(e)
19102     {
19103         this.swiping = false;
19104         
19105         this.startX = e.browserEvent.touches[0].clientX;
19106         this.startY = e.browserEvent.touches[0].clientY;
19107     },
19108     
19109     onTouchMove : function(e)
19110     {
19111         this.swiping = true;
19112         
19113         this.endX = e.browserEvent.touches[0].clientX;
19114         this.endY = e.browserEvent.touches[0].clientY;
19115     },
19116     
19117     onTouchEnd : function(e)
19118     {
19119         if(!this.swiping){
19120             this.onClick(e);
19121             return;
19122         }
19123         
19124         var tabGroup = this.parent();
19125         
19126         if(this.endX > this.startX){ // swiping right
19127             tabGroup.showPanelPrev();
19128             return;
19129         }
19130         
19131         if(this.startX > this.endX){ // swiping left
19132             tabGroup.showPanelNext();
19133             return;
19134         }
19135     }
19136     
19137     
19138 });
19139  
19140
19141  
19142
19143  /*
19144  * - LGPL
19145  *
19146  * DateField
19147  * 
19148  */
19149
19150 /**
19151  * @class Roo.bootstrap.DateField
19152  * @extends Roo.bootstrap.Input
19153  * Bootstrap DateField class
19154  * @cfg {Number} weekStart default 0
19155  * @cfg {String} viewMode default empty, (months|years)
19156  * @cfg {String} minViewMode default empty, (months|years)
19157  * @cfg {Number} startDate default -Infinity
19158  * @cfg {Number} endDate default Infinity
19159  * @cfg {Boolean} todayHighlight default false
19160  * @cfg {Boolean} todayBtn default false
19161  * @cfg {Boolean} calendarWeeks default false
19162  * @cfg {Object} daysOfWeekDisabled default empty
19163  * @cfg {Boolean} singleMode default false (true | false)
19164  * 
19165  * @cfg {Boolean} keyboardNavigation default true
19166  * @cfg {String} language default en
19167  * 
19168  * @constructor
19169  * Create a new DateField
19170  * @param {Object} config The config object
19171  */
19172
19173 Roo.bootstrap.DateField = function(config){
19174     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19175      this.addEvents({
19176             /**
19177              * @event show
19178              * Fires when this field show.
19179              * @param {Roo.bootstrap.DateField} this
19180              * @param {Mixed} date The date value
19181              */
19182             show : true,
19183             /**
19184              * @event show
19185              * Fires when this field hide.
19186              * @param {Roo.bootstrap.DateField} this
19187              * @param {Mixed} date The date value
19188              */
19189             hide : true,
19190             /**
19191              * @event select
19192              * Fires when select a date.
19193              * @param {Roo.bootstrap.DateField} this
19194              * @param {Mixed} date The date value
19195              */
19196             select : true,
19197             /**
19198              * @event beforeselect
19199              * Fires when before select a date.
19200              * @param {Roo.bootstrap.DateField} this
19201              * @param {Mixed} date The date value
19202              */
19203             beforeselect : true
19204         });
19205 };
19206
19207 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19208     
19209     /**
19210      * @cfg {String} format
19211      * The default date format string which can be overriden for localization support.  The format must be
19212      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19213      */
19214     format : "m/d/y",
19215     /**
19216      * @cfg {String} altFormats
19217      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19218      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19219      */
19220     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19221     
19222     weekStart : 0,
19223     
19224     viewMode : '',
19225     
19226     minViewMode : '',
19227     
19228     todayHighlight : false,
19229     
19230     todayBtn: false,
19231     
19232     language: 'en',
19233     
19234     keyboardNavigation: true,
19235     
19236     calendarWeeks: false,
19237     
19238     startDate: -Infinity,
19239     
19240     endDate: Infinity,
19241     
19242     daysOfWeekDisabled: [],
19243     
19244     _events: [],
19245     
19246     singleMode : false,
19247     
19248     UTCDate: function()
19249     {
19250         return new Date(Date.UTC.apply(Date, arguments));
19251     },
19252     
19253     UTCToday: function()
19254     {
19255         var today = new Date();
19256         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19257     },
19258     
19259     getDate: function() {
19260             var d = this.getUTCDate();
19261             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19262     },
19263     
19264     getUTCDate: function() {
19265             return this.date;
19266     },
19267     
19268     setDate: function(d) {
19269             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19270     },
19271     
19272     setUTCDate: function(d) {
19273             this.date = d;
19274             this.setValue(this.formatDate(this.date));
19275     },
19276         
19277     onRender: function(ct, position)
19278     {
19279         
19280         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19281         
19282         this.language = this.language || 'en';
19283         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19284         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19285         
19286         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19287         this.format = this.format || 'm/d/y';
19288         this.isInline = false;
19289         this.isInput = true;
19290         this.component = this.el.select('.add-on', true).first() || false;
19291         this.component = (this.component && this.component.length === 0) ? false : this.component;
19292         this.hasInput = this.component && this.inputEl().length;
19293         
19294         if (typeof(this.minViewMode === 'string')) {
19295             switch (this.minViewMode) {
19296                 case 'months':
19297                     this.minViewMode = 1;
19298                     break;
19299                 case 'years':
19300                     this.minViewMode = 2;
19301                     break;
19302                 default:
19303                     this.minViewMode = 0;
19304                     break;
19305             }
19306         }
19307         
19308         if (typeof(this.viewMode === 'string')) {
19309             switch (this.viewMode) {
19310                 case 'months':
19311                     this.viewMode = 1;
19312                     break;
19313                 case 'years':
19314                     this.viewMode = 2;
19315                     break;
19316                 default:
19317                     this.viewMode = 0;
19318                     break;
19319             }
19320         }
19321                 
19322         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19323         
19324 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19325         
19326         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327         
19328         this.picker().on('mousedown', this.onMousedown, this);
19329         this.picker().on('click', this.onClick, this);
19330         
19331         this.picker().addClass('datepicker-dropdown');
19332         
19333         this.startViewMode = this.viewMode;
19334         
19335         if(this.singleMode){
19336             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19337                 v.setVisibilityMode(Roo.Element.DISPLAY);
19338                 v.hide();
19339             });
19340             
19341             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19342                 v.setStyle('width', '189px');
19343             });
19344         }
19345         
19346         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19347             if(!this.calendarWeeks){
19348                 v.remove();
19349                 return;
19350             }
19351             
19352             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19353             v.attr('colspan', function(i, val){
19354                 return parseInt(val) + 1;
19355             });
19356         });
19357                         
19358         
19359         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19360         
19361         this.setStartDate(this.startDate);
19362         this.setEndDate(this.endDate);
19363         
19364         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19365         
19366         this.fillDow();
19367         this.fillMonths();
19368         this.update();
19369         this.showMode();
19370         
19371         if(this.isInline) {
19372             this.showPopup();
19373         }
19374     },
19375     
19376     picker : function()
19377     {
19378         return this.pickerEl;
19379 //        return this.el.select('.datepicker', true).first();
19380     },
19381     
19382     fillDow: function()
19383     {
19384         var dowCnt = this.weekStart;
19385         
19386         var dow = {
19387             tag: 'tr',
19388             cn: [
19389                 
19390             ]
19391         };
19392         
19393         if(this.calendarWeeks){
19394             dow.cn.push({
19395                 tag: 'th',
19396                 cls: 'cw',
19397                 html: '&nbsp;'
19398             })
19399         }
19400         
19401         while (dowCnt < this.weekStart + 7) {
19402             dow.cn.push({
19403                 tag: 'th',
19404                 cls: 'dow',
19405                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19406             });
19407         }
19408         
19409         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19410     },
19411     
19412     fillMonths: function()
19413     {    
19414         var i = 0;
19415         var months = this.picker().select('>.datepicker-months td', true).first();
19416         
19417         months.dom.innerHTML = '';
19418         
19419         while (i < 12) {
19420             var month = {
19421                 tag: 'span',
19422                 cls: 'month',
19423                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19424             };
19425             
19426             months.createChild(month);
19427         }
19428         
19429     },
19430     
19431     update: function()
19432     {
19433         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;
19434         
19435         if (this.date < this.startDate) {
19436             this.viewDate = new Date(this.startDate);
19437         } else if (this.date > this.endDate) {
19438             this.viewDate = new Date(this.endDate);
19439         } else {
19440             this.viewDate = new Date(this.date);
19441         }
19442         
19443         this.fill();
19444     },
19445     
19446     fill: function() 
19447     {
19448         var d = new Date(this.viewDate),
19449                 year = d.getUTCFullYear(),
19450                 month = d.getUTCMonth(),
19451                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19452                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19453                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19454                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19455                 currentDate = this.date && this.date.valueOf(),
19456                 today = this.UTCToday();
19457         
19458         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19459         
19460 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19461         
19462 //        this.picker.select('>tfoot th.today').
19463 //                                              .text(dates[this.language].today)
19464 //                                              .toggle(this.todayBtn !== false);
19465     
19466         this.updateNavArrows();
19467         this.fillMonths();
19468                                                 
19469         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19470         
19471         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19472          
19473         prevMonth.setUTCDate(day);
19474         
19475         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19476         
19477         var nextMonth = new Date(prevMonth);
19478         
19479         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19480         
19481         nextMonth = nextMonth.valueOf();
19482         
19483         var fillMonths = false;
19484         
19485         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19486         
19487         while(prevMonth.valueOf() <= nextMonth) {
19488             var clsName = '';
19489             
19490             if (prevMonth.getUTCDay() === this.weekStart) {
19491                 if(fillMonths){
19492                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19493                 }
19494                     
19495                 fillMonths = {
19496                     tag: 'tr',
19497                     cn: []
19498                 };
19499                 
19500                 if(this.calendarWeeks){
19501                     // ISO 8601: First week contains first thursday.
19502                     // ISO also states week starts on Monday, but we can be more abstract here.
19503                     var
19504                     // Start of current week: based on weekstart/current date
19505                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19506                     // Thursday of this week
19507                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19508                     // First Thursday of year, year from thursday
19509                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19510                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19511                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19512                     
19513                     fillMonths.cn.push({
19514                         tag: 'td',
19515                         cls: 'cw',
19516                         html: calWeek
19517                     });
19518                 }
19519             }
19520             
19521             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19522                 clsName += ' old';
19523             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19524                 clsName += ' new';
19525             }
19526             if (this.todayHighlight &&
19527                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19528                 prevMonth.getUTCMonth() == today.getMonth() &&
19529                 prevMonth.getUTCDate() == today.getDate()) {
19530                 clsName += ' today';
19531             }
19532             
19533             if (currentDate && prevMonth.valueOf() === currentDate) {
19534                 clsName += ' active';
19535             }
19536             
19537             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19538                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19539                     clsName += ' disabled';
19540             }
19541             
19542             fillMonths.cn.push({
19543                 tag: 'td',
19544                 cls: 'day ' + clsName,
19545                 html: prevMonth.getDate()
19546             });
19547             
19548             prevMonth.setDate(prevMonth.getDate()+1);
19549         }
19550           
19551         var currentYear = this.date && this.date.getUTCFullYear();
19552         var currentMonth = this.date && this.date.getUTCMonth();
19553         
19554         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19555         
19556         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19557             v.removeClass('active');
19558             
19559             if(currentYear === year && k === currentMonth){
19560                 v.addClass('active');
19561             }
19562             
19563             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19564                 v.addClass('disabled');
19565             }
19566             
19567         });
19568         
19569         
19570         year = parseInt(year/10, 10) * 10;
19571         
19572         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19573         
19574         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19575         
19576         year -= 1;
19577         for (var i = -1; i < 11; i++) {
19578             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19579                 tag: 'span',
19580                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19581                 html: year
19582             });
19583             
19584             year += 1;
19585         }
19586     },
19587     
19588     showMode: function(dir) 
19589     {
19590         if (dir) {
19591             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19592         }
19593         
19594         Roo.each(this.picker().select('>div',true).elements, function(v){
19595             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19596             v.hide();
19597         });
19598         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19599     },
19600     
19601     place: function()
19602     {
19603         if(this.isInline) {
19604             return;
19605         }
19606         
19607         this.picker().removeClass(['bottom', 'top']);
19608         
19609         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19610             /*
19611              * place to the top of element!
19612              *
19613              */
19614             
19615             this.picker().addClass('top');
19616             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19617             
19618             return;
19619         }
19620         
19621         this.picker().addClass('bottom');
19622         
19623         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19624     },
19625     
19626     parseDate : function(value)
19627     {
19628         if(!value || value instanceof Date){
19629             return value;
19630         }
19631         var v = Date.parseDate(value, this.format);
19632         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19633             v = Date.parseDate(value, 'Y-m-d');
19634         }
19635         if(!v && this.altFormats){
19636             if(!this.altFormatsArray){
19637                 this.altFormatsArray = this.altFormats.split("|");
19638             }
19639             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19640                 v = Date.parseDate(value, this.altFormatsArray[i]);
19641             }
19642         }
19643         return v;
19644     },
19645     
19646     formatDate : function(date, fmt)
19647     {   
19648         return (!date || !(date instanceof Date)) ?
19649         date : date.dateFormat(fmt || this.format);
19650     },
19651     
19652     onFocus : function()
19653     {
19654         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19655         this.showPopup();
19656     },
19657     
19658     onBlur : function()
19659     {
19660         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19661         
19662         var d = this.inputEl().getValue();
19663         
19664         this.setValue(d);
19665                 
19666         this.hidePopup();
19667     },
19668     
19669     showPopup : function()
19670     {
19671         this.picker().show();
19672         this.update();
19673         this.place();
19674         
19675         this.fireEvent('showpopup', this, this.date);
19676     },
19677     
19678     hidePopup : function()
19679     {
19680         if(this.isInline) {
19681             return;
19682         }
19683         this.picker().hide();
19684         this.viewMode = this.startViewMode;
19685         this.showMode();
19686         
19687         this.fireEvent('hidepopup', this, this.date);
19688         
19689     },
19690     
19691     onMousedown: function(e)
19692     {
19693         e.stopPropagation();
19694         e.preventDefault();
19695     },
19696     
19697     keyup: function(e)
19698     {
19699         Roo.bootstrap.DateField.superclass.keyup.call(this);
19700         this.update();
19701     },
19702
19703     setValue: function(v)
19704     {
19705         if(this.fireEvent('beforeselect', this, v) !== false){
19706             var d = new Date(this.parseDate(v) ).clearTime();
19707         
19708             if(isNaN(d.getTime())){
19709                 this.date = this.viewDate = '';
19710                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19711                 return;
19712             }
19713
19714             v = this.formatDate(d);
19715
19716             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19717
19718             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19719
19720             this.update();
19721
19722             this.fireEvent('select', this, this.date);
19723         }
19724     },
19725     
19726     getValue: function()
19727     {
19728         return this.formatDate(this.date);
19729     },
19730     
19731     fireKey: function(e)
19732     {
19733         if (!this.picker().isVisible()){
19734             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19735                 this.showPopup();
19736             }
19737             return;
19738         }
19739         
19740         var dateChanged = false,
19741         dir, day, month,
19742         newDate, newViewDate;
19743         
19744         switch(e.keyCode){
19745             case 27: // escape
19746                 this.hidePopup();
19747                 e.preventDefault();
19748                 break;
19749             case 37: // left
19750             case 39: // right
19751                 if (!this.keyboardNavigation) {
19752                     break;
19753                 }
19754                 dir = e.keyCode == 37 ? -1 : 1;
19755                 
19756                 if (e.ctrlKey){
19757                     newDate = this.moveYear(this.date, dir);
19758                     newViewDate = this.moveYear(this.viewDate, dir);
19759                 } else if (e.shiftKey){
19760                     newDate = this.moveMonth(this.date, dir);
19761                     newViewDate = this.moveMonth(this.viewDate, dir);
19762                 } else {
19763                     newDate = new Date(this.date);
19764                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19765                     newViewDate = new Date(this.viewDate);
19766                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19767                 }
19768                 if (this.dateWithinRange(newDate)){
19769                     this.date = newDate;
19770                     this.viewDate = newViewDate;
19771                     this.setValue(this.formatDate(this.date));
19772 //                    this.update();
19773                     e.preventDefault();
19774                     dateChanged = true;
19775                 }
19776                 break;
19777             case 38: // up
19778             case 40: // down
19779                 if (!this.keyboardNavigation) {
19780                     break;
19781                 }
19782                 dir = e.keyCode == 38 ? -1 : 1;
19783                 if (e.ctrlKey){
19784                     newDate = this.moveYear(this.date, dir);
19785                     newViewDate = this.moveYear(this.viewDate, dir);
19786                 } else if (e.shiftKey){
19787                     newDate = this.moveMonth(this.date, dir);
19788                     newViewDate = this.moveMonth(this.viewDate, dir);
19789                 } else {
19790                     newDate = new Date(this.date);
19791                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19792                     newViewDate = new Date(this.viewDate);
19793                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19794                 }
19795                 if (this.dateWithinRange(newDate)){
19796                     this.date = newDate;
19797                     this.viewDate = newViewDate;
19798                     this.setValue(this.formatDate(this.date));
19799 //                    this.update();
19800                     e.preventDefault();
19801                     dateChanged = true;
19802                 }
19803                 break;
19804             case 13: // enter
19805                 this.setValue(this.formatDate(this.date));
19806                 this.hidePopup();
19807                 e.preventDefault();
19808                 break;
19809             case 9: // tab
19810                 this.setValue(this.formatDate(this.date));
19811                 this.hidePopup();
19812                 break;
19813             case 16: // shift
19814             case 17: // ctrl
19815             case 18: // alt
19816                 break;
19817             default :
19818                 this.hidePopup();
19819                 
19820         }
19821     },
19822     
19823     
19824     onClick: function(e) 
19825     {
19826         e.stopPropagation();
19827         e.preventDefault();
19828         
19829         var target = e.getTarget();
19830         
19831         if(target.nodeName.toLowerCase() === 'i'){
19832             target = Roo.get(target).dom.parentNode;
19833         }
19834         
19835         var nodeName = target.nodeName;
19836         var className = target.className;
19837         var html = target.innerHTML;
19838         //Roo.log(nodeName);
19839         
19840         switch(nodeName.toLowerCase()) {
19841             case 'th':
19842                 switch(className) {
19843                     case 'switch':
19844                         this.showMode(1);
19845                         break;
19846                     case 'prev':
19847                     case 'next':
19848                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19849                         switch(this.viewMode){
19850                                 case 0:
19851                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19852                                         break;
19853                                 case 1:
19854                                 case 2:
19855                                         this.viewDate = this.moveYear(this.viewDate, dir);
19856                                         break;
19857                         }
19858                         this.fill();
19859                         break;
19860                     case 'today':
19861                         var date = new Date();
19862                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19863 //                        this.fill()
19864                         this.setValue(this.formatDate(this.date));
19865                         
19866                         this.hidePopup();
19867                         break;
19868                 }
19869                 break;
19870             case 'span':
19871                 if (className.indexOf('disabled') < 0) {
19872                     this.viewDate.setUTCDate(1);
19873                     if (className.indexOf('month') > -1) {
19874                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19875                     } else {
19876                         var year = parseInt(html, 10) || 0;
19877                         this.viewDate.setUTCFullYear(year);
19878                         
19879                     }
19880                     
19881                     if(this.singleMode){
19882                         this.setValue(this.formatDate(this.viewDate));
19883                         this.hidePopup();
19884                         return;
19885                     }
19886                     
19887                     this.showMode(-1);
19888                     this.fill();
19889                 }
19890                 break;
19891                 
19892             case 'td':
19893                 //Roo.log(className);
19894                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19895                     var day = parseInt(html, 10) || 1;
19896                     var year = this.viewDate.getUTCFullYear(),
19897                         month = this.viewDate.getUTCMonth();
19898
19899                     if (className.indexOf('old') > -1) {
19900                         if(month === 0 ){
19901                             month = 11;
19902                             year -= 1;
19903                         }else{
19904                             month -= 1;
19905                         }
19906                     } else if (className.indexOf('new') > -1) {
19907                         if (month == 11) {
19908                             month = 0;
19909                             year += 1;
19910                         } else {
19911                             month += 1;
19912                         }
19913                     }
19914                     //Roo.log([year,month,day]);
19915                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19916                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19917 //                    this.fill();
19918                     //Roo.log(this.formatDate(this.date));
19919                     this.setValue(this.formatDate(this.date));
19920                     this.hidePopup();
19921                 }
19922                 break;
19923         }
19924     },
19925     
19926     setStartDate: function(startDate)
19927     {
19928         this.startDate = startDate || -Infinity;
19929         if (this.startDate !== -Infinity) {
19930             this.startDate = this.parseDate(this.startDate);
19931         }
19932         this.update();
19933         this.updateNavArrows();
19934     },
19935
19936     setEndDate: function(endDate)
19937     {
19938         this.endDate = endDate || Infinity;
19939         if (this.endDate !== Infinity) {
19940             this.endDate = this.parseDate(this.endDate);
19941         }
19942         this.update();
19943         this.updateNavArrows();
19944     },
19945     
19946     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19947     {
19948         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19949         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19950             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19951         }
19952         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19953             return parseInt(d, 10);
19954         });
19955         this.update();
19956         this.updateNavArrows();
19957     },
19958     
19959     updateNavArrows: function() 
19960     {
19961         if(this.singleMode){
19962             return;
19963         }
19964         
19965         var d = new Date(this.viewDate),
19966         year = d.getUTCFullYear(),
19967         month = d.getUTCMonth();
19968         
19969         Roo.each(this.picker().select('.prev', true).elements, function(v){
19970             v.show();
19971             switch (this.viewMode) {
19972                 case 0:
19973
19974                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19975                         v.hide();
19976                     }
19977                     break;
19978                 case 1:
19979                 case 2:
19980                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19981                         v.hide();
19982                     }
19983                     break;
19984             }
19985         });
19986         
19987         Roo.each(this.picker().select('.next', true).elements, function(v){
19988             v.show();
19989             switch (this.viewMode) {
19990                 case 0:
19991
19992                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19993                         v.hide();
19994                     }
19995                     break;
19996                 case 1:
19997                 case 2:
19998                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19999                         v.hide();
20000                     }
20001                     break;
20002             }
20003         })
20004     },
20005     
20006     moveMonth: function(date, dir)
20007     {
20008         if (!dir) {
20009             return date;
20010         }
20011         var new_date = new Date(date.valueOf()),
20012         day = new_date.getUTCDate(),
20013         month = new_date.getUTCMonth(),
20014         mag = Math.abs(dir),
20015         new_month, test;
20016         dir = dir > 0 ? 1 : -1;
20017         if (mag == 1){
20018             test = dir == -1
20019             // If going back one month, make sure month is not current month
20020             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20021             ? function(){
20022                 return new_date.getUTCMonth() == month;
20023             }
20024             // If going forward one month, make sure month is as expected
20025             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20026             : function(){
20027                 return new_date.getUTCMonth() != new_month;
20028             };
20029             new_month = month + dir;
20030             new_date.setUTCMonth(new_month);
20031             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20032             if (new_month < 0 || new_month > 11) {
20033                 new_month = (new_month + 12) % 12;
20034             }
20035         } else {
20036             // For magnitudes >1, move one month at a time...
20037             for (var i=0; i<mag; i++) {
20038                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20039                 new_date = this.moveMonth(new_date, dir);
20040             }
20041             // ...then reset the day, keeping it in the new month
20042             new_month = new_date.getUTCMonth();
20043             new_date.setUTCDate(day);
20044             test = function(){
20045                 return new_month != new_date.getUTCMonth();
20046             };
20047         }
20048         // Common date-resetting loop -- if date is beyond end of month, make it
20049         // end of month
20050         while (test()){
20051             new_date.setUTCDate(--day);
20052             new_date.setUTCMonth(new_month);
20053         }
20054         return new_date;
20055     },
20056
20057     moveYear: function(date, dir)
20058     {
20059         return this.moveMonth(date, dir*12);
20060     },
20061
20062     dateWithinRange: function(date)
20063     {
20064         return date >= this.startDate && date <= this.endDate;
20065     },
20066
20067     
20068     remove: function() 
20069     {
20070         this.picker().remove();
20071     },
20072     
20073     validateValue : function(value)
20074     {
20075         if(this.getVisibilityEl().hasClass('hidden')){
20076             return true;
20077         }
20078         
20079         if(value.length < 1)  {
20080             if(this.allowBlank){
20081                 return true;
20082             }
20083             return false;
20084         }
20085         
20086         if(value.length < this.minLength){
20087             return false;
20088         }
20089         if(value.length > this.maxLength){
20090             return false;
20091         }
20092         if(this.vtype){
20093             var vt = Roo.form.VTypes;
20094             if(!vt[this.vtype](value, this)){
20095                 return false;
20096             }
20097         }
20098         if(typeof this.validator == "function"){
20099             var msg = this.validator(value);
20100             if(msg !== true){
20101                 return false;
20102             }
20103         }
20104         
20105         if(this.regex && !this.regex.test(value)){
20106             return false;
20107         }
20108         
20109         if(typeof(this.parseDate(value)) == 'undefined'){
20110             return false;
20111         }
20112         
20113         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20114             return false;
20115         }      
20116         
20117         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20118             return false;
20119         } 
20120         
20121         
20122         return true;
20123     },
20124     
20125     reset : function()
20126     {
20127         this.date = this.viewDate = '';
20128         
20129         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20130     }
20131    
20132 });
20133
20134 Roo.apply(Roo.bootstrap.DateField,  {
20135     
20136     head : {
20137         tag: 'thead',
20138         cn: [
20139         {
20140             tag: 'tr',
20141             cn: [
20142             {
20143                 tag: 'th',
20144                 cls: 'prev',
20145                 html: '<i class="fa fa-arrow-left"/>'
20146             },
20147             {
20148                 tag: 'th',
20149                 cls: 'switch',
20150                 colspan: '5'
20151             },
20152             {
20153                 tag: 'th',
20154                 cls: 'next',
20155                 html: '<i class="fa fa-arrow-right"/>'
20156             }
20157
20158             ]
20159         }
20160         ]
20161     },
20162     
20163     content : {
20164         tag: 'tbody',
20165         cn: [
20166         {
20167             tag: 'tr',
20168             cn: [
20169             {
20170                 tag: 'td',
20171                 colspan: '7'
20172             }
20173             ]
20174         }
20175         ]
20176     },
20177     
20178     footer : {
20179         tag: 'tfoot',
20180         cn: [
20181         {
20182             tag: 'tr',
20183             cn: [
20184             {
20185                 tag: 'th',
20186                 colspan: '7',
20187                 cls: 'today'
20188             }
20189                     
20190             ]
20191         }
20192         ]
20193     },
20194     
20195     dates:{
20196         en: {
20197             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20198             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20199             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20200             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20201             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20202             today: "Today"
20203         }
20204     },
20205     
20206     modes: [
20207     {
20208         clsName: 'days',
20209         navFnc: 'Month',
20210         navStep: 1
20211     },
20212     {
20213         clsName: 'months',
20214         navFnc: 'FullYear',
20215         navStep: 1
20216     },
20217     {
20218         clsName: 'years',
20219         navFnc: 'FullYear',
20220         navStep: 10
20221     }]
20222 });
20223
20224 Roo.apply(Roo.bootstrap.DateField,  {
20225   
20226     template : {
20227         tag: 'div',
20228         cls: 'datepicker dropdown-menu roo-dynamic',
20229         cn: [
20230         {
20231             tag: 'div',
20232             cls: 'datepicker-days',
20233             cn: [
20234             {
20235                 tag: 'table',
20236                 cls: 'table-condensed',
20237                 cn:[
20238                 Roo.bootstrap.DateField.head,
20239                 {
20240                     tag: 'tbody'
20241                 },
20242                 Roo.bootstrap.DateField.footer
20243                 ]
20244             }
20245             ]
20246         },
20247         {
20248             tag: 'div',
20249             cls: 'datepicker-months',
20250             cn: [
20251             {
20252                 tag: 'table',
20253                 cls: 'table-condensed',
20254                 cn:[
20255                 Roo.bootstrap.DateField.head,
20256                 Roo.bootstrap.DateField.content,
20257                 Roo.bootstrap.DateField.footer
20258                 ]
20259             }
20260             ]
20261         },
20262         {
20263             tag: 'div',
20264             cls: 'datepicker-years',
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     }
20279 });
20280
20281  
20282
20283  /*
20284  * - LGPL
20285  *
20286  * TimeField
20287  * 
20288  */
20289
20290 /**
20291  * @class Roo.bootstrap.TimeField
20292  * @extends Roo.bootstrap.Input
20293  * Bootstrap DateField class
20294  * 
20295  * 
20296  * @constructor
20297  * Create a new TimeField
20298  * @param {Object} config The config object
20299  */
20300
20301 Roo.bootstrap.TimeField = function(config){
20302     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20303     this.addEvents({
20304             /**
20305              * @event show
20306              * Fires when this field show.
20307              * @param {Roo.bootstrap.DateField} thisthis
20308              * @param {Mixed} date The date value
20309              */
20310             show : true,
20311             /**
20312              * @event show
20313              * Fires when this field hide.
20314              * @param {Roo.bootstrap.DateField} this
20315              * @param {Mixed} date The date value
20316              */
20317             hide : true,
20318             /**
20319              * @event select
20320              * Fires when select a date.
20321              * @param {Roo.bootstrap.DateField} this
20322              * @param {Mixed} date The date value
20323              */
20324             select : true
20325         });
20326 };
20327
20328 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20329     
20330     /**
20331      * @cfg {String} format
20332      * The default time format string which can be overriden for localization support.  The format must be
20333      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20334      */
20335     format : "H:i",
20336        
20337     onRender: function(ct, position)
20338     {
20339         
20340         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20341                 
20342         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20343         
20344         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20345         
20346         this.pop = this.picker().select('>.datepicker-time',true).first();
20347         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20348         
20349         this.picker().on('mousedown', this.onMousedown, this);
20350         this.picker().on('click', this.onClick, this);
20351         
20352         this.picker().addClass('datepicker-dropdown');
20353     
20354         this.fillTime();
20355         this.update();
20356             
20357         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20358         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20359         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20360         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20361         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20362         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20363
20364     },
20365     
20366     fireKey: function(e){
20367         if (!this.picker().isVisible()){
20368             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20369                 this.show();
20370             }
20371             return;
20372         }
20373
20374         e.preventDefault();
20375         
20376         switch(e.keyCode){
20377             case 27: // escape
20378                 this.hide();
20379                 break;
20380             case 37: // left
20381             case 39: // right
20382                 this.onTogglePeriod();
20383                 break;
20384             case 38: // up
20385                 this.onIncrementMinutes();
20386                 break;
20387             case 40: // down
20388                 this.onDecrementMinutes();
20389                 break;
20390             case 13: // enter
20391             case 9: // tab
20392                 this.setTime();
20393                 break;
20394         }
20395     },
20396     
20397     onClick: function(e) {
20398         e.stopPropagation();
20399         e.preventDefault();
20400     },
20401     
20402     picker : function()
20403     {
20404         return this.el.select('.datepicker', true).first();
20405     },
20406     
20407     fillTime: function()
20408     {    
20409         var time = this.pop.select('tbody', true).first();
20410         
20411         time.dom.innerHTML = '';
20412         
20413         time.createChild({
20414             tag: 'tr',
20415             cn: [
20416                 {
20417                     tag: 'td',
20418                     cn: [
20419                         {
20420                             tag: 'a',
20421                             href: '#',
20422                             cls: 'btn',
20423                             cn: [
20424                                 {
20425                                     tag: 'span',
20426                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20427                                 }
20428                             ]
20429                         } 
20430                     ]
20431                 },
20432                 {
20433                     tag: 'td',
20434                     cls: 'separator'
20435                 },
20436                 {
20437                     tag: 'td',
20438                     cn: [
20439                         {
20440                             tag: 'a',
20441                             href: '#',
20442                             cls: 'btn',
20443                             cn: [
20444                                 {
20445                                     tag: 'span',
20446                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20447                                 }
20448                             ]
20449                         }
20450                     ]
20451                 },
20452                 {
20453                     tag: 'td',
20454                     cls: 'separator'
20455                 }
20456             ]
20457         });
20458         
20459         time.createChild({
20460             tag: 'tr',
20461             cn: [
20462                 {
20463                     tag: 'td',
20464                     cn: [
20465                         {
20466                             tag: 'span',
20467                             cls: 'timepicker-hour',
20468                             html: '00'
20469                         }  
20470                     ]
20471                 },
20472                 {
20473                     tag: 'td',
20474                     cls: 'separator',
20475                     html: ':'
20476                 },
20477                 {
20478                     tag: 'td',
20479                     cn: [
20480                         {
20481                             tag: 'span',
20482                             cls: 'timepicker-minute',
20483                             html: '00'
20484                         }  
20485                     ]
20486                 },
20487                 {
20488                     tag: 'td',
20489                     cls: 'separator'
20490                 },
20491                 {
20492                     tag: 'td',
20493                     cn: [
20494                         {
20495                             tag: 'button',
20496                             type: 'button',
20497                             cls: 'btn btn-primary period',
20498                             html: 'AM'
20499                             
20500                         }
20501                     ]
20502                 }
20503             ]
20504         });
20505         
20506         time.createChild({
20507             tag: 'tr',
20508             cn: [
20509                 {
20510                     tag: 'td',
20511                     cn: [
20512                         {
20513                             tag: 'a',
20514                             href: '#',
20515                             cls: 'btn',
20516                             cn: [
20517                                 {
20518                                     tag: 'span',
20519                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20520                                 }
20521                             ]
20522                         }
20523                     ]
20524                 },
20525                 {
20526                     tag: 'td',
20527                     cls: 'separator'
20528                 },
20529                 {
20530                     tag: 'td',
20531                     cn: [
20532                         {
20533                             tag: 'a',
20534                             href: '#',
20535                             cls: 'btn',
20536                             cn: [
20537                                 {
20538                                     tag: 'span',
20539                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20540                                 }
20541                             ]
20542                         }
20543                     ]
20544                 },
20545                 {
20546                     tag: 'td',
20547                     cls: 'separator'
20548                 }
20549             ]
20550         });
20551         
20552     },
20553     
20554     update: function()
20555     {
20556         
20557         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20558         
20559         this.fill();
20560     },
20561     
20562     fill: function() 
20563     {
20564         var hours = this.time.getHours();
20565         var minutes = this.time.getMinutes();
20566         var period = 'AM';
20567         
20568         if(hours > 11){
20569             period = 'PM';
20570         }
20571         
20572         if(hours == 0){
20573             hours = 12;
20574         }
20575         
20576         
20577         if(hours > 12){
20578             hours = hours - 12;
20579         }
20580         
20581         if(hours < 10){
20582             hours = '0' + hours;
20583         }
20584         
20585         if(minutes < 10){
20586             minutes = '0' + minutes;
20587         }
20588         
20589         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20590         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20591         this.pop.select('button', true).first().dom.innerHTML = period;
20592         
20593     },
20594     
20595     place: function()
20596     {   
20597         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20598         
20599         var cls = ['bottom'];
20600         
20601         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20602             cls.pop();
20603             cls.push('top');
20604         }
20605         
20606         cls.push('right');
20607         
20608         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20609             cls.pop();
20610             cls.push('left');
20611         }
20612         
20613         this.picker().addClass(cls.join('-'));
20614         
20615         var _this = this;
20616         
20617         Roo.each(cls, function(c){
20618             if(c == 'bottom'){
20619                 _this.picker().setTop(_this.inputEl().getHeight());
20620                 return;
20621             }
20622             if(c == 'top'){
20623                 _this.picker().setTop(0 - _this.picker().getHeight());
20624                 return;
20625             }
20626             
20627             if(c == 'left'){
20628                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20629                 return;
20630             }
20631             if(c == 'right'){
20632                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20633                 return;
20634             }
20635         });
20636         
20637     },
20638   
20639     onFocus : function()
20640     {
20641         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20642         this.show();
20643     },
20644     
20645     onBlur : function()
20646     {
20647         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20648         this.hide();
20649     },
20650     
20651     show : function()
20652     {
20653         this.picker().show();
20654         this.pop.show();
20655         this.update();
20656         this.place();
20657         
20658         this.fireEvent('show', this, this.date);
20659     },
20660     
20661     hide : function()
20662     {
20663         this.picker().hide();
20664         this.pop.hide();
20665         
20666         this.fireEvent('hide', this, this.date);
20667     },
20668     
20669     setTime : function()
20670     {
20671         this.hide();
20672         this.setValue(this.time.format(this.format));
20673         
20674         this.fireEvent('select', this, this.date);
20675         
20676         
20677     },
20678     
20679     onMousedown: function(e){
20680         e.stopPropagation();
20681         e.preventDefault();
20682     },
20683     
20684     onIncrementHours: function()
20685     {
20686         Roo.log('onIncrementHours');
20687         this.time = this.time.add(Date.HOUR, 1);
20688         this.update();
20689         
20690     },
20691     
20692     onDecrementHours: function()
20693     {
20694         Roo.log('onDecrementHours');
20695         this.time = this.time.add(Date.HOUR, -1);
20696         this.update();
20697     },
20698     
20699     onIncrementMinutes: function()
20700     {
20701         Roo.log('onIncrementMinutes');
20702         this.time = this.time.add(Date.MINUTE, 1);
20703         this.update();
20704     },
20705     
20706     onDecrementMinutes: function()
20707     {
20708         Roo.log('onDecrementMinutes');
20709         this.time = this.time.add(Date.MINUTE, -1);
20710         this.update();
20711     },
20712     
20713     onTogglePeriod: function()
20714     {
20715         Roo.log('onTogglePeriod');
20716         this.time = this.time.add(Date.HOUR, 12);
20717         this.update();
20718     }
20719     
20720    
20721 });
20722
20723 Roo.apply(Roo.bootstrap.TimeField,  {
20724     
20725     content : {
20726         tag: 'tbody',
20727         cn: [
20728             {
20729                 tag: 'tr',
20730                 cn: [
20731                 {
20732                     tag: 'td',
20733                     colspan: '7'
20734                 }
20735                 ]
20736             }
20737         ]
20738     },
20739     
20740     footer : {
20741         tag: 'tfoot',
20742         cn: [
20743             {
20744                 tag: 'tr',
20745                 cn: [
20746                 {
20747                     tag: 'th',
20748                     colspan: '7',
20749                     cls: '',
20750                     cn: [
20751                         {
20752                             tag: 'button',
20753                             cls: 'btn btn-info ok',
20754                             html: 'OK'
20755                         }
20756                     ]
20757                 }
20758
20759                 ]
20760             }
20761         ]
20762     }
20763 });
20764
20765 Roo.apply(Roo.bootstrap.TimeField,  {
20766   
20767     template : {
20768         tag: 'div',
20769         cls: 'datepicker dropdown-menu',
20770         cn: [
20771             {
20772                 tag: 'div',
20773                 cls: 'datepicker-time',
20774                 cn: [
20775                 {
20776                     tag: 'table',
20777                     cls: 'table-condensed',
20778                     cn:[
20779                     Roo.bootstrap.TimeField.content,
20780                     Roo.bootstrap.TimeField.footer
20781                     ]
20782                 }
20783                 ]
20784             }
20785         ]
20786     }
20787 });
20788
20789  
20790
20791  /*
20792  * - LGPL
20793  *
20794  * MonthField
20795  * 
20796  */
20797
20798 /**
20799  * @class Roo.bootstrap.MonthField
20800  * @extends Roo.bootstrap.Input
20801  * Bootstrap MonthField class
20802  * 
20803  * @cfg {String} language default en
20804  * 
20805  * @constructor
20806  * Create a new MonthField
20807  * @param {Object} config The config object
20808  */
20809
20810 Roo.bootstrap.MonthField = function(config){
20811     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20812     
20813     this.addEvents({
20814         /**
20815          * @event show
20816          * Fires when this field show.
20817          * @param {Roo.bootstrap.MonthField} this
20818          * @param {Mixed} date The date value
20819          */
20820         show : true,
20821         /**
20822          * @event show
20823          * Fires when this field hide.
20824          * @param {Roo.bootstrap.MonthField} this
20825          * @param {Mixed} date The date value
20826          */
20827         hide : true,
20828         /**
20829          * @event select
20830          * Fires when select a date.
20831          * @param {Roo.bootstrap.MonthField} this
20832          * @param {String} oldvalue The old value
20833          * @param {String} newvalue The new value
20834          */
20835         select : true
20836     });
20837 };
20838
20839 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20840     
20841     onRender: function(ct, position)
20842     {
20843         
20844         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20845         
20846         this.language = this.language || 'en';
20847         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20848         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20849         
20850         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20851         this.isInline = false;
20852         this.isInput = true;
20853         this.component = this.el.select('.add-on', true).first() || false;
20854         this.component = (this.component && this.component.length === 0) ? false : this.component;
20855         this.hasInput = this.component && this.inputEL().length;
20856         
20857         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20858         
20859         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20860         
20861         this.picker().on('mousedown', this.onMousedown, this);
20862         this.picker().on('click', this.onClick, this);
20863         
20864         this.picker().addClass('datepicker-dropdown');
20865         
20866         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20867             v.setStyle('width', '189px');
20868         });
20869         
20870         this.fillMonths();
20871         
20872         this.update();
20873         
20874         if(this.isInline) {
20875             this.show();
20876         }
20877         
20878     },
20879     
20880     setValue: function(v, suppressEvent)
20881     {   
20882         var o = this.getValue();
20883         
20884         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20885         
20886         this.update();
20887
20888         if(suppressEvent !== true){
20889             this.fireEvent('select', this, o, v);
20890         }
20891         
20892     },
20893     
20894     getValue: function()
20895     {
20896         return this.value;
20897     },
20898     
20899     onClick: function(e) 
20900     {
20901         e.stopPropagation();
20902         e.preventDefault();
20903         
20904         var target = e.getTarget();
20905         
20906         if(target.nodeName.toLowerCase() === 'i'){
20907             target = Roo.get(target).dom.parentNode;
20908         }
20909         
20910         var nodeName = target.nodeName;
20911         var className = target.className;
20912         var html = target.innerHTML;
20913         
20914         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20915             return;
20916         }
20917         
20918         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20919         
20920         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20921         
20922         this.hide();
20923                         
20924     },
20925     
20926     picker : function()
20927     {
20928         return this.pickerEl;
20929     },
20930     
20931     fillMonths: function()
20932     {    
20933         var i = 0;
20934         var months = this.picker().select('>.datepicker-months td', true).first();
20935         
20936         months.dom.innerHTML = '';
20937         
20938         while (i < 12) {
20939             var month = {
20940                 tag: 'span',
20941                 cls: 'month',
20942                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20943             };
20944             
20945             months.createChild(month);
20946         }
20947         
20948     },
20949     
20950     update: function()
20951     {
20952         var _this = this;
20953         
20954         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20955             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20956         }
20957         
20958         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20959             e.removeClass('active');
20960             
20961             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20962                 e.addClass('active');
20963             }
20964         })
20965     },
20966     
20967     place: function()
20968     {
20969         if(this.isInline) {
20970             return;
20971         }
20972         
20973         this.picker().removeClass(['bottom', 'top']);
20974         
20975         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20976             /*
20977              * place to the top of element!
20978              *
20979              */
20980             
20981             this.picker().addClass('top');
20982             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20983             
20984             return;
20985         }
20986         
20987         this.picker().addClass('bottom');
20988         
20989         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20990     },
20991     
20992     onFocus : function()
20993     {
20994         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20995         this.show();
20996     },
20997     
20998     onBlur : function()
20999     {
21000         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21001         
21002         var d = this.inputEl().getValue();
21003         
21004         this.setValue(d);
21005                 
21006         this.hide();
21007     },
21008     
21009     show : function()
21010     {
21011         this.picker().show();
21012         this.picker().select('>.datepicker-months', true).first().show();
21013         this.update();
21014         this.place();
21015         
21016         this.fireEvent('show', this, this.date);
21017     },
21018     
21019     hide : function()
21020     {
21021         if(this.isInline) {
21022             return;
21023         }
21024         this.picker().hide();
21025         this.fireEvent('hide', this, this.date);
21026         
21027     },
21028     
21029     onMousedown: function(e)
21030     {
21031         e.stopPropagation();
21032         e.preventDefault();
21033     },
21034     
21035     keyup: function(e)
21036     {
21037         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21038         this.update();
21039     },
21040
21041     fireKey: function(e)
21042     {
21043         if (!this.picker().isVisible()){
21044             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21045                 this.show();
21046             }
21047             return;
21048         }
21049         
21050         var dir;
21051         
21052         switch(e.keyCode){
21053             case 27: // escape
21054                 this.hide();
21055                 e.preventDefault();
21056                 break;
21057             case 37: // left
21058             case 39: // right
21059                 dir = e.keyCode == 37 ? -1 : 1;
21060                 
21061                 this.vIndex = this.vIndex + dir;
21062                 
21063                 if(this.vIndex < 0){
21064                     this.vIndex = 0;
21065                 }
21066                 
21067                 if(this.vIndex > 11){
21068                     this.vIndex = 11;
21069                 }
21070                 
21071                 if(isNaN(this.vIndex)){
21072                     this.vIndex = 0;
21073                 }
21074                 
21075                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21076                 
21077                 break;
21078             case 38: // up
21079             case 40: // down
21080                 
21081                 dir = e.keyCode == 38 ? -1 : 1;
21082                 
21083                 this.vIndex = this.vIndex + dir * 4;
21084                 
21085                 if(this.vIndex < 0){
21086                     this.vIndex = 0;
21087                 }
21088                 
21089                 if(this.vIndex > 11){
21090                     this.vIndex = 11;
21091                 }
21092                 
21093                 if(isNaN(this.vIndex)){
21094                     this.vIndex = 0;
21095                 }
21096                 
21097                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21098                 break;
21099                 
21100             case 13: // enter
21101                 
21102                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21103                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21104                 }
21105                 
21106                 this.hide();
21107                 e.preventDefault();
21108                 break;
21109             case 9: // tab
21110                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21111                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21112                 }
21113                 this.hide();
21114                 break;
21115             case 16: // shift
21116             case 17: // ctrl
21117             case 18: // alt
21118                 break;
21119             default :
21120                 this.hide();
21121                 
21122         }
21123     },
21124     
21125     remove: function() 
21126     {
21127         this.picker().remove();
21128     }
21129    
21130 });
21131
21132 Roo.apply(Roo.bootstrap.MonthField,  {
21133     
21134     content : {
21135         tag: 'tbody',
21136         cn: [
21137         {
21138             tag: 'tr',
21139             cn: [
21140             {
21141                 tag: 'td',
21142                 colspan: '7'
21143             }
21144             ]
21145         }
21146         ]
21147     },
21148     
21149     dates:{
21150         en: {
21151             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21152             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21153         }
21154     }
21155 });
21156
21157 Roo.apply(Roo.bootstrap.MonthField,  {
21158   
21159     template : {
21160         tag: 'div',
21161         cls: 'datepicker dropdown-menu roo-dynamic',
21162         cn: [
21163             {
21164                 tag: 'div',
21165                 cls: 'datepicker-months',
21166                 cn: [
21167                 {
21168                     tag: 'table',
21169                     cls: 'table-condensed',
21170                     cn:[
21171                         Roo.bootstrap.DateField.content
21172                     ]
21173                 }
21174                 ]
21175             }
21176         ]
21177     }
21178 });
21179
21180  
21181
21182  
21183  /*
21184  * - LGPL
21185  *
21186  * CheckBox
21187  * 
21188  */
21189
21190 /**
21191  * @class Roo.bootstrap.CheckBox
21192  * @extends Roo.bootstrap.Input
21193  * Bootstrap CheckBox class
21194  * 
21195  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21196  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21197  * @cfg {String} boxLabel The text that appears beside the checkbox
21198  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21199  * @cfg {Boolean} checked initnal the element
21200  * @cfg {Boolean} inline inline the element (default false)
21201  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21202  * @cfg {String} tooltip label tooltip
21203  * 
21204  * @constructor
21205  * Create a new CheckBox
21206  * @param {Object} config The config object
21207  */
21208
21209 Roo.bootstrap.CheckBox = function(config){
21210     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21211    
21212     this.addEvents({
21213         /**
21214         * @event check
21215         * Fires when the element is checked or unchecked.
21216         * @param {Roo.bootstrap.CheckBox} this This input
21217         * @param {Boolean} checked The new checked value
21218         */
21219        check : true,
21220        /**
21221         * @event click
21222         * Fires when the element is click.
21223         * @param {Roo.bootstrap.CheckBox} this This input
21224         */
21225        click : true
21226     });
21227     
21228 };
21229
21230 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21231   
21232     inputType: 'checkbox',
21233     inputValue: 1,
21234     valueOff: 0,
21235     boxLabel: false,
21236     checked: false,
21237     weight : false,
21238     inline: false,
21239     tooltip : '',
21240     
21241     // checkbox success does not make any sense really.. 
21242     invalidClass : "",
21243     validClass : "",
21244     
21245     
21246     getAutoCreate : function()
21247     {
21248         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21249         
21250         var id = Roo.id();
21251         
21252         var cfg = {};
21253         
21254         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21255         
21256         if(this.inline){
21257             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21258         }
21259         
21260         var input =  {
21261             tag: 'input',
21262             id : id,
21263             type : this.inputType,
21264             value : this.inputValue,
21265             cls : 'roo-' + this.inputType, //'form-box',
21266             placeholder : this.placeholder || ''
21267             
21268         };
21269         
21270         if(this.inputType != 'radio'){
21271             var hidden =  {
21272                 tag: 'input',
21273                 type : 'hidden',
21274                 cls : 'roo-hidden-value',
21275                 value : this.checked ? this.inputValue : this.valueOff
21276             };
21277         }
21278         
21279             
21280         if (this.weight) { // Validity check?
21281             cfg.cls += " " + this.inputType + "-" + this.weight;
21282         }
21283         
21284         if (this.disabled) {
21285             input.disabled=true;
21286         }
21287         
21288         if(this.checked){
21289             input.checked = this.checked;
21290         }
21291         
21292         if (this.name) {
21293             
21294             input.name = this.name;
21295             
21296             if(this.inputType != 'radio'){
21297                 hidden.name = this.name;
21298                 input.name = '_hidden_' + this.name;
21299             }
21300         }
21301         
21302         if (this.size) {
21303             input.cls += ' input-' + this.size;
21304         }
21305         
21306         var settings=this;
21307         
21308         ['xs','sm','md','lg'].map(function(size){
21309             if (settings[size]) {
21310                 cfg.cls += ' col-' + size + '-' + settings[size];
21311             }
21312         });
21313         
21314         var inputblock = input;
21315          
21316         if (this.before || this.after) {
21317             
21318             inputblock = {
21319                 cls : 'input-group',
21320                 cn :  [] 
21321             };
21322             
21323             if (this.before) {
21324                 inputblock.cn.push({
21325                     tag :'span',
21326                     cls : 'input-group-addon',
21327                     html : this.before
21328                 });
21329             }
21330             
21331             inputblock.cn.push(input);
21332             
21333             if(this.inputType != 'radio'){
21334                 inputblock.cn.push(hidden);
21335             }
21336             
21337             if (this.after) {
21338                 inputblock.cn.push({
21339                     tag :'span',
21340                     cls : 'input-group-addon',
21341                     html : this.after
21342                 });
21343             }
21344             
21345         }
21346         var boxLabelCfg = false;
21347         
21348         if(this.boxLabel){
21349            
21350             boxLabelCfg = {
21351                 tag: 'label',
21352                 //'for': id, // box label is handled by onclick - so no for...
21353                 cls: 'box-label',
21354                 html: this.boxLabel
21355             };
21356             if(this.tooltip){
21357                 boxLabelCfg.tooltip = this.tooltip;
21358             }
21359              
21360         }
21361         
21362         
21363         if (align ==='left' && this.fieldLabel.length) {
21364 //                Roo.log("left and has label");
21365             cfg.cn = [
21366                 {
21367                     tag: 'label',
21368                     'for' :  id,
21369                     cls : 'control-label',
21370                     html : this.fieldLabel
21371                 },
21372                 {
21373                     cls : "", 
21374                     cn: [
21375                         inputblock
21376                     ]
21377                 }
21378             ];
21379             
21380             if (boxLabelCfg) {
21381                 cfg.cn[1].cn.push(boxLabelCfg);
21382             }
21383             
21384             if(this.labelWidth > 12){
21385                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21386             }
21387             
21388             if(this.labelWidth < 13 && this.labelmd == 0){
21389                 this.labelmd = this.labelWidth;
21390             }
21391             
21392             if(this.labellg > 0){
21393                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21394                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21395             }
21396             
21397             if(this.labelmd > 0){
21398                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21399                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21400             }
21401             
21402             if(this.labelsm > 0){
21403                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21404                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21405             }
21406             
21407             if(this.labelxs > 0){
21408                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21409                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21410             }
21411             
21412         } else if ( this.fieldLabel.length) {
21413 //                Roo.log(" label");
21414                 cfg.cn = [
21415                    
21416                     {
21417                         tag: this.boxLabel ? 'span' : 'label',
21418                         'for': id,
21419                         cls: 'control-label box-input-label',
21420                         //cls : 'input-group-addon',
21421                         html : this.fieldLabel
21422                     },
21423                     
21424                     inputblock
21425                     
21426                 ];
21427                 if (boxLabelCfg) {
21428                     cfg.cn.push(boxLabelCfg);
21429                 }
21430
21431         } else {
21432             
21433 //                Roo.log(" no label && no align");
21434                 cfg.cn = [  inputblock ] ;
21435                 if (boxLabelCfg) {
21436                     cfg.cn.push(boxLabelCfg);
21437                 }
21438
21439                 
21440         }
21441         
21442        
21443         
21444         if(this.inputType != 'radio'){
21445             cfg.cn.push(hidden);
21446         }
21447         
21448         return cfg;
21449         
21450     },
21451     
21452     /**
21453      * return the real input element.
21454      */
21455     inputEl: function ()
21456     {
21457         return this.el.select('input.roo-' + this.inputType,true).first();
21458     },
21459     hiddenEl: function ()
21460     {
21461         return this.el.select('input.roo-hidden-value',true).first();
21462     },
21463     
21464     labelEl: function()
21465     {
21466         return this.el.select('label.control-label',true).first();
21467     },
21468     /* depricated... */
21469     
21470     label: function()
21471     {
21472         return this.labelEl();
21473     },
21474     
21475     boxLabelEl: function()
21476     {
21477         return this.el.select('label.box-label',true).first();
21478     },
21479     
21480     initEvents : function()
21481     {
21482 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21483         
21484         this.inputEl().on('click', this.onClick,  this);
21485         
21486         if (this.boxLabel) { 
21487             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21488         }
21489         
21490         this.startValue = this.getValue();
21491         
21492         if(this.groupId){
21493             Roo.bootstrap.CheckBox.register(this);
21494         }
21495     },
21496     
21497     onClick : function(e)
21498     {   
21499         if(this.fireEvent('click', this, e) !== false){
21500             this.setChecked(!this.checked);
21501         }
21502         
21503     },
21504     
21505     setChecked : function(state,suppressEvent)
21506     {
21507         this.startValue = this.getValue();
21508
21509         if(this.inputType == 'radio'){
21510             
21511             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21512                 e.dom.checked = false;
21513             });
21514             
21515             this.inputEl().dom.checked = true;
21516             
21517             this.inputEl().dom.value = this.inputValue;
21518             
21519             if(suppressEvent !== true){
21520                 this.fireEvent('check', this, true);
21521             }
21522             
21523             this.validate();
21524             
21525             return;
21526         }
21527         
21528         this.checked = state;
21529         
21530         this.inputEl().dom.checked = state;
21531         
21532         
21533         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21534         
21535         if(suppressEvent !== true){
21536             this.fireEvent('check', this, state);
21537         }
21538         
21539         this.validate();
21540     },
21541     
21542     getValue : function()
21543     {
21544         if(this.inputType == 'radio'){
21545             return this.getGroupValue();
21546         }
21547         
21548         return this.hiddenEl().dom.value;
21549         
21550     },
21551     
21552     getGroupValue : function()
21553     {
21554         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21555             return '';
21556         }
21557         
21558         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21559     },
21560     
21561     setValue : function(v,suppressEvent)
21562     {
21563         if(this.inputType == 'radio'){
21564             this.setGroupValue(v, suppressEvent);
21565             return;
21566         }
21567         
21568         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21569         
21570         this.validate();
21571     },
21572     
21573     setGroupValue : function(v, suppressEvent)
21574     {
21575         this.startValue = this.getValue();
21576         
21577         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21578             e.dom.checked = false;
21579             
21580             if(e.dom.value == v){
21581                 e.dom.checked = true;
21582             }
21583         });
21584         
21585         if(suppressEvent !== true){
21586             this.fireEvent('check', this, true);
21587         }
21588
21589         this.validate();
21590         
21591         return;
21592     },
21593     
21594     validate : function()
21595     {
21596         if(this.getVisibilityEl().hasClass('hidden')){
21597             return true;
21598         }
21599         
21600         if(
21601                 this.disabled || 
21602                 (this.inputType == 'radio' && this.validateRadio()) ||
21603                 (this.inputType == 'checkbox' && this.validateCheckbox())
21604         ){
21605             this.markValid();
21606             return true;
21607         }
21608         
21609         this.markInvalid();
21610         return false;
21611     },
21612     
21613     validateRadio : function()
21614     {
21615         if(this.getVisibilityEl().hasClass('hidden')){
21616             return true;
21617         }
21618         
21619         if(this.allowBlank){
21620             return true;
21621         }
21622         
21623         var valid = false;
21624         
21625         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21626             if(!e.dom.checked){
21627                 return;
21628             }
21629             
21630             valid = true;
21631             
21632             return false;
21633         });
21634         
21635         return valid;
21636     },
21637     
21638     validateCheckbox : function()
21639     {
21640         if(!this.groupId){
21641             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21642             //return (this.getValue() == this.inputValue) ? true : false;
21643         }
21644         
21645         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21646         
21647         if(!group){
21648             return false;
21649         }
21650         
21651         var r = false;
21652         
21653         for(var i in group){
21654             if(group[i].el.isVisible(true)){
21655                 r = false;
21656                 break;
21657             }
21658             
21659             r = true;
21660         }
21661         
21662         for(var i in group){
21663             if(r){
21664                 break;
21665             }
21666             
21667             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21668         }
21669         
21670         return r;
21671     },
21672     
21673     /**
21674      * Mark this field as valid
21675      */
21676     markValid : function()
21677     {
21678         var _this = this;
21679         
21680         this.fireEvent('valid', this);
21681         
21682         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21683         
21684         if(this.groupId){
21685             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21686         }
21687         
21688         if(label){
21689             label.markValid();
21690         }
21691
21692         if(this.inputType == 'radio'){
21693             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21694                 var fg = e.findParent('.form-group', false, true);
21695                 if (Roo.bootstrap.version == 3) {
21696                     fg.removeClass([_this.invalidClass, _this.validClass]);
21697                     fg.addClass(_this.validClass);
21698                 } else {
21699                     fg.removeClass(['is-valid', 'is-invalid']);
21700                     fg.addClass('is-valid');
21701                 }
21702             });
21703             
21704             return;
21705         }
21706
21707         if(!this.groupId){
21708             var fg = this.el.findParent('.form-group', false, true);
21709             if (Roo.bootstrap.version == 3) {
21710                 fg.removeClass([this.invalidClass, this.validClass]);
21711                 fg.addClass(this.validClass);
21712             } else {
21713                 fg.removeClass(['is-valid', 'is-invalid']);
21714                 fg.addClass('is-valid');
21715             }
21716             return;
21717         }
21718         
21719         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21720         
21721         if(!group){
21722             return;
21723         }
21724         
21725         for(var i in group){
21726             var fg = group[i].el.findParent('.form-group', false, true);
21727             if (Roo.bootstrap.version == 3) {
21728                 fg.removeClass([this.invalidClass, this.validClass]);
21729                 fg.addClass(this.validClass);
21730             } else {
21731                 fg.removeClass(['is-valid', 'is-invalid']);
21732                 fg.addClass('is-valid');
21733             }
21734         }
21735     },
21736     
21737      /**
21738      * Mark this field as invalid
21739      * @param {String} msg The validation message
21740      */
21741     markInvalid : function(msg)
21742     {
21743         if(this.allowBlank){
21744             return;
21745         }
21746         
21747         var _this = this;
21748         
21749         this.fireEvent('invalid', this, msg);
21750         
21751         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21752         
21753         if(this.groupId){
21754             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21755         }
21756         
21757         if(label){
21758             label.markInvalid();
21759         }
21760             
21761         if(this.inputType == 'radio'){
21762             
21763             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21764                 var fg = e.findParent('.form-group', false, true);
21765                 if (Roo.bootstrap.version == 3) {
21766                     fg.removeClass([_this.invalidClass, _this.validClass]);
21767                     fg.addClass(_this.invalidClass);
21768                 } else {
21769                     fg.removeClass(['is-invalid', 'is-valid']);
21770                     fg.addClass('is-invalid');
21771                 }
21772             });
21773             
21774             return;
21775         }
21776         
21777         if(!this.groupId){
21778             var fg = this.el.findParent('.form-group', false, true);
21779             if (Roo.bootstrap.version == 3) {
21780                 fg.removeClass([_this.invalidClass, _this.validClass]);
21781                 fg.addClass(_this.invalidClass);
21782             } else {
21783                 fg.removeClass(['is-invalid', 'is-valid']);
21784                 fg.addClass('is-invalid');
21785             }
21786             return;
21787         }
21788         
21789         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21790         
21791         if(!group){
21792             return;
21793         }
21794         
21795         for(var i in group){
21796             var fg = group[i].el.findParent('.form-group', false, true);
21797             if (Roo.bootstrap.version == 3) {
21798                 fg.removeClass([_this.invalidClass, _this.validClass]);
21799                 fg.addClass(_this.invalidClass);
21800             } else {
21801                 fg.removeClass(['is-invalid', 'is-valid']);
21802                 fg.addClass('is-invalid');
21803             }
21804         }
21805         
21806     },
21807     
21808     clearInvalid : function()
21809     {
21810         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21811         
21812         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21813         
21814         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21815         
21816         if (label && label.iconEl) {
21817             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21818             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21819         }
21820     },
21821     
21822     disable : function()
21823     {
21824         if(this.inputType != 'radio'){
21825             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21826             return;
21827         }
21828         
21829         var _this = this;
21830         
21831         if(this.rendered){
21832             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21833                 _this.getActionEl().addClass(this.disabledClass);
21834                 e.dom.disabled = true;
21835             });
21836         }
21837         
21838         this.disabled = true;
21839         this.fireEvent("disable", this);
21840         return this;
21841     },
21842
21843     enable : function()
21844     {
21845         if(this.inputType != 'radio'){
21846             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21847             return;
21848         }
21849         
21850         var _this = this;
21851         
21852         if(this.rendered){
21853             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21854                 _this.getActionEl().removeClass(this.disabledClass);
21855                 e.dom.disabled = false;
21856             });
21857         }
21858         
21859         this.disabled = false;
21860         this.fireEvent("enable", this);
21861         return this;
21862     },
21863     
21864     setBoxLabel : function(v)
21865     {
21866         this.boxLabel = v;
21867         
21868         if(this.rendered){
21869             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21870         }
21871     }
21872
21873 });
21874
21875 Roo.apply(Roo.bootstrap.CheckBox, {
21876     
21877     groups: {},
21878     
21879      /**
21880     * register a CheckBox Group
21881     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21882     */
21883     register : function(checkbox)
21884     {
21885         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21886             this.groups[checkbox.groupId] = {};
21887         }
21888         
21889         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21890             return;
21891         }
21892         
21893         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21894         
21895     },
21896     /**
21897     * fetch a CheckBox Group based on the group ID
21898     * @param {string} the group ID
21899     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21900     */
21901     get: function(groupId) {
21902         if (typeof(this.groups[groupId]) == 'undefined') {
21903             return false;
21904         }
21905         
21906         return this.groups[groupId] ;
21907     }
21908     
21909     
21910 });
21911 /*
21912  * - LGPL
21913  *
21914  * RadioItem
21915  * 
21916  */
21917
21918 /**
21919  * @class Roo.bootstrap.Radio
21920  * @extends Roo.bootstrap.Component
21921  * Bootstrap Radio class
21922  * @cfg {String} boxLabel - the label associated
21923  * @cfg {String} value - the value of radio
21924  * 
21925  * @constructor
21926  * Create a new Radio
21927  * @param {Object} config The config object
21928  */
21929 Roo.bootstrap.Radio = function(config){
21930     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21931     
21932 };
21933
21934 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21935     
21936     boxLabel : '',
21937     
21938     value : '',
21939     
21940     getAutoCreate : function()
21941     {
21942         var cfg = {
21943             tag : 'div',
21944             cls : 'form-group radio',
21945             cn : [
21946                 {
21947                     tag : 'label',
21948                     cls : 'box-label',
21949                     html : this.boxLabel
21950                 }
21951             ]
21952         };
21953         
21954         return cfg;
21955     },
21956     
21957     initEvents : function() 
21958     {
21959         this.parent().register(this);
21960         
21961         this.el.on('click', this.onClick, this);
21962         
21963     },
21964     
21965     onClick : function(e)
21966     {
21967         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21968             this.setChecked(true);
21969         }
21970     },
21971     
21972     setChecked : function(state, suppressEvent)
21973     {
21974         this.parent().setValue(this.value, suppressEvent);
21975         
21976     },
21977     
21978     setBoxLabel : function(v)
21979     {
21980         this.boxLabel = v;
21981         
21982         if(this.rendered){
21983             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21984         }
21985     }
21986     
21987 });
21988  
21989
21990  /*
21991  * - LGPL
21992  *
21993  * Input
21994  * 
21995  */
21996
21997 /**
21998  * @class Roo.bootstrap.SecurePass
21999  * @extends Roo.bootstrap.Input
22000  * Bootstrap SecurePass class
22001  *
22002  * 
22003  * @constructor
22004  * Create a new SecurePass
22005  * @param {Object} config The config object
22006  */
22007  
22008 Roo.bootstrap.SecurePass = function (config) {
22009     // these go here, so the translation tool can replace them..
22010     this.errors = {
22011         PwdEmpty: "Please type a password, and then retype it to confirm.",
22012         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22013         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22014         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22015         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22016         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22017         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22018         TooWeak: "Your password is Too Weak."
22019     },
22020     this.meterLabel = "Password strength:";
22021     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22022     this.meterClass = [
22023         "roo-password-meter-tooweak", 
22024         "roo-password-meter-weak", 
22025         "roo-password-meter-medium", 
22026         "roo-password-meter-strong", 
22027         "roo-password-meter-grey"
22028     ];
22029     
22030     this.errors = {};
22031     
22032     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22033 }
22034
22035 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22036     /**
22037      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22038      * {
22039      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22040      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22041      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22042      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22043      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22044      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22045      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22046      * })
22047      */
22048     // private
22049     
22050     meterWidth: 300,
22051     errorMsg :'',    
22052     errors: false,
22053     imageRoot: '/',
22054     /**
22055      * @cfg {String/Object} Label for the strength meter (defaults to
22056      * 'Password strength:')
22057      */
22058     // private
22059     meterLabel: '',
22060     /**
22061      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22062      * ['Weak', 'Medium', 'Strong'])
22063      */
22064     // private    
22065     pwdStrengths: false,    
22066     // private
22067     strength: 0,
22068     // private
22069     _lastPwd: null,
22070     // private
22071     kCapitalLetter: 0,
22072     kSmallLetter: 1,
22073     kDigit: 2,
22074     kPunctuation: 3,
22075     
22076     insecure: false,
22077     // private
22078     initEvents: function ()
22079     {
22080         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22081
22082         if (this.el.is('input[type=password]') && Roo.isSafari) {
22083             this.el.on('keydown', this.SafariOnKeyDown, this);
22084         }
22085
22086         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22087     },
22088     // private
22089     onRender: function (ct, position)
22090     {
22091         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22092         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22093         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22094
22095         this.trigger.createChild({
22096                    cn: [
22097                     {
22098                     //id: 'PwdMeter',
22099                     tag: 'div',
22100                     cls: 'roo-password-meter-grey col-xs-12',
22101                     style: {
22102                         //width: 0,
22103                         //width: this.meterWidth + 'px'                                                
22104                         }
22105                     },
22106                     {                            
22107                          cls: 'roo-password-meter-text'                          
22108                     }
22109                 ]            
22110         });
22111
22112          
22113         if (this.hideTrigger) {
22114             this.trigger.setDisplayed(false);
22115         }
22116         this.setSize(this.width || '', this.height || '');
22117     },
22118     // private
22119     onDestroy: function ()
22120     {
22121         if (this.trigger) {
22122             this.trigger.removeAllListeners();
22123             this.trigger.remove();
22124         }
22125         if (this.wrap) {
22126             this.wrap.remove();
22127         }
22128         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22129     },
22130     // private
22131     checkStrength: function ()
22132     {
22133         var pwd = this.inputEl().getValue();
22134         if (pwd == this._lastPwd) {
22135             return;
22136         }
22137
22138         var strength;
22139         if (this.ClientSideStrongPassword(pwd)) {
22140             strength = 3;
22141         } else if (this.ClientSideMediumPassword(pwd)) {
22142             strength = 2;
22143         } else if (this.ClientSideWeakPassword(pwd)) {
22144             strength = 1;
22145         } else {
22146             strength = 0;
22147         }
22148         
22149         Roo.log('strength1: ' + strength);
22150         
22151         //var pm = this.trigger.child('div/div/div').dom;
22152         var pm = this.trigger.child('div/div');
22153         pm.removeClass(this.meterClass);
22154         pm.addClass(this.meterClass[strength]);
22155                 
22156         
22157         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22158                 
22159         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22160         
22161         this._lastPwd = pwd;
22162     },
22163     reset: function ()
22164     {
22165         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22166         
22167         this._lastPwd = '';
22168         
22169         var pm = this.trigger.child('div/div');
22170         pm.removeClass(this.meterClass);
22171         pm.addClass('roo-password-meter-grey');        
22172         
22173         
22174         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22175         
22176         pt.innerHTML = '';
22177         this.inputEl().dom.type='password';
22178     },
22179     // private
22180     validateValue: function (value)
22181     {
22182         
22183         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22184             return false;
22185         }
22186         if (value.length == 0) {
22187             if (this.allowBlank) {
22188                 this.clearInvalid();
22189                 return true;
22190             }
22191
22192             this.markInvalid(this.errors.PwdEmpty);
22193             this.errorMsg = this.errors.PwdEmpty;
22194             return false;
22195         }
22196         
22197         if(this.insecure){
22198             return true;
22199         }
22200         
22201         if ('[\x21-\x7e]*'.match(value)) {
22202             this.markInvalid(this.errors.PwdBadChar);
22203             this.errorMsg = this.errors.PwdBadChar;
22204             return false;
22205         }
22206         if (value.length < 6) {
22207             this.markInvalid(this.errors.PwdShort);
22208             this.errorMsg = this.errors.PwdShort;
22209             return false;
22210         }
22211         if (value.length > 16) {
22212             this.markInvalid(this.errors.PwdLong);
22213             this.errorMsg = this.errors.PwdLong;
22214             return false;
22215         }
22216         var strength;
22217         if (this.ClientSideStrongPassword(value)) {
22218             strength = 3;
22219         } else if (this.ClientSideMediumPassword(value)) {
22220             strength = 2;
22221         } else if (this.ClientSideWeakPassword(value)) {
22222             strength = 1;
22223         } else {
22224             strength = 0;
22225         }
22226
22227         
22228         if (strength < 2) {
22229             //this.markInvalid(this.errors.TooWeak);
22230             this.errorMsg = this.errors.TooWeak;
22231             //return false;
22232         }
22233         
22234         
22235         console.log('strength2: ' + strength);
22236         
22237         //var pm = this.trigger.child('div/div/div').dom;
22238         
22239         var pm = this.trigger.child('div/div');
22240         pm.removeClass(this.meterClass);
22241         pm.addClass(this.meterClass[strength]);
22242                 
22243         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22244                 
22245         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22246         
22247         this.errorMsg = ''; 
22248         return true;
22249     },
22250     // private
22251     CharacterSetChecks: function (type)
22252     {
22253         this.type = type;
22254         this.fResult = false;
22255     },
22256     // private
22257     isctype: function (character, type)
22258     {
22259         switch (type) {  
22260             case this.kCapitalLetter:
22261                 if (character >= 'A' && character <= 'Z') {
22262                     return true;
22263                 }
22264                 break;
22265             
22266             case this.kSmallLetter:
22267                 if (character >= 'a' && character <= 'z') {
22268                     return true;
22269                 }
22270                 break;
22271             
22272             case this.kDigit:
22273                 if (character >= '0' && character <= '9') {
22274                     return true;
22275                 }
22276                 break;
22277             
22278             case this.kPunctuation:
22279                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22280                     return true;
22281                 }
22282                 break;
22283             
22284             default:
22285                 return false;
22286         }
22287
22288     },
22289     // private
22290     IsLongEnough: function (pwd, size)
22291     {
22292         return !(pwd == null || isNaN(size) || pwd.length < size);
22293     },
22294     // private
22295     SpansEnoughCharacterSets: function (word, nb)
22296     {
22297         if (!this.IsLongEnough(word, nb))
22298         {
22299             return false;
22300         }
22301
22302         var characterSetChecks = new Array(
22303             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22304             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22305         );
22306         
22307         for (var index = 0; index < word.length; ++index) {
22308             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22309                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22310                     characterSetChecks[nCharSet].fResult = true;
22311                     break;
22312                 }
22313             }
22314         }
22315
22316         var nCharSets = 0;
22317         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22318             if (characterSetChecks[nCharSet].fResult) {
22319                 ++nCharSets;
22320             }
22321         }
22322
22323         if (nCharSets < nb) {
22324             return false;
22325         }
22326         return true;
22327     },
22328     // private
22329     ClientSideStrongPassword: function (pwd)
22330     {
22331         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22332     },
22333     // private
22334     ClientSideMediumPassword: function (pwd)
22335     {
22336         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22337     },
22338     // private
22339     ClientSideWeakPassword: function (pwd)
22340     {
22341         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22342     }
22343           
22344 })//<script type="text/javascript">
22345
22346 /*
22347  * Based  Ext JS Library 1.1.1
22348  * Copyright(c) 2006-2007, Ext JS, LLC.
22349  * LGPL
22350  *
22351  */
22352  
22353 /**
22354  * @class Roo.HtmlEditorCore
22355  * @extends Roo.Component
22356  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22357  *
22358  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22359  */
22360
22361 Roo.HtmlEditorCore = function(config){
22362     
22363     
22364     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22365     
22366     
22367     this.addEvents({
22368         /**
22369          * @event initialize
22370          * Fires when the editor is fully initialized (including the iframe)
22371          * @param {Roo.HtmlEditorCore} this
22372          */
22373         initialize: true,
22374         /**
22375          * @event activate
22376          * Fires when the editor is first receives the focus. Any insertion must wait
22377          * until after this event.
22378          * @param {Roo.HtmlEditorCore} this
22379          */
22380         activate: true,
22381          /**
22382          * @event beforesync
22383          * Fires before the textarea is updated with content from the editor iframe. Return false
22384          * to cancel the sync.
22385          * @param {Roo.HtmlEditorCore} this
22386          * @param {String} html
22387          */
22388         beforesync: true,
22389          /**
22390          * @event beforepush
22391          * Fires before the iframe editor is updated with content from the textarea. Return false
22392          * to cancel the push.
22393          * @param {Roo.HtmlEditorCore} this
22394          * @param {String} html
22395          */
22396         beforepush: true,
22397          /**
22398          * @event sync
22399          * Fires when the textarea is updated with content from the editor iframe.
22400          * @param {Roo.HtmlEditorCore} this
22401          * @param {String} html
22402          */
22403         sync: true,
22404          /**
22405          * @event push
22406          * Fires when the iframe editor is updated with content from the textarea.
22407          * @param {Roo.HtmlEditorCore} this
22408          * @param {String} html
22409          */
22410         push: true,
22411         
22412         /**
22413          * @event editorevent
22414          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22415          * @param {Roo.HtmlEditorCore} this
22416          */
22417         editorevent: true
22418         
22419     });
22420     
22421     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22422     
22423     // defaults : white / black...
22424     this.applyBlacklists();
22425     
22426     
22427     
22428 };
22429
22430
22431 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22432
22433
22434      /**
22435      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22436      */
22437     
22438     owner : false,
22439     
22440      /**
22441      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22442      *                        Roo.resizable.
22443      */
22444     resizable : false,
22445      /**
22446      * @cfg {Number} height (in pixels)
22447      */   
22448     height: 300,
22449    /**
22450      * @cfg {Number} width (in pixels)
22451      */   
22452     width: 500,
22453     
22454     /**
22455      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22456      * 
22457      */
22458     stylesheets: false,
22459     
22460     // id of frame..
22461     frameId: false,
22462     
22463     // private properties
22464     validationEvent : false,
22465     deferHeight: true,
22466     initialized : false,
22467     activated : false,
22468     sourceEditMode : false,
22469     onFocus : Roo.emptyFn,
22470     iframePad:3,
22471     hideMode:'offsets',
22472     
22473     clearUp: true,
22474     
22475     // blacklist + whitelisted elements..
22476     black: false,
22477     white: false,
22478      
22479     bodyCls : '',
22480
22481     /**
22482      * Protected method that will not generally be called directly. It
22483      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22484      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22485      */
22486     getDocMarkup : function(){
22487         // body styles..
22488         var st = '';
22489         
22490         // inherit styels from page...?? 
22491         if (this.stylesheets === false) {
22492             
22493             Roo.get(document.head).select('style').each(function(node) {
22494                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22495             });
22496             
22497             Roo.get(document.head).select('link').each(function(node) { 
22498                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22499             });
22500             
22501         } else if (!this.stylesheets.length) {
22502                 // simple..
22503                 st = '<style type="text/css">' +
22504                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22505                    '</style>';
22506         } else { 
22507             st = '<style type="text/css">' +
22508                     this.stylesheets +
22509                 '</style>';
22510         }
22511         
22512         st +=  '<style type="text/css">' +
22513             'IMG { cursor: pointer } ' +
22514         '</style>';
22515
22516         var cls = 'roo-htmleditor-body';
22517         
22518         if(this.bodyCls.length){
22519             cls += ' ' + this.bodyCls;
22520         }
22521         
22522         return '<html><head>' + st  +
22523             //<style type="text/css">' +
22524             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22525             //'</style>' +
22526             ' </head><body class="' +  cls + '"></body></html>';
22527     },
22528
22529     // private
22530     onRender : function(ct, position)
22531     {
22532         var _t = this;
22533         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22534         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22535         
22536         
22537         this.el.dom.style.border = '0 none';
22538         this.el.dom.setAttribute('tabIndex', -1);
22539         this.el.addClass('x-hidden hide');
22540         
22541         
22542         
22543         if(Roo.isIE){ // fix IE 1px bogus margin
22544             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22545         }
22546        
22547         
22548         this.frameId = Roo.id();
22549         
22550          
22551         
22552         var iframe = this.owner.wrap.createChild({
22553             tag: 'iframe',
22554             cls: 'form-control', // bootstrap..
22555             id: this.frameId,
22556             name: this.frameId,
22557             frameBorder : 'no',
22558             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22559         }, this.el
22560         );
22561         
22562         
22563         this.iframe = iframe.dom;
22564
22565          this.assignDocWin();
22566         
22567         this.doc.designMode = 'on';
22568        
22569         this.doc.open();
22570         this.doc.write(this.getDocMarkup());
22571         this.doc.close();
22572
22573         
22574         var task = { // must defer to wait for browser to be ready
22575             run : function(){
22576                 //console.log("run task?" + this.doc.readyState);
22577                 this.assignDocWin();
22578                 if(this.doc.body || this.doc.readyState == 'complete'){
22579                     try {
22580                         this.doc.designMode="on";
22581                     } catch (e) {
22582                         return;
22583                     }
22584                     Roo.TaskMgr.stop(task);
22585                     this.initEditor.defer(10, this);
22586                 }
22587             },
22588             interval : 10,
22589             duration: 10000,
22590             scope: this
22591         };
22592         Roo.TaskMgr.start(task);
22593
22594     },
22595
22596     // private
22597     onResize : function(w, h)
22598     {
22599          Roo.log('resize: ' +w + ',' + h );
22600         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22601         if(!this.iframe){
22602             return;
22603         }
22604         if(typeof w == 'number'){
22605             
22606             this.iframe.style.width = w + 'px';
22607         }
22608         if(typeof h == 'number'){
22609             
22610             this.iframe.style.height = h + 'px';
22611             if(this.doc){
22612                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22613             }
22614         }
22615         
22616     },
22617
22618     /**
22619      * Toggles the editor between standard and source edit mode.
22620      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22621      */
22622     toggleSourceEdit : function(sourceEditMode){
22623         
22624         this.sourceEditMode = sourceEditMode === true;
22625         
22626         if(this.sourceEditMode){
22627  
22628             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22629             
22630         }else{
22631             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22632             //this.iframe.className = '';
22633             this.deferFocus();
22634         }
22635         //this.setSize(this.owner.wrap.getSize());
22636         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22637     },
22638
22639     
22640   
22641
22642     /**
22643      * Protected method that will not generally be called directly. If you need/want
22644      * custom HTML cleanup, this is the method you should override.
22645      * @param {String} html The HTML to be cleaned
22646      * return {String} The cleaned HTML
22647      */
22648     cleanHtml : function(html){
22649         html = String(html);
22650         if(html.length > 5){
22651             if(Roo.isSafari){ // strip safari nonsense
22652                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22653             }
22654         }
22655         if(html == '&nbsp;'){
22656             html = '';
22657         }
22658         return html;
22659     },
22660
22661     /**
22662      * HTML Editor -> Textarea
22663      * Protected method that will not generally be called directly. Syncs the contents
22664      * of the editor iframe with the textarea.
22665      */
22666     syncValue : function(){
22667         if(this.initialized){
22668             var bd = (this.doc.body || this.doc.documentElement);
22669             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22670             var html = bd.innerHTML;
22671             if(Roo.isSafari){
22672                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22673                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22674                 if(m && m[1]){
22675                     html = '<div style="'+m[0]+'">' + html + '</div>';
22676                 }
22677             }
22678             html = this.cleanHtml(html);
22679             // fix up the special chars.. normaly like back quotes in word...
22680             // however we do not want to do this with chinese..
22681             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22682                 
22683                 var cc = match.charCodeAt();
22684
22685                 // Get the character value, handling surrogate pairs
22686                 if (match.length == 2) {
22687                     // It's a surrogate pair, calculate the Unicode code point
22688                     var high = match.charCodeAt(0) - 0xD800;
22689                     var low  = match.charCodeAt(1) - 0xDC00;
22690                     cc = (high * 0x400) + low + 0x10000;
22691                 }  else if (
22692                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22693                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22694                     (cc >= 0xf900 && cc < 0xfb00 )
22695                 ) {
22696                         return match;
22697                 }  
22698          
22699                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22700                 return "&#" + cc + ";";
22701                 
22702                 
22703             });
22704             
22705             
22706              
22707             if(this.owner.fireEvent('beforesync', this, html) !== false){
22708                 this.el.dom.value = html;
22709                 this.owner.fireEvent('sync', this, html);
22710             }
22711         }
22712     },
22713
22714     /**
22715      * Protected method that will not generally be called directly. Pushes the value of the textarea
22716      * into the iframe editor.
22717      */
22718     pushValue : function(){
22719         if(this.initialized){
22720             var v = this.el.dom.value.trim();
22721             
22722 //            if(v.length < 1){
22723 //                v = '&#160;';
22724 //            }
22725             
22726             if(this.owner.fireEvent('beforepush', this, v) !== false){
22727                 var d = (this.doc.body || this.doc.documentElement);
22728                 d.innerHTML = v;
22729                 this.cleanUpPaste();
22730                 this.el.dom.value = d.innerHTML;
22731                 this.owner.fireEvent('push', this, v);
22732             }
22733         }
22734     },
22735
22736     // private
22737     deferFocus : function(){
22738         this.focus.defer(10, this);
22739     },
22740
22741     // doc'ed in Field
22742     focus : function(){
22743         if(this.win && !this.sourceEditMode){
22744             this.win.focus();
22745         }else{
22746             this.el.focus();
22747         }
22748     },
22749     
22750     assignDocWin: function()
22751     {
22752         var iframe = this.iframe;
22753         
22754          if(Roo.isIE){
22755             this.doc = iframe.contentWindow.document;
22756             this.win = iframe.contentWindow;
22757         } else {
22758 //            if (!Roo.get(this.frameId)) {
22759 //                return;
22760 //            }
22761 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22762 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22763             
22764             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22765                 return;
22766             }
22767             
22768             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22769             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22770         }
22771     },
22772     
22773     // private
22774     initEditor : function(){
22775         //console.log("INIT EDITOR");
22776         this.assignDocWin();
22777         
22778         
22779         
22780         this.doc.designMode="on";
22781         this.doc.open();
22782         this.doc.write(this.getDocMarkup());
22783         this.doc.close();
22784         
22785         var dbody = (this.doc.body || this.doc.documentElement);
22786         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22787         // this copies styles from the containing element into thsi one..
22788         // not sure why we need all of this..
22789         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22790         
22791         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22792         //ss['background-attachment'] = 'fixed'; // w3c
22793         dbody.bgProperties = 'fixed'; // ie
22794         //Roo.DomHelper.applyStyles(dbody, ss);
22795         Roo.EventManager.on(this.doc, {
22796             //'mousedown': this.onEditorEvent,
22797             'mouseup': this.onEditorEvent,
22798             'dblclick': this.onEditorEvent,
22799             'click': this.onEditorEvent,
22800             'keyup': this.onEditorEvent,
22801             buffer:100,
22802             scope: this
22803         });
22804         if(Roo.isGecko){
22805             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22806         }
22807         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22808             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22809         }
22810         this.initialized = true;
22811
22812         this.owner.fireEvent('initialize', this);
22813         this.pushValue();
22814     },
22815
22816     // private
22817     onDestroy : function(){
22818         
22819         
22820         
22821         if(this.rendered){
22822             
22823             //for (var i =0; i < this.toolbars.length;i++) {
22824             //    // fixme - ask toolbars for heights?
22825             //    this.toolbars[i].onDestroy();
22826            // }
22827             
22828             //this.wrap.dom.innerHTML = '';
22829             //this.wrap.remove();
22830         }
22831     },
22832
22833     // private
22834     onFirstFocus : function(){
22835         
22836         this.assignDocWin();
22837         
22838         
22839         this.activated = true;
22840          
22841     
22842         if(Roo.isGecko){ // prevent silly gecko errors
22843             this.win.focus();
22844             var s = this.win.getSelection();
22845             if(!s.focusNode || s.focusNode.nodeType != 3){
22846                 var r = s.getRangeAt(0);
22847                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22848                 r.collapse(true);
22849                 this.deferFocus();
22850             }
22851             try{
22852                 this.execCmd('useCSS', true);
22853                 this.execCmd('styleWithCSS', false);
22854             }catch(e){}
22855         }
22856         this.owner.fireEvent('activate', this);
22857     },
22858
22859     // private
22860     adjustFont: function(btn){
22861         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22862         //if(Roo.isSafari){ // safari
22863         //    adjust *= 2;
22864        // }
22865         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22866         if(Roo.isSafari){ // safari
22867             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22868             v =  (v < 10) ? 10 : v;
22869             v =  (v > 48) ? 48 : v;
22870             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22871             
22872         }
22873         
22874         
22875         v = Math.max(1, v+adjust);
22876         
22877         this.execCmd('FontSize', v  );
22878     },
22879
22880     onEditorEvent : function(e)
22881     {
22882         this.owner.fireEvent('editorevent', this, e);
22883       //  this.updateToolbar();
22884         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22885     },
22886
22887     insertTag : function(tg)
22888     {
22889         // could be a bit smarter... -> wrap the current selected tRoo..
22890         if (tg.toLowerCase() == 'span' ||
22891             tg.toLowerCase() == 'code' ||
22892             tg.toLowerCase() == 'sup' ||
22893             tg.toLowerCase() == 'sub' 
22894             ) {
22895             
22896             range = this.createRange(this.getSelection());
22897             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22898             wrappingNode.appendChild(range.extractContents());
22899             range.insertNode(wrappingNode);
22900
22901             return;
22902             
22903             
22904             
22905         }
22906         this.execCmd("formatblock",   tg);
22907         
22908     },
22909     
22910     insertText : function(txt)
22911     {
22912         
22913         
22914         var range = this.createRange();
22915         range.deleteContents();
22916                //alert(Sender.getAttribute('label'));
22917                
22918         range.insertNode(this.doc.createTextNode(txt));
22919     } ,
22920     
22921      
22922
22923     /**
22924      * Executes a Midas editor command on the editor document and performs necessary focus and
22925      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22926      * @param {String} cmd The Midas command
22927      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22928      */
22929     relayCmd : function(cmd, value){
22930         this.win.focus();
22931         this.execCmd(cmd, value);
22932         this.owner.fireEvent('editorevent', this);
22933         //this.updateToolbar();
22934         this.owner.deferFocus();
22935     },
22936
22937     /**
22938      * Executes a Midas editor command directly on the editor document.
22939      * For visual commands, you should use {@link #relayCmd} instead.
22940      * <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     execCmd : function(cmd, value){
22945         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22946         this.syncValue();
22947     },
22948  
22949  
22950    
22951     /**
22952      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22953      * to insert tRoo.
22954      * @param {String} text | dom node.. 
22955      */
22956     insertAtCursor : function(text)
22957     {
22958         
22959         if(!this.activated){
22960             return;
22961         }
22962         /*
22963         if(Roo.isIE){
22964             this.win.focus();
22965             var r = this.doc.selection.createRange();
22966             if(r){
22967                 r.collapse(true);
22968                 r.pasteHTML(text);
22969                 this.syncValue();
22970                 this.deferFocus();
22971             
22972             }
22973             return;
22974         }
22975         */
22976         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22977             this.win.focus();
22978             
22979             
22980             // from jquery ui (MIT licenced)
22981             var range, node;
22982             var win = this.win;
22983             
22984             if (win.getSelection && win.getSelection().getRangeAt) {
22985                 range = win.getSelection().getRangeAt(0);
22986                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22987                 range.insertNode(node);
22988             } else if (win.document.selection && win.document.selection.createRange) {
22989                 // no firefox support
22990                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22991                 win.document.selection.createRange().pasteHTML(txt);
22992             } else {
22993                 // no firefox support
22994                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22995                 this.execCmd('InsertHTML', txt);
22996             } 
22997             
22998             this.syncValue();
22999             
23000             this.deferFocus();
23001         }
23002     },
23003  // private
23004     mozKeyPress : function(e){
23005         if(e.ctrlKey){
23006             var c = e.getCharCode(), cmd;
23007           
23008             if(c > 0){
23009                 c = String.fromCharCode(c).toLowerCase();
23010                 switch(c){
23011                     case 'b':
23012                         cmd = 'bold';
23013                         break;
23014                     case 'i':
23015                         cmd = 'italic';
23016                         break;
23017                     
23018                     case 'u':
23019                         cmd = 'underline';
23020                         break;
23021                     
23022                     case 'v':
23023                         this.cleanUpPaste.defer(100, this);
23024                         return;
23025                         
23026                 }
23027                 if(cmd){
23028                     this.win.focus();
23029                     this.execCmd(cmd);
23030                     this.deferFocus();
23031                     e.preventDefault();
23032                 }
23033                 
23034             }
23035         }
23036     },
23037
23038     // private
23039     fixKeys : function(){ // load time branching for fastest keydown performance
23040         if(Roo.isIE){
23041             return function(e){
23042                 var k = e.getKey(), r;
23043                 if(k == e.TAB){
23044                     e.stopEvent();
23045                     r = this.doc.selection.createRange();
23046                     if(r){
23047                         r.collapse(true);
23048                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23049                         this.deferFocus();
23050                     }
23051                     return;
23052                 }
23053                 
23054                 if(k == e.ENTER){
23055                     r = this.doc.selection.createRange();
23056                     if(r){
23057                         var target = r.parentElement();
23058                         if(!target || target.tagName.toLowerCase() != 'li'){
23059                             e.stopEvent();
23060                             r.pasteHTML('<br />');
23061                             r.collapse(false);
23062                             r.select();
23063                         }
23064                     }
23065                 }
23066                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23067                     this.cleanUpPaste.defer(100, this);
23068                     return;
23069                 }
23070                 
23071                 
23072             };
23073         }else if(Roo.isOpera){
23074             return function(e){
23075                 var k = e.getKey();
23076                 if(k == e.TAB){
23077                     e.stopEvent();
23078                     this.win.focus();
23079                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23080                     this.deferFocus();
23081                 }
23082                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23083                     this.cleanUpPaste.defer(100, this);
23084                     return;
23085                 }
23086                 
23087             };
23088         }else if(Roo.isSafari){
23089             return function(e){
23090                 var k = e.getKey();
23091                 
23092                 if(k == e.TAB){
23093                     e.stopEvent();
23094                     this.execCmd('InsertText','\t');
23095                     this.deferFocus();
23096                     return;
23097                 }
23098                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23099                     this.cleanUpPaste.defer(100, this);
23100                     return;
23101                 }
23102                 
23103              };
23104         }
23105     }(),
23106     
23107     getAllAncestors: function()
23108     {
23109         var p = this.getSelectedNode();
23110         var a = [];
23111         if (!p) {
23112             a.push(p); // push blank onto stack..
23113             p = this.getParentElement();
23114         }
23115         
23116         
23117         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23118             a.push(p);
23119             p = p.parentNode;
23120         }
23121         a.push(this.doc.body);
23122         return a;
23123     },
23124     lastSel : false,
23125     lastSelNode : false,
23126     
23127     
23128     getSelection : function() 
23129     {
23130         this.assignDocWin();
23131         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23132     },
23133     
23134     getSelectedNode: function() 
23135     {
23136         // this may only work on Gecko!!!
23137         
23138         // should we cache this!!!!
23139         
23140         
23141         
23142          
23143         var range = this.createRange(this.getSelection()).cloneRange();
23144         
23145         if (Roo.isIE) {
23146             var parent = range.parentElement();
23147             while (true) {
23148                 var testRange = range.duplicate();
23149                 testRange.moveToElementText(parent);
23150                 if (testRange.inRange(range)) {
23151                     break;
23152                 }
23153                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23154                     break;
23155                 }
23156                 parent = parent.parentElement;
23157             }
23158             return parent;
23159         }
23160         
23161         // is ancestor a text element.
23162         var ac =  range.commonAncestorContainer;
23163         if (ac.nodeType == 3) {
23164             ac = ac.parentNode;
23165         }
23166         
23167         var ar = ac.childNodes;
23168          
23169         var nodes = [];
23170         var other_nodes = [];
23171         var has_other_nodes = false;
23172         for (var i=0;i<ar.length;i++) {
23173             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23174                 continue;
23175             }
23176             // fullly contained node.
23177             
23178             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23179                 nodes.push(ar[i]);
23180                 continue;
23181             }
23182             
23183             // probably selected..
23184             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23185                 other_nodes.push(ar[i]);
23186                 continue;
23187             }
23188             // outer..
23189             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23190                 continue;
23191             }
23192             
23193             
23194             has_other_nodes = true;
23195         }
23196         if (!nodes.length && other_nodes.length) {
23197             nodes= other_nodes;
23198         }
23199         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23200             return false;
23201         }
23202         
23203         return nodes[0];
23204     },
23205     createRange: function(sel)
23206     {
23207         // this has strange effects when using with 
23208         // top toolbar - not sure if it's a great idea.
23209         //this.editor.contentWindow.focus();
23210         if (typeof sel != "undefined") {
23211             try {
23212                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23213             } catch(e) {
23214                 return this.doc.createRange();
23215             }
23216         } else {
23217             return this.doc.createRange();
23218         }
23219     },
23220     getParentElement: function()
23221     {
23222         
23223         this.assignDocWin();
23224         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23225         
23226         var range = this.createRange(sel);
23227          
23228         try {
23229             var p = range.commonAncestorContainer;
23230             while (p.nodeType == 3) { // text node
23231                 p = p.parentNode;
23232             }
23233             return p;
23234         } catch (e) {
23235             return null;
23236         }
23237     
23238     },
23239     /***
23240      *
23241      * Range intersection.. the hard stuff...
23242      *  '-1' = before
23243      *  '0' = hits..
23244      *  '1' = after.
23245      *         [ -- selected range --- ]
23246      *   [fail]                        [fail]
23247      *
23248      *    basically..
23249      *      if end is before start or  hits it. fail.
23250      *      if start is after end or hits it fail.
23251      *
23252      *   if either hits (but other is outside. - then it's not 
23253      *   
23254      *    
23255      **/
23256     
23257     
23258     // @see http://www.thismuchiknow.co.uk/?p=64.
23259     rangeIntersectsNode : function(range, node)
23260     {
23261         var nodeRange = node.ownerDocument.createRange();
23262         try {
23263             nodeRange.selectNode(node);
23264         } catch (e) {
23265             nodeRange.selectNodeContents(node);
23266         }
23267     
23268         var rangeStartRange = range.cloneRange();
23269         rangeStartRange.collapse(true);
23270     
23271         var rangeEndRange = range.cloneRange();
23272         rangeEndRange.collapse(false);
23273     
23274         var nodeStartRange = nodeRange.cloneRange();
23275         nodeStartRange.collapse(true);
23276     
23277         var nodeEndRange = nodeRange.cloneRange();
23278         nodeEndRange.collapse(false);
23279     
23280         return rangeStartRange.compareBoundaryPoints(
23281                  Range.START_TO_START, nodeEndRange) == -1 &&
23282                rangeEndRange.compareBoundaryPoints(
23283                  Range.START_TO_START, nodeStartRange) == 1;
23284         
23285          
23286     },
23287     rangeCompareNode : function(range, node)
23288     {
23289         var nodeRange = node.ownerDocument.createRange();
23290         try {
23291             nodeRange.selectNode(node);
23292         } catch (e) {
23293             nodeRange.selectNodeContents(node);
23294         }
23295         
23296         
23297         range.collapse(true);
23298     
23299         nodeRange.collapse(true);
23300      
23301         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23302         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23303          
23304         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23305         
23306         var nodeIsBefore   =  ss == 1;
23307         var nodeIsAfter    = ee == -1;
23308         
23309         if (nodeIsBefore && nodeIsAfter) {
23310             return 0; // outer
23311         }
23312         if (!nodeIsBefore && nodeIsAfter) {
23313             return 1; //right trailed.
23314         }
23315         
23316         if (nodeIsBefore && !nodeIsAfter) {
23317             return 2;  // left trailed.
23318         }
23319         // fully contined.
23320         return 3;
23321     },
23322
23323     // private? - in a new class?
23324     cleanUpPaste :  function()
23325     {
23326         // cleans up the whole document..
23327         Roo.log('cleanuppaste');
23328         
23329         this.cleanUpChildren(this.doc.body);
23330         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23331         if (clean != this.doc.body.innerHTML) {
23332             this.doc.body.innerHTML = clean;
23333         }
23334         
23335     },
23336     
23337     cleanWordChars : function(input) {// change the chars to hex code
23338         var he = Roo.HtmlEditorCore;
23339         
23340         var output = input;
23341         Roo.each(he.swapCodes, function(sw) { 
23342             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23343             
23344             output = output.replace(swapper, sw[1]);
23345         });
23346         
23347         return output;
23348     },
23349     
23350     
23351     cleanUpChildren : function (n)
23352     {
23353         if (!n.childNodes.length) {
23354             return;
23355         }
23356         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23357            this.cleanUpChild(n.childNodes[i]);
23358         }
23359     },
23360     
23361     
23362         
23363     
23364     cleanUpChild : function (node)
23365     {
23366         var ed = this;
23367         //console.log(node);
23368         if (node.nodeName == "#text") {
23369             // clean up silly Windows -- stuff?
23370             return; 
23371         }
23372         if (node.nodeName == "#comment") {
23373             node.parentNode.removeChild(node);
23374             // clean up silly Windows -- stuff?
23375             return; 
23376         }
23377         var lcname = node.tagName.toLowerCase();
23378         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23379         // whitelist of tags..
23380         
23381         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23382             // remove node.
23383             node.parentNode.removeChild(node);
23384             return;
23385             
23386         }
23387         
23388         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23389         
23390         // spans with no attributes - just remove them..
23391         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23392             remove_keep_children = true;
23393         }
23394         
23395         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23396         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23397         
23398         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23399         //    remove_keep_children = true;
23400         //}
23401         
23402         if (remove_keep_children) {
23403             this.cleanUpChildren(node);
23404             // inserts everything just before this node...
23405             while (node.childNodes.length) {
23406                 var cn = node.childNodes[0];
23407                 node.removeChild(cn);
23408                 node.parentNode.insertBefore(cn, node);
23409             }
23410             node.parentNode.removeChild(node);
23411             return;
23412         }
23413         
23414         if (!node.attributes || !node.attributes.length) {
23415             
23416           
23417             
23418             
23419             this.cleanUpChildren(node);
23420             return;
23421         }
23422         
23423         function cleanAttr(n,v)
23424         {
23425             
23426             if (v.match(/^\./) || v.match(/^\//)) {
23427                 return;
23428             }
23429             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23430                 return;
23431             }
23432             if (v.match(/^#/)) {
23433                 return;
23434             }
23435 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23436             node.removeAttribute(n);
23437             
23438         }
23439         
23440         var cwhite = this.cwhite;
23441         var cblack = this.cblack;
23442             
23443         function cleanStyle(n,v)
23444         {
23445             if (v.match(/expression/)) { //XSS?? should we even bother..
23446                 node.removeAttribute(n);
23447                 return;
23448             }
23449             
23450             var parts = v.split(/;/);
23451             var clean = [];
23452             
23453             Roo.each(parts, function(p) {
23454                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23455                 if (!p.length) {
23456                     return true;
23457                 }
23458                 var l = p.split(':').shift().replace(/\s+/g,'');
23459                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23460                 
23461                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23462 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23463                     //node.removeAttribute(n);
23464                     return true;
23465                 }
23466                 //Roo.log()
23467                 // only allow 'c whitelisted system attributes'
23468                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23469 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23470                     //node.removeAttribute(n);
23471                     return true;
23472                 }
23473                 
23474                 
23475                  
23476                 
23477                 clean.push(p);
23478                 return true;
23479             });
23480             if (clean.length) { 
23481                 node.setAttribute(n, clean.join(';'));
23482             } else {
23483                 node.removeAttribute(n);
23484             }
23485             
23486         }
23487         
23488         
23489         for (var i = node.attributes.length-1; i > -1 ; i--) {
23490             var a = node.attributes[i];
23491             //console.log(a);
23492             
23493             if (a.name.toLowerCase().substr(0,2)=='on')  {
23494                 node.removeAttribute(a.name);
23495                 continue;
23496             }
23497             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23498                 node.removeAttribute(a.name);
23499                 continue;
23500             }
23501             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23502                 cleanAttr(a.name,a.value); // fixme..
23503                 continue;
23504             }
23505             if (a.name == 'style') {
23506                 cleanStyle(a.name,a.value);
23507                 continue;
23508             }
23509             /// clean up MS crap..
23510             // tecnically this should be a list of valid class'es..
23511             
23512             
23513             if (a.name == 'class') {
23514                 if (a.value.match(/^Mso/)) {
23515                     node.removeAttribute('class');
23516                 }
23517                 
23518                 if (a.value.match(/^body$/)) {
23519                     node.removeAttribute('class');
23520                 }
23521                 continue;
23522             }
23523             
23524             // style cleanup!?
23525             // class cleanup?
23526             
23527         }
23528         
23529         
23530         this.cleanUpChildren(node);
23531         
23532         
23533     },
23534     
23535     /**
23536      * Clean up MS wordisms...
23537      */
23538     cleanWord : function(node)
23539     {
23540         if (!node) {
23541             this.cleanWord(this.doc.body);
23542             return;
23543         }
23544         
23545         if(
23546                 node.nodeName == 'SPAN' &&
23547                 !node.hasAttributes() &&
23548                 node.childNodes.length == 1 &&
23549                 node.firstChild.nodeName == "#text"  
23550         ) {
23551             var textNode = node.firstChild;
23552             node.removeChild(textNode);
23553             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23554                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23555             }
23556             node.parentNode.insertBefore(textNode, node);
23557             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23558                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23559             }
23560             node.parentNode.removeChild(node);
23561         }
23562         
23563         if (node.nodeName == "#text") {
23564             // clean up silly Windows -- stuff?
23565             return; 
23566         }
23567         if (node.nodeName == "#comment") {
23568             node.parentNode.removeChild(node);
23569             // clean up silly Windows -- stuff?
23570             return; 
23571         }
23572         
23573         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23574             node.parentNode.removeChild(node);
23575             return;
23576         }
23577         //Roo.log(node.tagName);
23578         // remove - but keep children..
23579         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23580             //Roo.log('-- removed');
23581             while (node.childNodes.length) {
23582                 var cn = node.childNodes[0];
23583                 node.removeChild(cn);
23584                 node.parentNode.insertBefore(cn, node);
23585                 // move node to parent - and clean it..
23586                 this.cleanWord(cn);
23587             }
23588             node.parentNode.removeChild(node);
23589             /// no need to iterate chidlren = it's got none..
23590             //this.iterateChildren(node, this.cleanWord);
23591             return;
23592         }
23593         // clean styles
23594         if (node.className.length) {
23595             
23596             var cn = node.className.split(/\W+/);
23597             var cna = [];
23598             Roo.each(cn, function(cls) {
23599                 if (cls.match(/Mso[a-zA-Z]+/)) {
23600                     return;
23601                 }
23602                 cna.push(cls);
23603             });
23604             node.className = cna.length ? cna.join(' ') : '';
23605             if (!cna.length) {
23606                 node.removeAttribute("class");
23607             }
23608         }
23609         
23610         if (node.hasAttribute("lang")) {
23611             node.removeAttribute("lang");
23612         }
23613         
23614         if (node.hasAttribute("style")) {
23615             
23616             var styles = node.getAttribute("style").split(";");
23617             var nstyle = [];
23618             Roo.each(styles, function(s) {
23619                 if (!s.match(/:/)) {
23620                     return;
23621                 }
23622                 var kv = s.split(":");
23623                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23624                     return;
23625                 }
23626                 // what ever is left... we allow.
23627                 nstyle.push(s);
23628             });
23629             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23630             if (!nstyle.length) {
23631                 node.removeAttribute('style');
23632             }
23633         }
23634         this.iterateChildren(node, this.cleanWord);
23635         
23636         
23637         
23638     },
23639     /**
23640      * iterateChildren of a Node, calling fn each time, using this as the scole..
23641      * @param {DomNode} node node to iterate children of.
23642      * @param {Function} fn method of this class to call on each item.
23643      */
23644     iterateChildren : function(node, fn)
23645     {
23646         if (!node.childNodes.length) {
23647                 return;
23648         }
23649         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23650            fn.call(this, node.childNodes[i])
23651         }
23652     },
23653     
23654     
23655     /**
23656      * cleanTableWidths.
23657      *
23658      * Quite often pasting from word etc.. results in tables with column and widths.
23659      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23660      *
23661      */
23662     cleanTableWidths : function(node)
23663     {
23664          
23665          
23666         if (!node) {
23667             this.cleanTableWidths(this.doc.body);
23668             return;
23669         }
23670         
23671         // ignore list...
23672         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23673             return; 
23674         }
23675         Roo.log(node.tagName);
23676         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23677             this.iterateChildren(node, this.cleanTableWidths);
23678             return;
23679         }
23680         if (node.hasAttribute('width')) {
23681             node.removeAttribute('width');
23682         }
23683         
23684          
23685         if (node.hasAttribute("style")) {
23686             // pretty basic...
23687             
23688             var styles = node.getAttribute("style").split(";");
23689             var nstyle = [];
23690             Roo.each(styles, function(s) {
23691                 if (!s.match(/:/)) {
23692                     return;
23693                 }
23694                 var kv = s.split(":");
23695                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23696                     return;
23697                 }
23698                 // what ever is left... we allow.
23699                 nstyle.push(s);
23700             });
23701             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23702             if (!nstyle.length) {
23703                 node.removeAttribute('style');
23704             }
23705         }
23706         
23707         this.iterateChildren(node, this.cleanTableWidths);
23708         
23709         
23710     },
23711     
23712     
23713     
23714     
23715     domToHTML : function(currentElement, depth, nopadtext) {
23716         
23717         depth = depth || 0;
23718         nopadtext = nopadtext || false;
23719     
23720         if (!currentElement) {
23721             return this.domToHTML(this.doc.body);
23722         }
23723         
23724         //Roo.log(currentElement);
23725         var j;
23726         var allText = false;
23727         var nodeName = currentElement.nodeName;
23728         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23729         
23730         if  (nodeName == '#text') {
23731             
23732             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23733         }
23734         
23735         
23736         var ret = '';
23737         if (nodeName != 'BODY') {
23738              
23739             var i = 0;
23740             // Prints the node tagName, such as <A>, <IMG>, etc
23741             if (tagName) {
23742                 var attr = [];
23743                 for(i = 0; i < currentElement.attributes.length;i++) {
23744                     // quoting?
23745                     var aname = currentElement.attributes.item(i).name;
23746                     if (!currentElement.attributes.item(i).value.length) {
23747                         continue;
23748                     }
23749                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23750                 }
23751                 
23752                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23753             } 
23754             else {
23755                 
23756                 // eack
23757             }
23758         } else {
23759             tagName = false;
23760         }
23761         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23762             return ret;
23763         }
23764         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23765             nopadtext = true;
23766         }
23767         
23768         
23769         // Traverse the tree
23770         i = 0;
23771         var currentElementChild = currentElement.childNodes.item(i);
23772         var allText = true;
23773         var innerHTML  = '';
23774         lastnode = '';
23775         while (currentElementChild) {
23776             // Formatting code (indent the tree so it looks nice on the screen)
23777             var nopad = nopadtext;
23778             if (lastnode == 'SPAN') {
23779                 nopad  = true;
23780             }
23781             // text
23782             if  (currentElementChild.nodeName == '#text') {
23783                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23784                 toadd = nopadtext ? toadd : toadd.trim();
23785                 if (!nopad && toadd.length > 80) {
23786                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23787                 }
23788                 innerHTML  += toadd;
23789                 
23790                 i++;
23791                 currentElementChild = currentElement.childNodes.item(i);
23792                 lastNode = '';
23793                 continue;
23794             }
23795             allText = false;
23796             
23797             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23798                 
23799             // Recursively traverse the tree structure of the child node
23800             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23801             lastnode = currentElementChild.nodeName;
23802             i++;
23803             currentElementChild=currentElement.childNodes.item(i);
23804         }
23805         
23806         ret += innerHTML;
23807         
23808         if (!allText) {
23809                 // The remaining code is mostly for formatting the tree
23810             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23811         }
23812         
23813         
23814         if (tagName) {
23815             ret+= "</"+tagName+">";
23816         }
23817         return ret;
23818         
23819     },
23820         
23821     applyBlacklists : function()
23822     {
23823         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23824         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23825         
23826         this.white = [];
23827         this.black = [];
23828         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23829             if (b.indexOf(tag) > -1) {
23830                 return;
23831             }
23832             this.white.push(tag);
23833             
23834         }, this);
23835         
23836         Roo.each(w, function(tag) {
23837             if (b.indexOf(tag) > -1) {
23838                 return;
23839             }
23840             if (this.white.indexOf(tag) > -1) {
23841                 return;
23842             }
23843             this.white.push(tag);
23844             
23845         }, this);
23846         
23847         
23848         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23849             if (w.indexOf(tag) > -1) {
23850                 return;
23851             }
23852             this.black.push(tag);
23853             
23854         }, this);
23855         
23856         Roo.each(b, function(tag) {
23857             if (w.indexOf(tag) > -1) {
23858                 return;
23859             }
23860             if (this.black.indexOf(tag) > -1) {
23861                 return;
23862             }
23863             this.black.push(tag);
23864             
23865         }, this);
23866         
23867         
23868         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23869         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23870         
23871         this.cwhite = [];
23872         this.cblack = [];
23873         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23874             if (b.indexOf(tag) > -1) {
23875                 return;
23876             }
23877             this.cwhite.push(tag);
23878             
23879         }, this);
23880         
23881         Roo.each(w, function(tag) {
23882             if (b.indexOf(tag) > -1) {
23883                 return;
23884             }
23885             if (this.cwhite.indexOf(tag) > -1) {
23886                 return;
23887             }
23888             this.cwhite.push(tag);
23889             
23890         }, this);
23891         
23892         
23893         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23894             if (w.indexOf(tag) > -1) {
23895                 return;
23896             }
23897             this.cblack.push(tag);
23898             
23899         }, this);
23900         
23901         Roo.each(b, function(tag) {
23902             if (w.indexOf(tag) > -1) {
23903                 return;
23904             }
23905             if (this.cblack.indexOf(tag) > -1) {
23906                 return;
23907             }
23908             this.cblack.push(tag);
23909             
23910         }, this);
23911     },
23912     
23913     setStylesheets : function(stylesheets)
23914     {
23915         if(typeof(stylesheets) == 'string'){
23916             Roo.get(this.iframe.contentDocument.head).createChild({
23917                 tag : 'link',
23918                 rel : 'stylesheet',
23919                 type : 'text/css',
23920                 href : stylesheets
23921             });
23922             
23923             return;
23924         }
23925         var _this = this;
23926      
23927         Roo.each(stylesheets, function(s) {
23928             if(!s.length){
23929                 return;
23930             }
23931             
23932             Roo.get(_this.iframe.contentDocument.head).createChild({
23933                 tag : 'link',
23934                 rel : 'stylesheet',
23935                 type : 'text/css',
23936                 href : s
23937             });
23938         });
23939
23940         
23941     },
23942     
23943     removeStylesheets : function()
23944     {
23945         var _this = this;
23946         
23947         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23948             s.remove();
23949         });
23950     },
23951     
23952     setStyle : function(style)
23953     {
23954         Roo.get(this.iframe.contentDocument.head).createChild({
23955             tag : 'style',
23956             type : 'text/css',
23957             html : style
23958         });
23959
23960         return;
23961     }
23962     
23963     // hide stuff that is not compatible
23964     /**
23965      * @event blur
23966      * @hide
23967      */
23968     /**
23969      * @event change
23970      * @hide
23971      */
23972     /**
23973      * @event focus
23974      * @hide
23975      */
23976     /**
23977      * @event specialkey
23978      * @hide
23979      */
23980     /**
23981      * @cfg {String} fieldClass @hide
23982      */
23983     /**
23984      * @cfg {String} focusClass @hide
23985      */
23986     /**
23987      * @cfg {String} autoCreate @hide
23988      */
23989     /**
23990      * @cfg {String} inputType @hide
23991      */
23992     /**
23993      * @cfg {String} invalidClass @hide
23994      */
23995     /**
23996      * @cfg {String} invalidText @hide
23997      */
23998     /**
23999      * @cfg {String} msgFx @hide
24000      */
24001     /**
24002      * @cfg {String} validateOnBlur @hide
24003      */
24004 });
24005
24006 Roo.HtmlEditorCore.white = [
24007         'area', 'br', 'img', 'input', 'hr', 'wbr',
24008         
24009        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24010        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24011        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24012        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24013        'table',   'ul',         'xmp', 
24014        
24015        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24016       'thead',   'tr', 
24017      
24018       'dir', 'menu', 'ol', 'ul', 'dl',
24019        
24020       'embed',  'object'
24021 ];
24022
24023
24024 Roo.HtmlEditorCore.black = [
24025     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24026         'applet', // 
24027         'base',   'basefont', 'bgsound', 'blink',  'body', 
24028         'frame',  'frameset', 'head',    'html',   'ilayer', 
24029         'iframe', 'layer',  'link',     'meta',    'object',   
24030         'script', 'style' ,'title',  'xml' // clean later..
24031 ];
24032 Roo.HtmlEditorCore.clean = [
24033     'script', 'style', 'title', 'xml'
24034 ];
24035 Roo.HtmlEditorCore.remove = [
24036     'font'
24037 ];
24038 // attributes..
24039
24040 Roo.HtmlEditorCore.ablack = [
24041     'on'
24042 ];
24043     
24044 Roo.HtmlEditorCore.aclean = [ 
24045     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24046 ];
24047
24048 // protocols..
24049 Roo.HtmlEditorCore.pwhite= [
24050         'http',  'https',  'mailto'
24051 ];
24052
24053 // white listed style attributes.
24054 Roo.HtmlEditorCore.cwhite= [
24055       //  'text-align', /// default is to allow most things..
24056       
24057          
24058 //        'font-size'//??
24059 ];
24060
24061 // black listed style attributes.
24062 Roo.HtmlEditorCore.cblack= [
24063       //  'font-size' -- this can be set by the project 
24064 ];
24065
24066
24067 Roo.HtmlEditorCore.swapCodes   =[ 
24068     [    8211, "--" ], 
24069     [    8212, "--" ], 
24070     [    8216,  "'" ],  
24071     [    8217, "'" ],  
24072     [    8220, '"' ],  
24073     [    8221, '"' ],  
24074     [    8226, "*" ],  
24075     [    8230, "..." ]
24076 ]; 
24077
24078     /*
24079  * - LGPL
24080  *
24081  * HtmlEditor
24082  * 
24083  */
24084
24085 /**
24086  * @class Roo.bootstrap.HtmlEditor
24087  * @extends Roo.bootstrap.TextArea
24088  * Bootstrap HtmlEditor class
24089
24090  * @constructor
24091  * Create a new HtmlEditor
24092  * @param {Object} config The config object
24093  */
24094
24095 Roo.bootstrap.HtmlEditor = function(config){
24096     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24097     if (!this.toolbars) {
24098         this.toolbars = [];
24099     }
24100     
24101     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24102     this.addEvents({
24103             /**
24104              * @event initialize
24105              * Fires when the editor is fully initialized (including the iframe)
24106              * @param {HtmlEditor} this
24107              */
24108             initialize: true,
24109             /**
24110              * @event activate
24111              * Fires when the editor is first receives the focus. Any insertion must wait
24112              * until after this event.
24113              * @param {HtmlEditor} this
24114              */
24115             activate: true,
24116              /**
24117              * @event beforesync
24118              * Fires before the textarea is updated with content from the editor iframe. Return false
24119              * to cancel the sync.
24120              * @param {HtmlEditor} this
24121              * @param {String} html
24122              */
24123             beforesync: true,
24124              /**
24125              * @event beforepush
24126              * Fires before the iframe editor is updated with content from the textarea. Return false
24127              * to cancel the push.
24128              * @param {HtmlEditor} this
24129              * @param {String} html
24130              */
24131             beforepush: true,
24132              /**
24133              * @event sync
24134              * Fires when the textarea is updated with content from the editor iframe.
24135              * @param {HtmlEditor} this
24136              * @param {String} html
24137              */
24138             sync: true,
24139              /**
24140              * @event push
24141              * Fires when the iframe editor is updated with content from the textarea.
24142              * @param {HtmlEditor} this
24143              * @param {String} html
24144              */
24145             push: true,
24146              /**
24147              * @event editmodechange
24148              * Fires when the editor switches edit modes
24149              * @param {HtmlEditor} this
24150              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24151              */
24152             editmodechange: true,
24153             /**
24154              * @event editorevent
24155              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24156              * @param {HtmlEditor} this
24157              */
24158             editorevent: true,
24159             /**
24160              * @event firstfocus
24161              * Fires when on first focus - needed by toolbars..
24162              * @param {HtmlEditor} this
24163              */
24164             firstfocus: true,
24165             /**
24166              * @event autosave
24167              * Auto save the htmlEditor value as a file into Events
24168              * @param {HtmlEditor} this
24169              */
24170             autosave: true,
24171             /**
24172              * @event savedpreview
24173              * preview the saved version of htmlEditor
24174              * @param {HtmlEditor} this
24175              */
24176             savedpreview: true
24177         });
24178 };
24179
24180
24181 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24182     
24183     
24184       /**
24185      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24186      */
24187     toolbars : false,
24188     
24189      /**
24190     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24191     */
24192     btns : [],
24193    
24194      /**
24195      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24196      *                        Roo.resizable.
24197      */
24198     resizable : false,
24199      /**
24200      * @cfg {Number} height (in pixels)
24201      */   
24202     height: 300,
24203    /**
24204      * @cfg {Number} width (in pixels)
24205      */   
24206     width: false,
24207     
24208     /**
24209      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24210      * 
24211      */
24212     stylesheets: false,
24213     
24214     // id of frame..
24215     frameId: false,
24216     
24217     // private properties
24218     validationEvent : false,
24219     deferHeight: true,
24220     initialized : false,
24221     activated : false,
24222     
24223     onFocus : Roo.emptyFn,
24224     iframePad:3,
24225     hideMode:'offsets',
24226     
24227     tbContainer : false,
24228     
24229     bodyCls : '',
24230     
24231     toolbarContainer :function() {
24232         return this.wrap.select('.x-html-editor-tb',true).first();
24233     },
24234
24235     /**
24236      * Protected method that will not generally be called directly. It
24237      * is called when the editor creates its toolbar. Override this method if you need to
24238      * add custom toolbar buttons.
24239      * @param {HtmlEditor} editor
24240      */
24241     createToolbar : function(){
24242         Roo.log('renewing');
24243         Roo.log("create toolbars");
24244         
24245         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24246         this.toolbars[0].render(this.toolbarContainer());
24247         
24248         return;
24249         
24250 //        if (!editor.toolbars || !editor.toolbars.length) {
24251 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24252 //        }
24253 //        
24254 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24255 //            editor.toolbars[i] = Roo.factory(
24256 //                    typeof(editor.toolbars[i]) == 'string' ?
24257 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24258 //                Roo.bootstrap.HtmlEditor);
24259 //            editor.toolbars[i].init(editor);
24260 //        }
24261     },
24262
24263      
24264     // private
24265     onRender : function(ct, position)
24266     {
24267        // Roo.log("Call onRender: " + this.xtype);
24268         var _t = this;
24269         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24270       
24271         this.wrap = this.inputEl().wrap({
24272             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24273         });
24274         
24275         this.editorcore.onRender(ct, position);
24276          
24277         if (this.resizable) {
24278             this.resizeEl = new Roo.Resizable(this.wrap, {
24279                 pinned : true,
24280                 wrap: true,
24281                 dynamic : true,
24282                 minHeight : this.height,
24283                 height: this.height,
24284                 handles : this.resizable,
24285                 width: this.width,
24286                 listeners : {
24287                     resize : function(r, w, h) {
24288                         _t.onResize(w,h); // -something
24289                     }
24290                 }
24291             });
24292             
24293         }
24294         this.createToolbar(this);
24295        
24296         
24297         if(!this.width && this.resizable){
24298             this.setSize(this.wrap.getSize());
24299         }
24300         if (this.resizeEl) {
24301             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24302             // should trigger onReize..
24303         }
24304         
24305     },
24306
24307     // private
24308     onResize : function(w, h)
24309     {
24310         Roo.log('resize: ' +w + ',' + h );
24311         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24312         var ew = false;
24313         var eh = false;
24314         
24315         if(this.inputEl() ){
24316             if(typeof w == 'number'){
24317                 var aw = w - this.wrap.getFrameWidth('lr');
24318                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24319                 ew = aw;
24320             }
24321             if(typeof h == 'number'){
24322                  var tbh = -11;  // fixme it needs to tool bar size!
24323                 for (var i =0; i < this.toolbars.length;i++) {
24324                     // fixme - ask toolbars for heights?
24325                     tbh += this.toolbars[i].el.getHeight();
24326                     //if (this.toolbars[i].footer) {
24327                     //    tbh += this.toolbars[i].footer.el.getHeight();
24328                     //}
24329                 }
24330               
24331                 
24332                 
24333                 
24334                 
24335                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24336                 ah -= 5; // knock a few pixes off for look..
24337                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24338                 var eh = ah;
24339             }
24340         }
24341         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24342         this.editorcore.onResize(ew,eh);
24343         
24344     },
24345
24346     /**
24347      * Toggles the editor between standard and source edit mode.
24348      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24349      */
24350     toggleSourceEdit : function(sourceEditMode)
24351     {
24352         this.editorcore.toggleSourceEdit(sourceEditMode);
24353         
24354         if(this.editorcore.sourceEditMode){
24355             Roo.log('editor - showing textarea');
24356             
24357 //            Roo.log('in');
24358 //            Roo.log(this.syncValue());
24359             this.syncValue();
24360             this.inputEl().removeClass(['hide', 'x-hidden']);
24361             this.inputEl().dom.removeAttribute('tabIndex');
24362             this.inputEl().focus();
24363         }else{
24364             Roo.log('editor - hiding textarea');
24365 //            Roo.log('out')
24366 //            Roo.log(this.pushValue()); 
24367             this.pushValue();
24368             
24369             this.inputEl().addClass(['hide', 'x-hidden']);
24370             this.inputEl().dom.setAttribute('tabIndex', -1);
24371             //this.deferFocus();
24372         }
24373          
24374         if(this.resizable){
24375             this.setSize(this.wrap.getSize());
24376         }
24377         
24378         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24379     },
24380  
24381     // private (for BoxComponent)
24382     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24383
24384     // private (for BoxComponent)
24385     getResizeEl : function(){
24386         return this.wrap;
24387     },
24388
24389     // private (for BoxComponent)
24390     getPositionEl : function(){
24391         return this.wrap;
24392     },
24393
24394     // private
24395     initEvents : function(){
24396         this.originalValue = this.getValue();
24397     },
24398
24399 //    /**
24400 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24401 //     * @method
24402 //     */
24403 //    markInvalid : Roo.emptyFn,
24404 //    /**
24405 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24406 //     * @method
24407 //     */
24408 //    clearInvalid : Roo.emptyFn,
24409
24410     setValue : function(v){
24411         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24412         this.editorcore.pushValue();
24413     },
24414
24415      
24416     // private
24417     deferFocus : function(){
24418         this.focus.defer(10, this);
24419     },
24420
24421     // doc'ed in Field
24422     focus : function(){
24423         this.editorcore.focus();
24424         
24425     },
24426       
24427
24428     // private
24429     onDestroy : function(){
24430         
24431         
24432         
24433         if(this.rendered){
24434             
24435             for (var i =0; i < this.toolbars.length;i++) {
24436                 // fixme - ask toolbars for heights?
24437                 this.toolbars[i].onDestroy();
24438             }
24439             
24440             this.wrap.dom.innerHTML = '';
24441             this.wrap.remove();
24442         }
24443     },
24444
24445     // private
24446     onFirstFocus : function(){
24447         //Roo.log("onFirstFocus");
24448         this.editorcore.onFirstFocus();
24449          for (var i =0; i < this.toolbars.length;i++) {
24450             this.toolbars[i].onFirstFocus();
24451         }
24452         
24453     },
24454     
24455     // private
24456     syncValue : function()
24457     {   
24458         this.editorcore.syncValue();
24459     },
24460     
24461     pushValue : function()
24462     {   
24463         this.editorcore.pushValue();
24464     }
24465      
24466     
24467     // hide stuff that is not compatible
24468     /**
24469      * @event blur
24470      * @hide
24471      */
24472     /**
24473      * @event change
24474      * @hide
24475      */
24476     /**
24477      * @event focus
24478      * @hide
24479      */
24480     /**
24481      * @event specialkey
24482      * @hide
24483      */
24484     /**
24485      * @cfg {String} fieldClass @hide
24486      */
24487     /**
24488      * @cfg {String} focusClass @hide
24489      */
24490     /**
24491      * @cfg {String} autoCreate @hide
24492      */
24493     /**
24494      * @cfg {String} inputType @hide
24495      */
24496      
24497     /**
24498      * @cfg {String} invalidText @hide
24499      */
24500     /**
24501      * @cfg {String} msgFx @hide
24502      */
24503     /**
24504      * @cfg {String} validateOnBlur @hide
24505      */
24506 });
24507  
24508     
24509    
24510    
24511    
24512       
24513 Roo.namespace('Roo.bootstrap.htmleditor');
24514 /**
24515  * @class Roo.bootstrap.HtmlEditorToolbar1
24516  * Basic Toolbar
24517  * 
24518  * @example
24519  * Usage:
24520  *
24521  new Roo.bootstrap.HtmlEditor({
24522     ....
24523     toolbars : [
24524         new Roo.bootstrap.HtmlEditorToolbar1({
24525             disable : { fonts: 1 , format: 1, ..., ... , ...],
24526             btns : [ .... ]
24527         })
24528     }
24529      
24530  * 
24531  * @cfg {Object} disable List of elements to disable..
24532  * @cfg {Array} btns List of additional buttons.
24533  * 
24534  * 
24535  * NEEDS Extra CSS? 
24536  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24537  */
24538  
24539 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24540 {
24541     
24542     Roo.apply(this, config);
24543     
24544     // default disabled, based on 'good practice'..
24545     this.disable = this.disable || {};
24546     Roo.applyIf(this.disable, {
24547         fontSize : true,
24548         colors : true,
24549         specialElements : true
24550     });
24551     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24552     
24553     this.editor = config.editor;
24554     this.editorcore = config.editor.editorcore;
24555     
24556     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24557     
24558     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24559     // dont call parent... till later.
24560 }
24561 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24562      
24563     bar : true,
24564     
24565     editor : false,
24566     editorcore : false,
24567     
24568     
24569     formats : [
24570         "p" ,  
24571         "h1","h2","h3","h4","h5","h6", 
24572         "pre", "code", 
24573         "abbr", "acronym", "address", "cite", "samp", "var",
24574         'div','span'
24575     ],
24576     
24577     onRender : function(ct, position)
24578     {
24579        // Roo.log("Call onRender: " + this.xtype);
24580         
24581        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24582        Roo.log(this.el);
24583        this.el.dom.style.marginBottom = '0';
24584        var _this = this;
24585        var editorcore = this.editorcore;
24586        var editor= this.editor;
24587        
24588        var children = [];
24589        var btn = function(id,cmd , toggle, handler, html){
24590        
24591             var  event = toggle ? 'toggle' : 'click';
24592        
24593             var a = {
24594                 size : 'sm',
24595                 xtype: 'Button',
24596                 xns: Roo.bootstrap,
24597                 //glyphicon : id,
24598                 fa: id,
24599                 cmd : id || cmd,
24600                 enableToggle:toggle !== false,
24601                 html : html || '',
24602                 pressed : toggle ? false : null,
24603                 listeners : {}
24604             };
24605             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24606                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24607             };
24608             children.push(a);
24609             return a;
24610        }
24611        
24612     //    var cb_box = function...
24613         
24614         var style = {
24615                 xtype: 'Button',
24616                 size : 'sm',
24617                 xns: Roo.bootstrap,
24618                 fa : 'font',
24619                 //html : 'submit'
24620                 menu : {
24621                     xtype: 'Menu',
24622                     xns: Roo.bootstrap,
24623                     items:  []
24624                 }
24625         };
24626         Roo.each(this.formats, function(f) {
24627             style.menu.items.push({
24628                 xtype :'MenuItem',
24629                 xns: Roo.bootstrap,
24630                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24631                 tagname : f,
24632                 listeners : {
24633                     click : function()
24634                     {
24635                         editorcore.insertTag(this.tagname);
24636                         editor.focus();
24637                     }
24638                 }
24639                 
24640             });
24641         });
24642         children.push(style);   
24643         
24644         btn('bold',false,true);
24645         btn('italic',false,true);
24646         btn('align-left', 'justifyleft',true);
24647         btn('align-center', 'justifycenter',true);
24648         btn('align-right' , 'justifyright',true);
24649         btn('link', false, false, function(btn) {
24650             //Roo.log("create link?");
24651             var url = prompt(this.createLinkText, this.defaultLinkValue);
24652             if(url && url != 'http:/'+'/'){
24653                 this.editorcore.relayCmd('createlink', url);
24654             }
24655         }),
24656         btn('list','insertunorderedlist',true);
24657         btn('pencil', false,true, function(btn){
24658                 Roo.log(this);
24659                 this.toggleSourceEdit(btn.pressed);
24660         });
24661         
24662         if (this.editor.btns.length > 0) {
24663             for (var i = 0; i<this.editor.btns.length; i++) {
24664                 children.push(this.editor.btns[i]);
24665             }
24666         }
24667         
24668         /*
24669         var cog = {
24670                 xtype: 'Button',
24671                 size : 'sm',
24672                 xns: Roo.bootstrap,
24673                 glyphicon : 'cog',
24674                 //html : 'submit'
24675                 menu : {
24676                     xtype: 'Menu',
24677                     xns: Roo.bootstrap,
24678                     items:  []
24679                 }
24680         };
24681         
24682         cog.menu.items.push({
24683             xtype :'MenuItem',
24684             xns: Roo.bootstrap,
24685             html : Clean styles,
24686             tagname : f,
24687             listeners : {
24688                 click : function()
24689                 {
24690                     editorcore.insertTag(this.tagname);
24691                     editor.focus();
24692                 }
24693             }
24694             
24695         });
24696        */
24697         
24698          
24699        this.xtype = 'NavSimplebar';
24700         
24701         for(var i=0;i< children.length;i++) {
24702             
24703             this.buttons.add(this.addxtypeChild(children[i]));
24704             
24705         }
24706         
24707         editor.on('editorevent', this.updateToolbar, this);
24708     },
24709     onBtnClick : function(id)
24710     {
24711        this.editorcore.relayCmd(id);
24712        this.editorcore.focus();
24713     },
24714     
24715     /**
24716      * Protected method that will not generally be called directly. It triggers
24717      * a toolbar update by reading the markup state of the current selection in the editor.
24718      */
24719     updateToolbar: function(){
24720
24721         if(!this.editorcore.activated){
24722             this.editor.onFirstFocus(); // is this neeed?
24723             return;
24724         }
24725
24726         var btns = this.buttons; 
24727         var doc = this.editorcore.doc;
24728         btns.get('bold').setActive(doc.queryCommandState('bold'));
24729         btns.get('italic').setActive(doc.queryCommandState('italic'));
24730         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24731         
24732         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24733         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24734         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24735         
24736         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24737         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24738          /*
24739         
24740         var ans = this.editorcore.getAllAncestors();
24741         if (this.formatCombo) {
24742             
24743             
24744             var store = this.formatCombo.store;
24745             this.formatCombo.setValue("");
24746             for (var i =0; i < ans.length;i++) {
24747                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24748                     // select it..
24749                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24750                     break;
24751                 }
24752             }
24753         }
24754         
24755         
24756         
24757         // hides menus... - so this cant be on a menu...
24758         Roo.bootstrap.MenuMgr.hideAll();
24759         */
24760         Roo.bootstrap.MenuMgr.hideAll();
24761         //this.editorsyncValue();
24762     },
24763     onFirstFocus: function() {
24764         this.buttons.each(function(item){
24765            item.enable();
24766         });
24767     },
24768     toggleSourceEdit : function(sourceEditMode){
24769         
24770           
24771         if(sourceEditMode){
24772             Roo.log("disabling buttons");
24773            this.buttons.each( function(item){
24774                 if(item.cmd != 'pencil'){
24775                     item.disable();
24776                 }
24777             });
24778           
24779         }else{
24780             Roo.log("enabling buttons");
24781             if(this.editorcore.initialized){
24782                 this.buttons.each( function(item){
24783                     item.enable();
24784                 });
24785             }
24786             
24787         }
24788         Roo.log("calling toggole on editor");
24789         // tell the editor that it's been pressed..
24790         this.editor.toggleSourceEdit(sourceEditMode);
24791        
24792     }
24793 });
24794
24795
24796
24797
24798
24799 /**
24800  * @class Roo.bootstrap.Table.AbstractSelectionModel
24801  * @extends Roo.util.Observable
24802  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24803  * implemented by descendant classes.  This class should not be directly instantiated.
24804  * @constructor
24805  */
24806 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24807     this.locked = false;
24808     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24809 };
24810
24811
24812 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24813     /** @ignore Called by the grid automatically. Do not call directly. */
24814     init : function(grid){
24815         this.grid = grid;
24816         this.initEvents();
24817     },
24818
24819     /**
24820      * Locks the selections.
24821      */
24822     lock : function(){
24823         this.locked = true;
24824     },
24825
24826     /**
24827      * Unlocks the selections.
24828      */
24829     unlock : function(){
24830         this.locked = false;
24831     },
24832
24833     /**
24834      * Returns true if the selections are locked.
24835      * @return {Boolean}
24836      */
24837     isLocked : function(){
24838         return this.locked;
24839     }
24840 });
24841 /**
24842  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24843  * @class Roo.bootstrap.Table.RowSelectionModel
24844  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24845  * It supports multiple selections and keyboard selection/navigation. 
24846  * @constructor
24847  * @param {Object} config
24848  */
24849
24850 Roo.bootstrap.Table.RowSelectionModel = function(config){
24851     Roo.apply(this, config);
24852     this.selections = new Roo.util.MixedCollection(false, function(o){
24853         return o.id;
24854     });
24855
24856     this.last = false;
24857     this.lastActive = false;
24858
24859     this.addEvents({
24860         /**
24861              * @event selectionchange
24862              * Fires when the selection changes
24863              * @param {SelectionModel} this
24864              */
24865             "selectionchange" : true,
24866         /**
24867              * @event afterselectionchange
24868              * Fires after the selection changes (eg. by key press or clicking)
24869              * @param {SelectionModel} this
24870              */
24871             "afterselectionchange" : true,
24872         /**
24873              * @event beforerowselect
24874              * Fires when a row is selected being selected, return false to cancel.
24875              * @param {SelectionModel} this
24876              * @param {Number} rowIndex The selected index
24877              * @param {Boolean} keepExisting False if other selections will be cleared
24878              */
24879             "beforerowselect" : true,
24880         /**
24881              * @event rowselect
24882              * Fires when a row is selected.
24883              * @param {SelectionModel} this
24884              * @param {Number} rowIndex The selected index
24885              * @param {Roo.data.Record} r The record
24886              */
24887             "rowselect" : true,
24888         /**
24889              * @event rowdeselect
24890              * Fires when a row is deselected.
24891              * @param {SelectionModel} this
24892              * @param {Number} rowIndex The selected index
24893              */
24894         "rowdeselect" : true
24895     });
24896     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24897     this.locked = false;
24898  };
24899
24900 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24901     /**
24902      * @cfg {Boolean} singleSelect
24903      * True to allow selection of only one row at a time (defaults to false)
24904      */
24905     singleSelect : false,
24906
24907     // private
24908     initEvents : function()
24909     {
24910
24911         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24912         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24913         //}else{ // allow click to work like normal
24914          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24915         //}
24916         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24917         this.grid.on("rowclick", this.handleMouseDown, this);
24918         
24919         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24920             "up" : function(e){
24921                 if(!e.shiftKey){
24922                     this.selectPrevious(e.shiftKey);
24923                 }else if(this.last !== false && this.lastActive !== false){
24924                     var last = this.last;
24925                     this.selectRange(this.last,  this.lastActive-1);
24926                     this.grid.getView().focusRow(this.lastActive);
24927                     if(last !== false){
24928                         this.last = last;
24929                     }
24930                 }else{
24931                     this.selectFirstRow();
24932                 }
24933                 this.fireEvent("afterselectionchange", this);
24934             },
24935             "down" : function(e){
24936                 if(!e.shiftKey){
24937                     this.selectNext(e.shiftKey);
24938                 }else if(this.last !== false && this.lastActive !== false){
24939                     var last = this.last;
24940                     this.selectRange(this.last,  this.lastActive+1);
24941                     this.grid.getView().focusRow(this.lastActive);
24942                     if(last !== false){
24943                         this.last = last;
24944                     }
24945                 }else{
24946                     this.selectFirstRow();
24947                 }
24948                 this.fireEvent("afterselectionchange", this);
24949             },
24950             scope: this
24951         });
24952         this.grid.store.on('load', function(){
24953             this.selections.clear();
24954         },this);
24955         /*
24956         var view = this.grid.view;
24957         view.on("refresh", this.onRefresh, this);
24958         view.on("rowupdated", this.onRowUpdated, this);
24959         view.on("rowremoved", this.onRemove, this);
24960         */
24961     },
24962
24963     // private
24964     onRefresh : function()
24965     {
24966         var ds = this.grid.store, i, v = this.grid.view;
24967         var s = this.selections;
24968         s.each(function(r){
24969             if((i = ds.indexOfId(r.id)) != -1){
24970                 v.onRowSelect(i);
24971             }else{
24972                 s.remove(r);
24973             }
24974         });
24975     },
24976
24977     // private
24978     onRemove : function(v, index, r){
24979         this.selections.remove(r);
24980     },
24981
24982     // private
24983     onRowUpdated : function(v, index, r){
24984         if(this.isSelected(r)){
24985             v.onRowSelect(index);
24986         }
24987     },
24988
24989     /**
24990      * Select records.
24991      * @param {Array} records The records to select
24992      * @param {Boolean} keepExisting (optional) True to keep existing selections
24993      */
24994     selectRecords : function(records, keepExisting)
24995     {
24996         if(!keepExisting){
24997             this.clearSelections();
24998         }
24999             var ds = this.grid.store;
25000         for(var i = 0, len = records.length; i < len; i++){
25001             this.selectRow(ds.indexOf(records[i]), true);
25002         }
25003     },
25004
25005     /**
25006      * Gets the number of selected rows.
25007      * @return {Number}
25008      */
25009     getCount : function(){
25010         return this.selections.length;
25011     },
25012
25013     /**
25014      * Selects the first row in the grid.
25015      */
25016     selectFirstRow : function(){
25017         this.selectRow(0);
25018     },
25019
25020     /**
25021      * Select the last row.
25022      * @param {Boolean} keepExisting (optional) True to keep existing selections
25023      */
25024     selectLastRow : function(keepExisting){
25025         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25026         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25027     },
25028
25029     /**
25030      * Selects the row immediately following the last selected row.
25031      * @param {Boolean} keepExisting (optional) True to keep existing selections
25032      */
25033     selectNext : function(keepExisting)
25034     {
25035             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25036             this.selectRow(this.last+1, keepExisting);
25037             this.grid.getView().focusRow(this.last);
25038         }
25039     },
25040
25041     /**
25042      * Selects the row that precedes the last selected row.
25043      * @param {Boolean} keepExisting (optional) True to keep existing selections
25044      */
25045     selectPrevious : function(keepExisting){
25046         if(this.last){
25047             this.selectRow(this.last-1, keepExisting);
25048             this.grid.getView().focusRow(this.last);
25049         }
25050     },
25051
25052     /**
25053      * Returns the selected records
25054      * @return {Array} Array of selected records
25055      */
25056     getSelections : function(){
25057         return [].concat(this.selections.items);
25058     },
25059
25060     /**
25061      * Returns the first selected record.
25062      * @return {Record}
25063      */
25064     getSelected : function(){
25065         return this.selections.itemAt(0);
25066     },
25067
25068
25069     /**
25070      * Clears all selections.
25071      */
25072     clearSelections : function(fast)
25073     {
25074         if(this.locked) {
25075             return;
25076         }
25077         if(fast !== true){
25078                 var ds = this.grid.store;
25079             var s = this.selections;
25080             s.each(function(r){
25081                 this.deselectRow(ds.indexOfId(r.id));
25082             }, this);
25083             s.clear();
25084         }else{
25085             this.selections.clear();
25086         }
25087         this.last = false;
25088     },
25089
25090
25091     /**
25092      * Selects all rows.
25093      */
25094     selectAll : function(){
25095         if(this.locked) {
25096             return;
25097         }
25098         this.selections.clear();
25099         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25100             this.selectRow(i, true);
25101         }
25102     },
25103
25104     /**
25105      * Returns True if there is a selection.
25106      * @return {Boolean}
25107      */
25108     hasSelection : function(){
25109         return this.selections.length > 0;
25110     },
25111
25112     /**
25113      * Returns True if the specified row is selected.
25114      * @param {Number/Record} record The record or index of the record to check
25115      * @return {Boolean}
25116      */
25117     isSelected : function(index){
25118             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25119         return (r && this.selections.key(r.id) ? true : false);
25120     },
25121
25122     /**
25123      * Returns True if the specified record id is selected.
25124      * @param {String} id The id of record to check
25125      * @return {Boolean}
25126      */
25127     isIdSelected : function(id){
25128         return (this.selections.key(id) ? true : false);
25129     },
25130
25131
25132     // private
25133     handleMouseDBClick : function(e, t){
25134         
25135     },
25136     // private
25137     handleMouseDown : function(e, t)
25138     {
25139             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25140         if(this.isLocked() || rowIndex < 0 ){
25141             return;
25142         };
25143         if(e.shiftKey && this.last !== false){
25144             var last = this.last;
25145             this.selectRange(last, rowIndex, e.ctrlKey);
25146             this.last = last; // reset the last
25147             t.focus();
25148     
25149         }else{
25150             var isSelected = this.isSelected(rowIndex);
25151             //Roo.log("select row:" + rowIndex);
25152             if(isSelected){
25153                 this.deselectRow(rowIndex);
25154             } else {
25155                         this.selectRow(rowIndex, true);
25156             }
25157     
25158             /*
25159                 if(e.button !== 0 && isSelected){
25160                 alert('rowIndex 2: ' + rowIndex);
25161                     view.focusRow(rowIndex);
25162                 }else if(e.ctrlKey && isSelected){
25163                     this.deselectRow(rowIndex);
25164                 }else if(!isSelected){
25165                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25166                     view.focusRow(rowIndex);
25167                 }
25168             */
25169         }
25170         this.fireEvent("afterselectionchange", this);
25171     },
25172     // private
25173     handleDragableRowClick :  function(grid, rowIndex, e) 
25174     {
25175         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25176             this.selectRow(rowIndex, false);
25177             grid.view.focusRow(rowIndex);
25178              this.fireEvent("afterselectionchange", this);
25179         }
25180     },
25181     
25182     /**
25183      * Selects multiple rows.
25184      * @param {Array} rows Array of the indexes of the row to select
25185      * @param {Boolean} keepExisting (optional) True to keep existing selections
25186      */
25187     selectRows : function(rows, keepExisting){
25188         if(!keepExisting){
25189             this.clearSelections();
25190         }
25191         for(var i = 0, len = rows.length; i < len; i++){
25192             this.selectRow(rows[i], true);
25193         }
25194     },
25195
25196     /**
25197      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25198      * @param {Number} startRow The index of the first row in the range
25199      * @param {Number} endRow The index of the last row in the range
25200      * @param {Boolean} keepExisting (optional) True to retain existing selections
25201      */
25202     selectRange : function(startRow, endRow, keepExisting){
25203         if(this.locked) {
25204             return;
25205         }
25206         if(!keepExisting){
25207             this.clearSelections();
25208         }
25209         if(startRow <= endRow){
25210             for(var i = startRow; i <= endRow; i++){
25211                 this.selectRow(i, true);
25212             }
25213         }else{
25214             for(var i = startRow; i >= endRow; i--){
25215                 this.selectRow(i, true);
25216             }
25217         }
25218     },
25219
25220     /**
25221      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25222      * @param {Number} startRow The index of the first row in the range
25223      * @param {Number} endRow The index of the last row in the range
25224      */
25225     deselectRange : function(startRow, endRow, preventViewNotify){
25226         if(this.locked) {
25227             return;
25228         }
25229         for(var i = startRow; i <= endRow; i++){
25230             this.deselectRow(i, preventViewNotify);
25231         }
25232     },
25233
25234     /**
25235      * Selects a row.
25236      * @param {Number} row The index of the row to select
25237      * @param {Boolean} keepExisting (optional) True to keep existing selections
25238      */
25239     selectRow : function(index, keepExisting, preventViewNotify)
25240     {
25241             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25242             return;
25243         }
25244         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25245             if(!keepExisting || this.singleSelect){
25246                 this.clearSelections();
25247             }
25248             
25249             var r = this.grid.store.getAt(index);
25250             //console.log('selectRow - record id :' + r.id);
25251             
25252             this.selections.add(r);
25253             this.last = this.lastActive = index;
25254             if(!preventViewNotify){
25255                 var proxy = new Roo.Element(
25256                                 this.grid.getRowDom(index)
25257                 );
25258                 proxy.addClass('bg-info info');
25259             }
25260             this.fireEvent("rowselect", this, index, r);
25261             this.fireEvent("selectionchange", this);
25262         }
25263     },
25264
25265     /**
25266      * Deselects a row.
25267      * @param {Number} row The index of the row to deselect
25268      */
25269     deselectRow : function(index, preventViewNotify)
25270     {
25271         if(this.locked) {
25272             return;
25273         }
25274         if(this.last == index){
25275             this.last = false;
25276         }
25277         if(this.lastActive == index){
25278             this.lastActive = false;
25279         }
25280         
25281         var r = this.grid.store.getAt(index);
25282         if (!r) {
25283             return;
25284         }
25285         
25286         this.selections.remove(r);
25287         //.console.log('deselectRow - record id :' + r.id);
25288         if(!preventViewNotify){
25289         
25290             var proxy = new Roo.Element(
25291                 this.grid.getRowDom(index)
25292             );
25293             proxy.removeClass('bg-info info');
25294         }
25295         this.fireEvent("rowdeselect", this, index);
25296         this.fireEvent("selectionchange", this);
25297     },
25298
25299     // private
25300     restoreLast : function(){
25301         if(this._last){
25302             this.last = this._last;
25303         }
25304     },
25305
25306     // private
25307     acceptsNav : function(row, col, cm){
25308         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25309     },
25310
25311     // private
25312     onEditorKey : function(field, e){
25313         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25314         if(k == e.TAB){
25315             e.stopEvent();
25316             ed.completeEdit();
25317             if(e.shiftKey){
25318                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25319             }else{
25320                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25321             }
25322         }else if(k == e.ENTER && !e.ctrlKey){
25323             e.stopEvent();
25324             ed.completeEdit();
25325             if(e.shiftKey){
25326                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25327             }else{
25328                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25329             }
25330         }else if(k == e.ESC){
25331             ed.cancelEdit();
25332         }
25333         if(newCell){
25334             g.startEditing(newCell[0], newCell[1]);
25335         }
25336     }
25337 });
25338 /*
25339  * Based on:
25340  * Ext JS Library 1.1.1
25341  * Copyright(c) 2006-2007, Ext JS, LLC.
25342  *
25343  * Originally Released Under LGPL - original licence link has changed is not relivant.
25344  *
25345  * Fork - LGPL
25346  * <script type="text/javascript">
25347  */
25348  
25349 /**
25350  * @class Roo.bootstrap.PagingToolbar
25351  * @extends Roo.bootstrap.NavSimplebar
25352  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25353  * @constructor
25354  * Create a new PagingToolbar
25355  * @param {Object} config The config object
25356  * @param {Roo.data.Store} store
25357  */
25358 Roo.bootstrap.PagingToolbar = function(config)
25359 {
25360     // old args format still supported... - xtype is prefered..
25361         // created from xtype...
25362     
25363     this.ds = config.dataSource;
25364     
25365     if (config.store && !this.ds) {
25366         this.store= Roo.factory(config.store, Roo.data);
25367         this.ds = this.store;
25368         this.ds.xmodule = this.xmodule || false;
25369     }
25370     
25371     this.toolbarItems = [];
25372     if (config.items) {
25373         this.toolbarItems = config.items;
25374     }
25375     
25376     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25377     
25378     this.cursor = 0;
25379     
25380     if (this.ds) { 
25381         this.bind(this.ds);
25382     }
25383     
25384     if (Roo.bootstrap.version == 4) {
25385         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25386     } else {
25387         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25388     }
25389     
25390 };
25391
25392 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25393     /**
25394      * @cfg {Roo.data.Store} dataSource
25395      * The underlying data store providing the paged data
25396      */
25397     /**
25398      * @cfg {String/HTMLElement/Element} container
25399      * container The id or element that will contain the toolbar
25400      */
25401     /**
25402      * @cfg {Boolean} displayInfo
25403      * True to display the displayMsg (defaults to false)
25404      */
25405     /**
25406      * @cfg {Number} pageSize
25407      * The number of records to display per page (defaults to 20)
25408      */
25409     pageSize: 20,
25410     /**
25411      * @cfg {String} displayMsg
25412      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25413      */
25414     displayMsg : 'Displaying {0} - {1} of {2}',
25415     /**
25416      * @cfg {String} emptyMsg
25417      * The message to display when no records are found (defaults to "No data to display")
25418      */
25419     emptyMsg : 'No data to display',
25420     /**
25421      * Customizable piece of the default paging text (defaults to "Page")
25422      * @type String
25423      */
25424     beforePageText : "Page",
25425     /**
25426      * Customizable piece of the default paging text (defaults to "of %0")
25427      * @type String
25428      */
25429     afterPageText : "of {0}",
25430     /**
25431      * Customizable piece of the default paging text (defaults to "First Page")
25432      * @type String
25433      */
25434     firstText : "First Page",
25435     /**
25436      * Customizable piece of the default paging text (defaults to "Previous Page")
25437      * @type String
25438      */
25439     prevText : "Previous Page",
25440     /**
25441      * Customizable piece of the default paging text (defaults to "Next Page")
25442      * @type String
25443      */
25444     nextText : "Next Page",
25445     /**
25446      * Customizable piece of the default paging text (defaults to "Last Page")
25447      * @type String
25448      */
25449     lastText : "Last Page",
25450     /**
25451      * Customizable piece of the default paging text (defaults to "Refresh")
25452      * @type String
25453      */
25454     refreshText : "Refresh",
25455
25456     buttons : false,
25457     // private
25458     onRender : function(ct, position) 
25459     {
25460         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25461         this.navgroup.parentId = this.id;
25462         this.navgroup.onRender(this.el, null);
25463         // add the buttons to the navgroup
25464         
25465         if(this.displayInfo){
25466             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25467             this.displayEl = this.el.select('.x-paging-info', true).first();
25468 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25469 //            this.displayEl = navel.el.select('span',true).first();
25470         }
25471         
25472         var _this = this;
25473         
25474         if(this.buttons){
25475             Roo.each(_this.buttons, function(e){ // this might need to use render????
25476                Roo.factory(e).render(_this.el);
25477             });
25478         }
25479             
25480         Roo.each(_this.toolbarItems, function(e) {
25481             _this.navgroup.addItem(e);
25482         });
25483         
25484         
25485         this.first = this.navgroup.addItem({
25486             tooltip: this.firstText,
25487             cls: "prev btn-outline-secondary",
25488             html : ' <i class="fa fa-step-backward"></i>',
25489             disabled: true,
25490             preventDefault: true,
25491             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25492         });
25493         
25494         this.prev =  this.navgroup.addItem({
25495             tooltip: this.prevText,
25496             cls: "prev btn-outline-secondary",
25497             html : ' <i class="fa fa-backward"></i>',
25498             disabled: true,
25499             preventDefault: true,
25500             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25501         });
25502     //this.addSeparator();
25503         
25504         
25505         var field = this.navgroup.addItem( {
25506             tagtype : 'span',
25507             cls : 'x-paging-position  btn-outline-secondary',
25508              disabled: true,
25509             html : this.beforePageText  +
25510                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25511                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25512          } ); //?? escaped?
25513         
25514         this.field = field.el.select('input', true).first();
25515         this.field.on("keydown", this.onPagingKeydown, this);
25516         this.field.on("focus", function(){this.dom.select();});
25517     
25518     
25519         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25520         //this.field.setHeight(18);
25521         //this.addSeparator();
25522         this.next = this.navgroup.addItem({
25523             tooltip: this.nextText,
25524             cls: "next btn-outline-secondary",
25525             html : ' <i class="fa fa-forward"></i>',
25526             disabled: true,
25527             preventDefault: true,
25528             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25529         });
25530         this.last = this.navgroup.addItem({
25531             tooltip: this.lastText,
25532             html : ' <i class="fa fa-step-forward"></i>',
25533             cls: "next btn-outline-secondary",
25534             disabled: true,
25535             preventDefault: true,
25536             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25537         });
25538     //this.addSeparator();
25539         this.loading = this.navgroup.addItem({
25540             tooltip: this.refreshText,
25541             cls: "btn-outline-secondary",
25542             html : ' <i class="fa fa-refresh"></i>',
25543             preventDefault: true,
25544             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25545         });
25546         
25547     },
25548
25549     // private
25550     updateInfo : function(){
25551         if(this.displayEl){
25552             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25553             var msg = count == 0 ?
25554                 this.emptyMsg :
25555                 String.format(
25556                     this.displayMsg,
25557                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25558                 );
25559             this.displayEl.update(msg);
25560         }
25561     },
25562
25563     // private
25564     onLoad : function(ds, r, o)
25565     {
25566         this.cursor = o.params.start ? o.params.start : 0;
25567         
25568         var d = this.getPageData(),
25569             ap = d.activePage,
25570             ps = d.pages;
25571         
25572         
25573         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25574         this.field.dom.value = ap;
25575         this.first.setDisabled(ap == 1);
25576         this.prev.setDisabled(ap == 1);
25577         this.next.setDisabled(ap == ps);
25578         this.last.setDisabled(ap == ps);
25579         this.loading.enable();
25580         this.updateInfo();
25581     },
25582
25583     // private
25584     getPageData : function(){
25585         var total = this.ds.getTotalCount();
25586         return {
25587             total : total,
25588             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25589             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25590         };
25591     },
25592
25593     // private
25594     onLoadError : function(){
25595         this.loading.enable();
25596     },
25597
25598     // private
25599     onPagingKeydown : function(e){
25600         var k = e.getKey();
25601         var d = this.getPageData();
25602         if(k == e.RETURN){
25603             var v = this.field.dom.value, pageNum;
25604             if(!v || isNaN(pageNum = parseInt(v, 10))){
25605                 this.field.dom.value = d.activePage;
25606                 return;
25607             }
25608             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25609             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25610             e.stopEvent();
25611         }
25612         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))
25613         {
25614           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25615           this.field.dom.value = pageNum;
25616           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25617           e.stopEvent();
25618         }
25619         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25620         {
25621           var v = this.field.dom.value, pageNum; 
25622           var increment = (e.shiftKey) ? 10 : 1;
25623           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25624                 increment *= -1;
25625           }
25626           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25627             this.field.dom.value = d.activePage;
25628             return;
25629           }
25630           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25631           {
25632             this.field.dom.value = parseInt(v, 10) + increment;
25633             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25634             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25635           }
25636           e.stopEvent();
25637         }
25638     },
25639
25640     // private
25641     beforeLoad : function(){
25642         if(this.loading){
25643             this.loading.disable();
25644         }
25645     },
25646
25647     // private
25648     onClick : function(which){
25649         
25650         var ds = this.ds;
25651         if (!ds) {
25652             return;
25653         }
25654         
25655         switch(which){
25656             case "first":
25657                 ds.load({params:{start: 0, limit: this.pageSize}});
25658             break;
25659             case "prev":
25660                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25661             break;
25662             case "next":
25663                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25664             break;
25665             case "last":
25666                 var total = ds.getTotalCount();
25667                 var extra = total % this.pageSize;
25668                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25669                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25670             break;
25671             case "refresh":
25672                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25673             break;
25674         }
25675     },
25676
25677     /**
25678      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25679      * @param {Roo.data.Store} store The data store to unbind
25680      */
25681     unbind : function(ds){
25682         ds.un("beforeload", this.beforeLoad, this);
25683         ds.un("load", this.onLoad, this);
25684         ds.un("loadexception", this.onLoadError, this);
25685         ds.un("remove", this.updateInfo, this);
25686         ds.un("add", this.updateInfo, this);
25687         this.ds = undefined;
25688     },
25689
25690     /**
25691      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25692      * @param {Roo.data.Store} store The data store to bind
25693      */
25694     bind : function(ds){
25695         ds.on("beforeload", this.beforeLoad, this);
25696         ds.on("load", this.onLoad, this);
25697         ds.on("loadexception", this.onLoadError, this);
25698         ds.on("remove", this.updateInfo, this);
25699         ds.on("add", this.updateInfo, this);
25700         this.ds = ds;
25701     }
25702 });/*
25703  * - LGPL
25704  *
25705  * element
25706  * 
25707  */
25708
25709 /**
25710  * @class Roo.bootstrap.MessageBar
25711  * @extends Roo.bootstrap.Component
25712  * Bootstrap MessageBar class
25713  * @cfg {String} html contents of the MessageBar
25714  * @cfg {String} weight (info | success | warning | danger) default info
25715  * @cfg {String} beforeClass insert the bar before the given class
25716  * @cfg {Boolean} closable (true | false) default false
25717  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25718  * 
25719  * @constructor
25720  * Create a new Element
25721  * @param {Object} config The config object
25722  */
25723
25724 Roo.bootstrap.MessageBar = function(config){
25725     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25726 };
25727
25728 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25729     
25730     html: '',
25731     weight: 'info',
25732     closable: false,
25733     fixed: false,
25734     beforeClass: 'bootstrap-sticky-wrap',
25735     
25736     getAutoCreate : function(){
25737         
25738         var cfg = {
25739             tag: 'div',
25740             cls: 'alert alert-dismissable alert-' + this.weight,
25741             cn: [
25742                 {
25743                     tag: 'span',
25744                     cls: 'message',
25745                     html: this.html || ''
25746                 }
25747             ]
25748         };
25749         
25750         if(this.fixed){
25751             cfg.cls += ' alert-messages-fixed';
25752         }
25753         
25754         if(this.closable){
25755             cfg.cn.push({
25756                 tag: 'button',
25757                 cls: 'close',
25758                 html: 'x'
25759             });
25760         }
25761         
25762         return cfg;
25763     },
25764     
25765     onRender : function(ct, position)
25766     {
25767         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25768         
25769         if(!this.el){
25770             var cfg = Roo.apply({},  this.getAutoCreate());
25771             cfg.id = Roo.id();
25772             
25773             if (this.cls) {
25774                 cfg.cls += ' ' + this.cls;
25775             }
25776             if (this.style) {
25777                 cfg.style = this.style;
25778             }
25779             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25780             
25781             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25782         }
25783         
25784         this.el.select('>button.close').on('click', this.hide, this);
25785         
25786     },
25787     
25788     show : function()
25789     {
25790         if (!this.rendered) {
25791             this.render();
25792         }
25793         
25794         this.el.show();
25795         
25796         this.fireEvent('show', this);
25797         
25798     },
25799     
25800     hide : function()
25801     {
25802         if (!this.rendered) {
25803             this.render();
25804         }
25805         
25806         this.el.hide();
25807         
25808         this.fireEvent('hide', this);
25809     },
25810     
25811     update : function()
25812     {
25813 //        var e = this.el.dom.firstChild;
25814 //        
25815 //        if(this.closable){
25816 //            e = e.nextSibling;
25817 //        }
25818 //        
25819 //        e.data = this.html || '';
25820
25821         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25822     }
25823    
25824 });
25825
25826  
25827
25828      /*
25829  * - LGPL
25830  *
25831  * Graph
25832  * 
25833  */
25834
25835
25836 /**
25837  * @class Roo.bootstrap.Graph
25838  * @extends Roo.bootstrap.Component
25839  * Bootstrap Graph class
25840 > Prameters
25841  -sm {number} sm 4
25842  -md {number} md 5
25843  @cfg {String} graphtype  bar | vbar | pie
25844  @cfg {number} g_x coodinator | centre x (pie)
25845  @cfg {number} g_y coodinator | centre y (pie)
25846  @cfg {number} g_r radius (pie)
25847  @cfg {number} g_height height of the chart (respected by all elements in the set)
25848  @cfg {number} g_width width of the chart (respected by all elements in the set)
25849  @cfg {Object} title The title of the chart
25850     
25851  -{Array}  values
25852  -opts (object) options for the chart 
25853      o {
25854      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25855      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25856      o vgutter (number)
25857      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.
25858      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25859      o to
25860      o stretch (boolean)
25861      o }
25862  -opts (object) options for the pie
25863      o{
25864      o cut
25865      o startAngle (number)
25866      o endAngle (number)
25867      } 
25868  *
25869  * @constructor
25870  * Create a new Input
25871  * @param {Object} config The config object
25872  */
25873
25874 Roo.bootstrap.Graph = function(config){
25875     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25876     
25877     this.addEvents({
25878         // img events
25879         /**
25880          * @event click
25881          * The img click event for the img.
25882          * @param {Roo.EventObject} e
25883          */
25884         "click" : true
25885     });
25886 };
25887
25888 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25889     
25890     sm: 4,
25891     md: 5,
25892     graphtype: 'bar',
25893     g_height: 250,
25894     g_width: 400,
25895     g_x: 50,
25896     g_y: 50,
25897     g_r: 30,
25898     opts:{
25899         //g_colors: this.colors,
25900         g_type: 'soft',
25901         g_gutter: '20%'
25902
25903     },
25904     title : false,
25905
25906     getAutoCreate : function(){
25907         
25908         var cfg = {
25909             tag: 'div',
25910             html : null
25911         };
25912         
25913         
25914         return  cfg;
25915     },
25916
25917     onRender : function(ct,position){
25918         
25919         
25920         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25921         
25922         if (typeof(Raphael) == 'undefined') {
25923             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25924             return;
25925         }
25926         
25927         this.raphael = Raphael(this.el.dom);
25928         
25929                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25930                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25931                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25932                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25933                 /*
25934                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25935                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25936                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25937                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25938                 
25939                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25940                 r.barchart(330, 10, 300, 220, data1);
25941                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25942                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25943                 */
25944                 
25945                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25946                 // r.barchart(30, 30, 560, 250,  xdata, {
25947                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25948                 //     axis : "0 0 1 1",
25949                 //     axisxlabels :  xdata
25950                 //     //yvalues : cols,
25951                    
25952                 // });
25953 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25954 //        
25955 //        this.load(null,xdata,{
25956 //                axis : "0 0 1 1",
25957 //                axisxlabels :  xdata
25958 //                });
25959
25960     },
25961
25962     load : function(graphtype,xdata,opts)
25963     {
25964         this.raphael.clear();
25965         if(!graphtype) {
25966             graphtype = this.graphtype;
25967         }
25968         if(!opts){
25969             opts = this.opts;
25970         }
25971         var r = this.raphael,
25972             fin = function () {
25973                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25974             },
25975             fout = function () {
25976                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25977             },
25978             pfin = function() {
25979                 this.sector.stop();
25980                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25981
25982                 if (this.label) {
25983                     this.label[0].stop();
25984                     this.label[0].attr({ r: 7.5 });
25985                     this.label[1].attr({ "font-weight": 800 });
25986                 }
25987             },
25988             pfout = function() {
25989                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25990
25991                 if (this.label) {
25992                     this.label[0].animate({ r: 5 }, 500, "bounce");
25993                     this.label[1].attr({ "font-weight": 400 });
25994                 }
25995             };
25996
25997         switch(graphtype){
25998             case 'bar':
25999                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26000                 break;
26001             case 'hbar':
26002                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26003                 break;
26004             case 'pie':
26005 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26006 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26007 //            
26008                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26009                 
26010                 break;
26011
26012         }
26013         
26014         if(this.title){
26015             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26016         }
26017         
26018     },
26019     
26020     setTitle: function(o)
26021     {
26022         this.title = o;
26023     },
26024     
26025     initEvents: function() {
26026         
26027         if(!this.href){
26028             this.el.on('click', this.onClick, this);
26029         }
26030     },
26031     
26032     onClick : function(e)
26033     {
26034         Roo.log('img onclick');
26035         this.fireEvent('click', this, e);
26036     }
26037    
26038 });
26039
26040  
26041 /*
26042  * - LGPL
26043  *
26044  * numberBox
26045  * 
26046  */
26047 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26048
26049 /**
26050  * @class Roo.bootstrap.dash.NumberBox
26051  * @extends Roo.bootstrap.Component
26052  * Bootstrap NumberBox class
26053  * @cfg {String} headline Box headline
26054  * @cfg {String} content Box content
26055  * @cfg {String} icon Box icon
26056  * @cfg {String} footer Footer text
26057  * @cfg {String} fhref Footer href
26058  * 
26059  * @constructor
26060  * Create a new NumberBox
26061  * @param {Object} config The config object
26062  */
26063
26064
26065 Roo.bootstrap.dash.NumberBox = function(config){
26066     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26067     
26068 };
26069
26070 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26071     
26072     headline : '',
26073     content : '',
26074     icon : '',
26075     footer : '',
26076     fhref : '',
26077     ficon : '',
26078     
26079     getAutoCreate : function(){
26080         
26081         var cfg = {
26082             tag : 'div',
26083             cls : 'small-box ',
26084             cn : [
26085                 {
26086                     tag : 'div',
26087                     cls : 'inner',
26088                     cn :[
26089                         {
26090                             tag : 'h3',
26091                             cls : 'roo-headline',
26092                             html : this.headline
26093                         },
26094                         {
26095                             tag : 'p',
26096                             cls : 'roo-content',
26097                             html : this.content
26098                         }
26099                     ]
26100                 }
26101             ]
26102         };
26103         
26104         if(this.icon){
26105             cfg.cn.push({
26106                 tag : 'div',
26107                 cls : 'icon',
26108                 cn :[
26109                     {
26110                         tag : 'i',
26111                         cls : 'ion ' + this.icon
26112                     }
26113                 ]
26114             });
26115         }
26116         
26117         if(this.footer){
26118             var footer = {
26119                 tag : 'a',
26120                 cls : 'small-box-footer',
26121                 href : this.fhref || '#',
26122                 html : this.footer
26123             };
26124             
26125             cfg.cn.push(footer);
26126             
26127         }
26128         
26129         return  cfg;
26130     },
26131
26132     onRender : function(ct,position){
26133         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26134
26135
26136        
26137                 
26138     },
26139
26140     setHeadline: function (value)
26141     {
26142         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26143     },
26144     
26145     setFooter: function (value, href)
26146     {
26147         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26148         
26149         if(href){
26150             this.el.select('a.small-box-footer',true).first().attr('href', href);
26151         }
26152         
26153     },
26154
26155     setContent: function (value)
26156     {
26157         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26158     },
26159
26160     initEvents: function() 
26161     {   
26162         
26163     }
26164     
26165 });
26166
26167  
26168 /*
26169  * - LGPL
26170  *
26171  * TabBox
26172  * 
26173  */
26174 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26175
26176 /**
26177  * @class Roo.bootstrap.dash.TabBox
26178  * @extends Roo.bootstrap.Component
26179  * Bootstrap TabBox class
26180  * @cfg {String} title Title of the TabBox
26181  * @cfg {String} icon Icon of the TabBox
26182  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26183  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26184  * 
26185  * @constructor
26186  * Create a new TabBox
26187  * @param {Object} config The config object
26188  */
26189
26190
26191 Roo.bootstrap.dash.TabBox = function(config){
26192     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26193     this.addEvents({
26194         // raw events
26195         /**
26196          * @event addpane
26197          * When a pane is added
26198          * @param {Roo.bootstrap.dash.TabPane} pane
26199          */
26200         "addpane" : true,
26201         /**
26202          * @event activatepane
26203          * When a pane is activated
26204          * @param {Roo.bootstrap.dash.TabPane} pane
26205          */
26206         "activatepane" : true
26207         
26208          
26209     });
26210     
26211     this.panes = [];
26212 };
26213
26214 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26215
26216     title : '',
26217     icon : false,
26218     showtabs : true,
26219     tabScrollable : false,
26220     
26221     getChildContainer : function()
26222     {
26223         return this.el.select('.tab-content', true).first();
26224     },
26225     
26226     getAutoCreate : function(){
26227         
26228         var header = {
26229             tag: 'li',
26230             cls: 'pull-left header',
26231             html: this.title,
26232             cn : []
26233         };
26234         
26235         if(this.icon){
26236             header.cn.push({
26237                 tag: 'i',
26238                 cls: 'fa ' + this.icon
26239             });
26240         }
26241         
26242         var h = {
26243             tag: 'ul',
26244             cls: 'nav nav-tabs pull-right',
26245             cn: [
26246                 header
26247             ]
26248         };
26249         
26250         if(this.tabScrollable){
26251             h = {
26252                 tag: 'div',
26253                 cls: 'tab-header',
26254                 cn: [
26255                     {
26256                         tag: 'ul',
26257                         cls: 'nav nav-tabs pull-right',
26258                         cn: [
26259                             header
26260                         ]
26261                     }
26262                 ]
26263             };
26264         }
26265         
26266         var cfg = {
26267             tag: 'div',
26268             cls: 'nav-tabs-custom',
26269             cn: [
26270                 h,
26271                 {
26272                     tag: 'div',
26273                     cls: 'tab-content no-padding',
26274                     cn: []
26275                 }
26276             ]
26277         };
26278
26279         return  cfg;
26280     },
26281     initEvents : function()
26282     {
26283         //Roo.log('add add pane handler');
26284         this.on('addpane', this.onAddPane, this);
26285     },
26286      /**
26287      * Updates the box title
26288      * @param {String} html to set the title to.
26289      */
26290     setTitle : function(value)
26291     {
26292         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26293     },
26294     onAddPane : function(pane)
26295     {
26296         this.panes.push(pane);
26297         //Roo.log('addpane');
26298         //Roo.log(pane);
26299         // tabs are rendere left to right..
26300         if(!this.showtabs){
26301             return;
26302         }
26303         
26304         var ctr = this.el.select('.nav-tabs', true).first();
26305          
26306          
26307         var existing = ctr.select('.nav-tab',true);
26308         var qty = existing.getCount();;
26309         
26310         
26311         var tab = ctr.createChild({
26312             tag : 'li',
26313             cls : 'nav-tab' + (qty ? '' : ' active'),
26314             cn : [
26315                 {
26316                     tag : 'a',
26317                     href:'#',
26318                     html : pane.title
26319                 }
26320             ]
26321         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26322         pane.tab = tab;
26323         
26324         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26325         if (!qty) {
26326             pane.el.addClass('active');
26327         }
26328         
26329                 
26330     },
26331     onTabClick : function(ev,un,ob,pane)
26332     {
26333         //Roo.log('tab - prev default');
26334         ev.preventDefault();
26335         
26336         
26337         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26338         pane.tab.addClass('active');
26339         //Roo.log(pane.title);
26340         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26341         // technically we should have a deactivate event.. but maybe add later.
26342         // and it should not de-activate the selected tab...
26343         this.fireEvent('activatepane', pane);
26344         pane.el.addClass('active');
26345         pane.fireEvent('activate');
26346         
26347         
26348     },
26349     
26350     getActivePane : function()
26351     {
26352         var r = false;
26353         Roo.each(this.panes, function(p) {
26354             if(p.el.hasClass('active')){
26355                 r = p;
26356                 return false;
26357             }
26358             
26359             return;
26360         });
26361         
26362         return r;
26363     }
26364     
26365     
26366 });
26367
26368  
26369 /*
26370  * - LGPL
26371  *
26372  * Tab pane
26373  * 
26374  */
26375 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26376 /**
26377  * @class Roo.bootstrap.TabPane
26378  * @extends Roo.bootstrap.Component
26379  * Bootstrap TabPane class
26380  * @cfg {Boolean} active (false | true) Default false
26381  * @cfg {String} title title of panel
26382
26383  * 
26384  * @constructor
26385  * Create a new TabPane
26386  * @param {Object} config The config object
26387  */
26388
26389 Roo.bootstrap.dash.TabPane = function(config){
26390     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26391     
26392     this.addEvents({
26393         // raw events
26394         /**
26395          * @event activate
26396          * When a pane is activated
26397          * @param {Roo.bootstrap.dash.TabPane} pane
26398          */
26399         "activate" : true
26400          
26401     });
26402 };
26403
26404 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26405     
26406     active : false,
26407     title : '',
26408     
26409     // the tabBox that this is attached to.
26410     tab : false,
26411      
26412     getAutoCreate : function() 
26413     {
26414         var cfg = {
26415             tag: 'div',
26416             cls: 'tab-pane'
26417         };
26418         
26419         if(this.active){
26420             cfg.cls += ' active';
26421         }
26422         
26423         return cfg;
26424     },
26425     initEvents  : function()
26426     {
26427         //Roo.log('trigger add pane handler');
26428         this.parent().fireEvent('addpane', this)
26429     },
26430     
26431      /**
26432      * Updates the tab title 
26433      * @param {String} html to set the title to.
26434      */
26435     setTitle: function(str)
26436     {
26437         if (!this.tab) {
26438             return;
26439         }
26440         this.title = str;
26441         this.tab.select('a', true).first().dom.innerHTML = str;
26442         
26443     }
26444     
26445     
26446     
26447 });
26448
26449  
26450
26451
26452  /*
26453  * - LGPL
26454  *
26455  * menu
26456  * 
26457  */
26458 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26459
26460 /**
26461  * @class Roo.bootstrap.menu.Menu
26462  * @extends Roo.bootstrap.Component
26463  * Bootstrap Menu class - container for Menu
26464  * @cfg {String} html Text of the menu
26465  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26466  * @cfg {String} icon Font awesome icon
26467  * @cfg {String} pos Menu align to (top | bottom) default bottom
26468  * 
26469  * 
26470  * @constructor
26471  * Create a new Menu
26472  * @param {Object} config The config object
26473  */
26474
26475
26476 Roo.bootstrap.menu.Menu = function(config){
26477     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26478     
26479     this.addEvents({
26480         /**
26481          * @event beforeshow
26482          * Fires before this menu is displayed
26483          * @param {Roo.bootstrap.menu.Menu} this
26484          */
26485         beforeshow : true,
26486         /**
26487          * @event beforehide
26488          * Fires before this menu is hidden
26489          * @param {Roo.bootstrap.menu.Menu} this
26490          */
26491         beforehide : true,
26492         /**
26493          * @event show
26494          * Fires after this menu is displayed
26495          * @param {Roo.bootstrap.menu.Menu} this
26496          */
26497         show : true,
26498         /**
26499          * @event hide
26500          * Fires after this menu is hidden
26501          * @param {Roo.bootstrap.menu.Menu} this
26502          */
26503         hide : true,
26504         /**
26505          * @event click
26506          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26507          * @param {Roo.bootstrap.menu.Menu} this
26508          * @param {Roo.EventObject} e
26509          */
26510         click : true
26511     });
26512     
26513 };
26514
26515 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26516     
26517     submenu : false,
26518     html : '',
26519     weight : 'default',
26520     icon : false,
26521     pos : 'bottom',
26522     
26523     
26524     getChildContainer : function() {
26525         if(this.isSubMenu){
26526             return this.el;
26527         }
26528         
26529         return this.el.select('ul.dropdown-menu', true).first();  
26530     },
26531     
26532     getAutoCreate : function()
26533     {
26534         var text = [
26535             {
26536                 tag : 'span',
26537                 cls : 'roo-menu-text',
26538                 html : this.html
26539             }
26540         ];
26541         
26542         if(this.icon){
26543             text.unshift({
26544                 tag : 'i',
26545                 cls : 'fa ' + this.icon
26546             })
26547         }
26548         
26549         
26550         var cfg = {
26551             tag : 'div',
26552             cls : 'btn-group',
26553             cn : [
26554                 {
26555                     tag : 'button',
26556                     cls : 'dropdown-button btn btn-' + this.weight,
26557                     cn : text
26558                 },
26559                 {
26560                     tag : 'button',
26561                     cls : 'dropdown-toggle btn btn-' + this.weight,
26562                     cn : [
26563                         {
26564                             tag : 'span',
26565                             cls : 'caret'
26566                         }
26567                     ]
26568                 },
26569                 {
26570                     tag : 'ul',
26571                     cls : 'dropdown-menu'
26572                 }
26573             ]
26574             
26575         };
26576         
26577         if(this.pos == 'top'){
26578             cfg.cls += ' dropup';
26579         }
26580         
26581         if(this.isSubMenu){
26582             cfg = {
26583                 tag : 'ul',
26584                 cls : 'dropdown-menu'
26585             }
26586         }
26587         
26588         return cfg;
26589     },
26590     
26591     onRender : function(ct, position)
26592     {
26593         this.isSubMenu = ct.hasClass('dropdown-submenu');
26594         
26595         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26596     },
26597     
26598     initEvents : function() 
26599     {
26600         if(this.isSubMenu){
26601             return;
26602         }
26603         
26604         this.hidden = true;
26605         
26606         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26607         this.triggerEl.on('click', this.onTriggerPress, this);
26608         
26609         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26610         this.buttonEl.on('click', this.onClick, this);
26611         
26612     },
26613     
26614     list : function()
26615     {
26616         if(this.isSubMenu){
26617             return this.el;
26618         }
26619         
26620         return this.el.select('ul.dropdown-menu', true).first();
26621     },
26622     
26623     onClick : function(e)
26624     {
26625         this.fireEvent("click", this, e);
26626     },
26627     
26628     onTriggerPress  : function(e)
26629     {   
26630         if (this.isVisible()) {
26631             this.hide();
26632         } else {
26633             this.show();
26634         }
26635     },
26636     
26637     isVisible : function(){
26638         return !this.hidden;
26639     },
26640     
26641     show : function()
26642     {
26643         this.fireEvent("beforeshow", this);
26644         
26645         this.hidden = false;
26646         this.el.addClass('open');
26647         
26648         Roo.get(document).on("mouseup", this.onMouseUp, this);
26649         
26650         this.fireEvent("show", this);
26651         
26652         
26653     },
26654     
26655     hide : function()
26656     {
26657         this.fireEvent("beforehide", this);
26658         
26659         this.hidden = true;
26660         this.el.removeClass('open');
26661         
26662         Roo.get(document).un("mouseup", this.onMouseUp);
26663         
26664         this.fireEvent("hide", this);
26665     },
26666     
26667     onMouseUp : function()
26668     {
26669         this.hide();
26670     }
26671     
26672 });
26673
26674  
26675  /*
26676  * - LGPL
26677  *
26678  * menu item
26679  * 
26680  */
26681 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26682
26683 /**
26684  * @class Roo.bootstrap.menu.Item
26685  * @extends Roo.bootstrap.Component
26686  * Bootstrap MenuItem class
26687  * @cfg {Boolean} submenu (true | false) default false
26688  * @cfg {String} html text of the item
26689  * @cfg {String} href the link
26690  * @cfg {Boolean} disable (true | false) default false
26691  * @cfg {Boolean} preventDefault (true | false) default true
26692  * @cfg {String} icon Font awesome icon
26693  * @cfg {String} pos Submenu align to (left | right) default right 
26694  * 
26695  * 
26696  * @constructor
26697  * Create a new Item
26698  * @param {Object} config The config object
26699  */
26700
26701
26702 Roo.bootstrap.menu.Item = function(config){
26703     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26704     this.addEvents({
26705         /**
26706          * @event mouseover
26707          * Fires when the mouse is hovering over this menu
26708          * @param {Roo.bootstrap.menu.Item} this
26709          * @param {Roo.EventObject} e
26710          */
26711         mouseover : true,
26712         /**
26713          * @event mouseout
26714          * Fires when the mouse exits this menu
26715          * @param {Roo.bootstrap.menu.Item} this
26716          * @param {Roo.EventObject} e
26717          */
26718         mouseout : true,
26719         // raw events
26720         /**
26721          * @event click
26722          * The raw click event for the entire grid.
26723          * @param {Roo.EventObject} e
26724          */
26725         click : true
26726     });
26727 };
26728
26729 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26730     
26731     submenu : false,
26732     href : '',
26733     html : '',
26734     preventDefault: true,
26735     disable : false,
26736     icon : false,
26737     pos : 'right',
26738     
26739     getAutoCreate : function()
26740     {
26741         var text = [
26742             {
26743                 tag : 'span',
26744                 cls : 'roo-menu-item-text',
26745                 html : this.html
26746             }
26747         ];
26748         
26749         if(this.icon){
26750             text.unshift({
26751                 tag : 'i',
26752                 cls : 'fa ' + this.icon
26753             })
26754         }
26755         
26756         var cfg = {
26757             tag : 'li',
26758             cn : [
26759                 {
26760                     tag : 'a',
26761                     href : this.href || '#',
26762                     cn : text
26763                 }
26764             ]
26765         };
26766         
26767         if(this.disable){
26768             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26769         }
26770         
26771         if(this.submenu){
26772             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26773             
26774             if(this.pos == 'left'){
26775                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26776             }
26777         }
26778         
26779         return cfg;
26780     },
26781     
26782     initEvents : function() 
26783     {
26784         this.el.on('mouseover', this.onMouseOver, this);
26785         this.el.on('mouseout', this.onMouseOut, this);
26786         
26787         this.el.select('a', true).first().on('click', this.onClick, this);
26788         
26789     },
26790     
26791     onClick : function(e)
26792     {
26793         if(this.preventDefault){
26794             e.preventDefault();
26795         }
26796         
26797         this.fireEvent("click", this, e);
26798     },
26799     
26800     onMouseOver : function(e)
26801     {
26802         if(this.submenu && this.pos == 'left'){
26803             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26804         }
26805         
26806         this.fireEvent("mouseover", this, e);
26807     },
26808     
26809     onMouseOut : function(e)
26810     {
26811         this.fireEvent("mouseout", this, e);
26812     }
26813 });
26814
26815  
26816
26817  /*
26818  * - LGPL
26819  *
26820  * menu separator
26821  * 
26822  */
26823 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26824
26825 /**
26826  * @class Roo.bootstrap.menu.Separator
26827  * @extends Roo.bootstrap.Component
26828  * Bootstrap Separator class
26829  * 
26830  * @constructor
26831  * Create a new Separator
26832  * @param {Object} config The config object
26833  */
26834
26835
26836 Roo.bootstrap.menu.Separator = function(config){
26837     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26838 };
26839
26840 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26841     
26842     getAutoCreate : function(){
26843         var cfg = {
26844             tag : 'li',
26845             cls: 'divider'
26846         };
26847         
26848         return cfg;
26849     }
26850    
26851 });
26852
26853  
26854
26855  /*
26856  * - LGPL
26857  *
26858  * Tooltip
26859  * 
26860  */
26861
26862 /**
26863  * @class Roo.bootstrap.Tooltip
26864  * Bootstrap Tooltip class
26865  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26866  * to determine which dom element triggers the tooltip.
26867  * 
26868  * It needs to add support for additional attributes like tooltip-position
26869  * 
26870  * @constructor
26871  * Create a new Toolti
26872  * @param {Object} config The config object
26873  */
26874
26875 Roo.bootstrap.Tooltip = function(config){
26876     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26877     
26878     this.alignment = Roo.bootstrap.Tooltip.alignment;
26879     
26880     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26881         this.alignment = config.alignment;
26882     }
26883     
26884 };
26885
26886 Roo.apply(Roo.bootstrap.Tooltip, {
26887     /**
26888      * @function init initialize tooltip monitoring.
26889      * @static
26890      */
26891     currentEl : false,
26892     currentTip : false,
26893     currentRegion : false,
26894     
26895     //  init : delay?
26896     
26897     init : function()
26898     {
26899         Roo.get(document).on('mouseover', this.enter ,this);
26900         Roo.get(document).on('mouseout', this.leave, this);
26901          
26902         
26903         this.currentTip = new Roo.bootstrap.Tooltip();
26904     },
26905     
26906     enter : function(ev)
26907     {
26908         var dom = ev.getTarget();
26909         
26910         //Roo.log(['enter',dom]);
26911         var el = Roo.fly(dom);
26912         if (this.currentEl) {
26913             //Roo.log(dom);
26914             //Roo.log(this.currentEl);
26915             //Roo.log(this.currentEl.contains(dom));
26916             if (this.currentEl == el) {
26917                 return;
26918             }
26919             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26920                 return;
26921             }
26922
26923         }
26924         
26925         if (this.currentTip.el) {
26926             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26927         }    
26928         //Roo.log(ev);
26929         
26930         if(!el || el.dom == document){
26931             return;
26932         }
26933         
26934         var bindEl = el;
26935         
26936         // you can not look for children, as if el is the body.. then everythign is the child..
26937         if (!el.attr('tooltip')) { //
26938             if (!el.select("[tooltip]").elements.length) {
26939                 return;
26940             }
26941             // is the mouse over this child...?
26942             bindEl = el.select("[tooltip]").first();
26943             var xy = ev.getXY();
26944             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26945                 //Roo.log("not in region.");
26946                 return;
26947             }
26948             //Roo.log("child element over..");
26949             
26950         }
26951         this.currentEl = bindEl;
26952         this.currentTip.bind(bindEl);
26953         this.currentRegion = Roo.lib.Region.getRegion(dom);
26954         this.currentTip.enter();
26955         
26956     },
26957     leave : function(ev)
26958     {
26959         var dom = ev.getTarget();
26960         //Roo.log(['leave',dom]);
26961         if (!this.currentEl) {
26962             return;
26963         }
26964         
26965         
26966         if (dom != this.currentEl.dom) {
26967             return;
26968         }
26969         var xy = ev.getXY();
26970         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26971             return;
26972         }
26973         // only activate leave if mouse cursor is outside... bounding box..
26974         
26975         
26976         
26977         
26978         if (this.currentTip) {
26979             this.currentTip.leave();
26980         }
26981         //Roo.log('clear currentEl');
26982         this.currentEl = false;
26983         
26984         
26985     },
26986     alignment : {
26987         'left' : ['r-l', [-2,0], 'right'],
26988         'right' : ['l-r', [2,0], 'left'],
26989         'bottom' : ['t-b', [0,2], 'top'],
26990         'top' : [ 'b-t', [0,-2], 'bottom']
26991     }
26992     
26993 });
26994
26995
26996 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26997     
26998     
26999     bindEl : false,
27000     
27001     delay : null, // can be { show : 300 , hide: 500}
27002     
27003     timeout : null,
27004     
27005     hoverState : null, //???
27006     
27007     placement : 'bottom', 
27008     
27009     alignment : false,
27010     
27011     getAutoCreate : function(){
27012     
27013         var cfg = {
27014            cls : 'tooltip',
27015            role : 'tooltip',
27016            cn : [
27017                 {
27018                     cls : 'tooltip-arrow'
27019                 },
27020                 {
27021                     cls : 'tooltip-inner'
27022                 }
27023            ]
27024         };
27025         
27026         return cfg;
27027     },
27028     bind : function(el)
27029     {
27030         this.bindEl = el;
27031     },
27032       
27033     
27034     enter : function () {
27035        
27036         if (this.timeout != null) {
27037             clearTimeout(this.timeout);
27038         }
27039         
27040         this.hoverState = 'in';
27041          //Roo.log("enter - show");
27042         if (!this.delay || !this.delay.show) {
27043             this.show();
27044             return;
27045         }
27046         var _t = this;
27047         this.timeout = setTimeout(function () {
27048             if (_t.hoverState == 'in') {
27049                 _t.show();
27050             }
27051         }, this.delay.show);
27052     },
27053     leave : function()
27054     {
27055         clearTimeout(this.timeout);
27056     
27057         this.hoverState = 'out';
27058          if (!this.delay || !this.delay.hide) {
27059             this.hide();
27060             return;
27061         }
27062        
27063         var _t = this;
27064         this.timeout = setTimeout(function () {
27065             //Roo.log("leave - timeout");
27066             
27067             if (_t.hoverState == 'out') {
27068                 _t.hide();
27069                 Roo.bootstrap.Tooltip.currentEl = false;
27070             }
27071         }, delay);
27072     },
27073     
27074     show : function (msg)
27075     {
27076         if (!this.el) {
27077             this.render(document.body);
27078         }
27079         // set content.
27080         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27081         
27082         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27083         
27084         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27085         
27086         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27087         
27088         var placement = typeof this.placement == 'function' ?
27089             this.placement.call(this, this.el, on_el) :
27090             this.placement;
27091             
27092         var autoToken = /\s?auto?\s?/i;
27093         var autoPlace = autoToken.test(placement);
27094         if (autoPlace) {
27095             placement = placement.replace(autoToken, '') || 'top';
27096         }
27097         
27098         //this.el.detach()
27099         //this.el.setXY([0,0]);
27100         this.el.show();
27101         //this.el.dom.style.display='block';
27102         
27103         //this.el.appendTo(on_el);
27104         
27105         var p = this.getPosition();
27106         var box = this.el.getBox();
27107         
27108         if (autoPlace) {
27109             // fixme..
27110         }
27111         
27112         var align = this.alignment[placement];
27113         
27114         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27115         
27116         if(placement == 'top' || placement == 'bottom'){
27117             if(xy[0] < 0){
27118                 placement = 'right';
27119             }
27120             
27121             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27122                 placement = 'left';
27123             }
27124             
27125             var scroll = Roo.select('body', true).first().getScroll();
27126             
27127             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27128                 placement = 'top';
27129             }
27130             
27131             align = this.alignment[placement];
27132         }
27133         
27134         this.el.alignTo(this.bindEl, align[0],align[1]);
27135         //var arrow = this.el.select('.arrow',true).first();
27136         //arrow.set(align[2], 
27137         
27138         this.el.addClass(placement);
27139         
27140         this.el.addClass('in fade');
27141         
27142         this.hoverState = null;
27143         
27144         if (this.el.hasClass('fade')) {
27145             // fade it?
27146         }
27147         
27148     },
27149     hide : function()
27150     {
27151          
27152         if (!this.el) {
27153             return;
27154         }
27155         //this.el.setXY([0,0]);
27156         this.el.removeClass('in');
27157         //this.el.hide();
27158         
27159     }
27160     
27161 });
27162  
27163
27164  /*
27165  * - LGPL
27166  *
27167  * Location Picker
27168  * 
27169  */
27170
27171 /**
27172  * @class Roo.bootstrap.LocationPicker
27173  * @extends Roo.bootstrap.Component
27174  * Bootstrap LocationPicker class
27175  * @cfg {Number} latitude Position when init default 0
27176  * @cfg {Number} longitude Position when init default 0
27177  * @cfg {Number} zoom default 15
27178  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27179  * @cfg {Boolean} mapTypeControl default false
27180  * @cfg {Boolean} disableDoubleClickZoom default false
27181  * @cfg {Boolean} scrollwheel default true
27182  * @cfg {Boolean} streetViewControl default false
27183  * @cfg {Number} radius default 0
27184  * @cfg {String} locationName
27185  * @cfg {Boolean} draggable default true
27186  * @cfg {Boolean} enableAutocomplete default false
27187  * @cfg {Boolean} enableReverseGeocode default true
27188  * @cfg {String} markerTitle
27189  * 
27190  * @constructor
27191  * Create a new LocationPicker
27192  * @param {Object} config The config object
27193  */
27194
27195
27196 Roo.bootstrap.LocationPicker = function(config){
27197     
27198     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27199     
27200     this.addEvents({
27201         /**
27202          * @event initial
27203          * Fires when the picker initialized.
27204          * @param {Roo.bootstrap.LocationPicker} this
27205          * @param {Google Location} location
27206          */
27207         initial : true,
27208         /**
27209          * @event positionchanged
27210          * Fires when the picker position changed.
27211          * @param {Roo.bootstrap.LocationPicker} this
27212          * @param {Google Location} location
27213          */
27214         positionchanged : true,
27215         /**
27216          * @event resize
27217          * Fires when the map resize.
27218          * @param {Roo.bootstrap.LocationPicker} this
27219          */
27220         resize : true,
27221         /**
27222          * @event show
27223          * Fires when the map show.
27224          * @param {Roo.bootstrap.LocationPicker} this
27225          */
27226         show : true,
27227         /**
27228          * @event hide
27229          * Fires when the map hide.
27230          * @param {Roo.bootstrap.LocationPicker} this
27231          */
27232         hide : true,
27233         /**
27234          * @event mapClick
27235          * Fires when click the map.
27236          * @param {Roo.bootstrap.LocationPicker} this
27237          * @param {Map event} e
27238          */
27239         mapClick : true,
27240         /**
27241          * @event mapRightClick
27242          * Fires when right click the map.
27243          * @param {Roo.bootstrap.LocationPicker} this
27244          * @param {Map event} e
27245          */
27246         mapRightClick : true,
27247         /**
27248          * @event markerClick
27249          * Fires when click the marker.
27250          * @param {Roo.bootstrap.LocationPicker} this
27251          * @param {Map event} e
27252          */
27253         markerClick : true,
27254         /**
27255          * @event markerRightClick
27256          * Fires when right click the marker.
27257          * @param {Roo.bootstrap.LocationPicker} this
27258          * @param {Map event} e
27259          */
27260         markerRightClick : true,
27261         /**
27262          * @event OverlayViewDraw
27263          * Fires when OverlayView Draw
27264          * @param {Roo.bootstrap.LocationPicker} this
27265          */
27266         OverlayViewDraw : true,
27267         /**
27268          * @event OverlayViewOnAdd
27269          * Fires when OverlayView Draw
27270          * @param {Roo.bootstrap.LocationPicker} this
27271          */
27272         OverlayViewOnAdd : true,
27273         /**
27274          * @event OverlayViewOnRemove
27275          * Fires when OverlayView Draw
27276          * @param {Roo.bootstrap.LocationPicker} this
27277          */
27278         OverlayViewOnRemove : true,
27279         /**
27280          * @event OverlayViewShow
27281          * Fires when OverlayView Draw
27282          * @param {Roo.bootstrap.LocationPicker} this
27283          * @param {Pixel} cpx
27284          */
27285         OverlayViewShow : true,
27286         /**
27287          * @event OverlayViewHide
27288          * Fires when OverlayView Draw
27289          * @param {Roo.bootstrap.LocationPicker} this
27290          */
27291         OverlayViewHide : true,
27292         /**
27293          * @event loadexception
27294          * Fires when load google lib failed.
27295          * @param {Roo.bootstrap.LocationPicker} this
27296          */
27297         loadexception : true
27298     });
27299         
27300 };
27301
27302 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27303     
27304     gMapContext: false,
27305     
27306     latitude: 0,
27307     longitude: 0,
27308     zoom: 15,
27309     mapTypeId: false,
27310     mapTypeControl: false,
27311     disableDoubleClickZoom: false,
27312     scrollwheel: true,
27313     streetViewControl: false,
27314     radius: 0,
27315     locationName: '',
27316     draggable: true,
27317     enableAutocomplete: false,
27318     enableReverseGeocode: true,
27319     markerTitle: '',
27320     
27321     getAutoCreate: function()
27322     {
27323
27324         var cfg = {
27325             tag: 'div',
27326             cls: 'roo-location-picker'
27327         };
27328         
27329         return cfg
27330     },
27331     
27332     initEvents: function(ct, position)
27333     {       
27334         if(!this.el.getWidth() || this.isApplied()){
27335             return;
27336         }
27337         
27338         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27339         
27340         this.initial();
27341     },
27342     
27343     initial: function()
27344     {
27345         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27346             this.fireEvent('loadexception', this);
27347             return;
27348         }
27349         
27350         if(!this.mapTypeId){
27351             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27352         }
27353         
27354         this.gMapContext = this.GMapContext();
27355         
27356         this.initOverlayView();
27357         
27358         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27359         
27360         var _this = this;
27361                 
27362         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27363             _this.setPosition(_this.gMapContext.marker.position);
27364         });
27365         
27366         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27367             _this.fireEvent('mapClick', this, event);
27368             
27369         });
27370
27371         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27372             _this.fireEvent('mapRightClick', this, event);
27373             
27374         });
27375         
27376         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27377             _this.fireEvent('markerClick', this, event);
27378             
27379         });
27380
27381         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27382             _this.fireEvent('markerRightClick', this, event);
27383             
27384         });
27385         
27386         this.setPosition(this.gMapContext.location);
27387         
27388         this.fireEvent('initial', this, this.gMapContext.location);
27389     },
27390     
27391     initOverlayView: function()
27392     {
27393         var _this = this;
27394         
27395         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27396             
27397             draw: function()
27398             {
27399                 _this.fireEvent('OverlayViewDraw', _this);
27400             },
27401             
27402             onAdd: function()
27403             {
27404                 _this.fireEvent('OverlayViewOnAdd', _this);
27405             },
27406             
27407             onRemove: function()
27408             {
27409                 _this.fireEvent('OverlayViewOnRemove', _this);
27410             },
27411             
27412             show: function(cpx)
27413             {
27414                 _this.fireEvent('OverlayViewShow', _this, cpx);
27415             },
27416             
27417             hide: function()
27418             {
27419                 _this.fireEvent('OverlayViewHide', _this);
27420             }
27421             
27422         });
27423     },
27424     
27425     fromLatLngToContainerPixel: function(event)
27426     {
27427         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27428     },
27429     
27430     isApplied: function() 
27431     {
27432         return this.getGmapContext() == false ? false : true;
27433     },
27434     
27435     getGmapContext: function() 
27436     {
27437         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27438     },
27439     
27440     GMapContext: function() 
27441     {
27442         var position = new google.maps.LatLng(this.latitude, this.longitude);
27443         
27444         var _map = new google.maps.Map(this.el.dom, {
27445             center: position,
27446             zoom: this.zoom,
27447             mapTypeId: this.mapTypeId,
27448             mapTypeControl: this.mapTypeControl,
27449             disableDoubleClickZoom: this.disableDoubleClickZoom,
27450             scrollwheel: this.scrollwheel,
27451             streetViewControl: this.streetViewControl,
27452             locationName: this.locationName,
27453             draggable: this.draggable,
27454             enableAutocomplete: this.enableAutocomplete,
27455             enableReverseGeocode: this.enableReverseGeocode
27456         });
27457         
27458         var _marker = new google.maps.Marker({
27459             position: position,
27460             map: _map,
27461             title: this.markerTitle,
27462             draggable: this.draggable
27463         });
27464         
27465         return {
27466             map: _map,
27467             marker: _marker,
27468             circle: null,
27469             location: position,
27470             radius: this.radius,
27471             locationName: this.locationName,
27472             addressComponents: {
27473                 formatted_address: null,
27474                 addressLine1: null,
27475                 addressLine2: null,
27476                 streetName: null,
27477                 streetNumber: null,
27478                 city: null,
27479                 district: null,
27480                 state: null,
27481                 stateOrProvince: null
27482             },
27483             settings: this,
27484             domContainer: this.el.dom,
27485             geodecoder: new google.maps.Geocoder()
27486         };
27487     },
27488     
27489     drawCircle: function(center, radius, options) 
27490     {
27491         if (this.gMapContext.circle != null) {
27492             this.gMapContext.circle.setMap(null);
27493         }
27494         if (radius > 0) {
27495             radius *= 1;
27496             options = Roo.apply({}, options, {
27497                 strokeColor: "#0000FF",
27498                 strokeOpacity: .35,
27499                 strokeWeight: 2,
27500                 fillColor: "#0000FF",
27501                 fillOpacity: .2
27502             });
27503             
27504             options.map = this.gMapContext.map;
27505             options.radius = radius;
27506             options.center = center;
27507             this.gMapContext.circle = new google.maps.Circle(options);
27508             return this.gMapContext.circle;
27509         }
27510         
27511         return null;
27512     },
27513     
27514     setPosition: function(location) 
27515     {
27516         this.gMapContext.location = location;
27517         this.gMapContext.marker.setPosition(location);
27518         this.gMapContext.map.panTo(location);
27519         this.drawCircle(location, this.gMapContext.radius, {});
27520         
27521         var _this = this;
27522         
27523         if (this.gMapContext.settings.enableReverseGeocode) {
27524             this.gMapContext.geodecoder.geocode({
27525                 latLng: this.gMapContext.location
27526             }, function(results, status) {
27527                 
27528                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27529                     _this.gMapContext.locationName = results[0].formatted_address;
27530                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27531                     
27532                     _this.fireEvent('positionchanged', this, location);
27533                 }
27534             });
27535             
27536             return;
27537         }
27538         
27539         this.fireEvent('positionchanged', this, location);
27540     },
27541     
27542     resize: function()
27543     {
27544         google.maps.event.trigger(this.gMapContext.map, "resize");
27545         
27546         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27547         
27548         this.fireEvent('resize', this);
27549     },
27550     
27551     setPositionByLatLng: function(latitude, longitude)
27552     {
27553         this.setPosition(new google.maps.LatLng(latitude, longitude));
27554     },
27555     
27556     getCurrentPosition: function() 
27557     {
27558         return {
27559             latitude: this.gMapContext.location.lat(),
27560             longitude: this.gMapContext.location.lng()
27561         };
27562     },
27563     
27564     getAddressName: function() 
27565     {
27566         return this.gMapContext.locationName;
27567     },
27568     
27569     getAddressComponents: function() 
27570     {
27571         return this.gMapContext.addressComponents;
27572     },
27573     
27574     address_component_from_google_geocode: function(address_components) 
27575     {
27576         var result = {};
27577         
27578         for (var i = 0; i < address_components.length; i++) {
27579             var component = address_components[i];
27580             if (component.types.indexOf("postal_code") >= 0) {
27581                 result.postalCode = component.short_name;
27582             } else if (component.types.indexOf("street_number") >= 0) {
27583                 result.streetNumber = component.short_name;
27584             } else if (component.types.indexOf("route") >= 0) {
27585                 result.streetName = component.short_name;
27586             } else if (component.types.indexOf("neighborhood") >= 0) {
27587                 result.city = component.short_name;
27588             } else if (component.types.indexOf("locality") >= 0) {
27589                 result.city = component.short_name;
27590             } else if (component.types.indexOf("sublocality") >= 0) {
27591                 result.district = component.short_name;
27592             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27593                 result.stateOrProvince = component.short_name;
27594             } else if (component.types.indexOf("country") >= 0) {
27595                 result.country = component.short_name;
27596             }
27597         }
27598         
27599         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27600         result.addressLine2 = "";
27601         return result;
27602     },
27603     
27604     setZoomLevel: function(zoom)
27605     {
27606         this.gMapContext.map.setZoom(zoom);
27607     },
27608     
27609     show: function()
27610     {
27611         if(!this.el){
27612             return;
27613         }
27614         
27615         this.el.show();
27616         
27617         this.resize();
27618         
27619         this.fireEvent('show', this);
27620     },
27621     
27622     hide: function()
27623     {
27624         if(!this.el){
27625             return;
27626         }
27627         
27628         this.el.hide();
27629         
27630         this.fireEvent('hide', this);
27631     }
27632     
27633 });
27634
27635 Roo.apply(Roo.bootstrap.LocationPicker, {
27636     
27637     OverlayView : function(map, options)
27638     {
27639         options = options || {};
27640         
27641         this.setMap(map);
27642     }
27643     
27644     
27645 });/**
27646  * @class Roo.bootstrap.Alert
27647  * @extends Roo.bootstrap.Component
27648  * Bootstrap Alert class - shows an alert area box
27649  * eg
27650  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27651   Enter a valid email address
27652 </div>
27653  * @licence LGPL
27654  * @cfg {String} title The title of alert
27655  * @cfg {String} html The content of alert
27656  * @cfg {String} weight (  success | info | warning | danger )
27657  * @cfg {String} faicon font-awesomeicon
27658  * 
27659  * @constructor
27660  * Create a new alert
27661  * @param {Object} config The config object
27662  */
27663
27664
27665 Roo.bootstrap.Alert = function(config){
27666     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27667     
27668 };
27669
27670 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27671     
27672     title: '',
27673     html: '',
27674     weight: false,
27675     faicon: false,
27676     
27677     getAutoCreate : function()
27678     {
27679         
27680         var cfg = {
27681             tag : 'div',
27682             cls : 'alert',
27683             cn : [
27684                 {
27685                     tag : 'i',
27686                     cls : 'roo-alert-icon'
27687                     
27688                 },
27689                 {
27690                     tag : 'b',
27691                     cls : 'roo-alert-title',
27692                     html : this.title
27693                 },
27694                 {
27695                     tag : 'span',
27696                     cls : 'roo-alert-text',
27697                     html : this.html
27698                 }
27699             ]
27700         };
27701         
27702         if(this.faicon){
27703             cfg.cn[0].cls += ' fa ' + this.faicon;
27704         }
27705         
27706         if(this.weight){
27707             cfg.cls += ' alert-' + this.weight;
27708         }
27709         
27710         return cfg;
27711     },
27712     
27713     initEvents: function() 
27714     {
27715         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27716     },
27717     
27718     setTitle : function(str)
27719     {
27720         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27721     },
27722     
27723     setText : function(str)
27724     {
27725         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27726     },
27727     
27728     setWeight : function(weight)
27729     {
27730         if(this.weight){
27731             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27732         }
27733         
27734         this.weight = weight;
27735         
27736         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27737     },
27738     
27739     setIcon : function(icon)
27740     {
27741         if(this.faicon){
27742             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27743         }
27744         
27745         this.faicon = icon;
27746         
27747         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27748     },
27749     
27750     hide: function() 
27751     {
27752         this.el.hide();   
27753     },
27754     
27755     show: function() 
27756     {  
27757         this.el.show();   
27758     }
27759     
27760 });
27761
27762  
27763 /*
27764 * Licence: LGPL
27765 */
27766
27767 /**
27768  * @class Roo.bootstrap.UploadCropbox
27769  * @extends Roo.bootstrap.Component
27770  * Bootstrap UploadCropbox class
27771  * @cfg {String} emptyText show when image has been loaded
27772  * @cfg {String} rotateNotify show when image too small to rotate
27773  * @cfg {Number} errorTimeout default 3000
27774  * @cfg {Number} minWidth default 300
27775  * @cfg {Number} minHeight default 300
27776  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27777  * @cfg {Boolean} isDocument (true|false) default false
27778  * @cfg {String} url action url
27779  * @cfg {String} paramName default 'imageUpload'
27780  * @cfg {String} method default POST
27781  * @cfg {Boolean} loadMask (true|false) default true
27782  * @cfg {Boolean} loadingText default 'Loading...'
27783  * 
27784  * @constructor
27785  * Create a new UploadCropbox
27786  * @param {Object} config The config object
27787  */
27788
27789 Roo.bootstrap.UploadCropbox = function(config){
27790     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27791     
27792     this.addEvents({
27793         /**
27794          * @event beforeselectfile
27795          * Fire before select file
27796          * @param {Roo.bootstrap.UploadCropbox} this
27797          */
27798         "beforeselectfile" : true,
27799         /**
27800          * @event initial
27801          * Fire after initEvent
27802          * @param {Roo.bootstrap.UploadCropbox} this
27803          */
27804         "initial" : true,
27805         /**
27806          * @event crop
27807          * Fire after initEvent
27808          * @param {Roo.bootstrap.UploadCropbox} this
27809          * @param {String} data
27810          */
27811         "crop" : true,
27812         /**
27813          * @event prepare
27814          * Fire when preparing the file data
27815          * @param {Roo.bootstrap.UploadCropbox} this
27816          * @param {Object} file
27817          */
27818         "prepare" : true,
27819         /**
27820          * @event exception
27821          * Fire when get exception
27822          * @param {Roo.bootstrap.UploadCropbox} this
27823          * @param {XMLHttpRequest} xhr
27824          */
27825         "exception" : true,
27826         /**
27827          * @event beforeloadcanvas
27828          * Fire before load the canvas
27829          * @param {Roo.bootstrap.UploadCropbox} this
27830          * @param {String} src
27831          */
27832         "beforeloadcanvas" : true,
27833         /**
27834          * @event trash
27835          * Fire when trash image
27836          * @param {Roo.bootstrap.UploadCropbox} this
27837          */
27838         "trash" : true,
27839         /**
27840          * @event download
27841          * Fire when download the image
27842          * @param {Roo.bootstrap.UploadCropbox} this
27843          */
27844         "download" : true,
27845         /**
27846          * @event footerbuttonclick
27847          * Fire when footerbuttonclick
27848          * @param {Roo.bootstrap.UploadCropbox} this
27849          * @param {String} type
27850          */
27851         "footerbuttonclick" : true,
27852         /**
27853          * @event resize
27854          * Fire when resize
27855          * @param {Roo.bootstrap.UploadCropbox} this
27856          */
27857         "resize" : true,
27858         /**
27859          * @event rotate
27860          * Fire when rotate the image
27861          * @param {Roo.bootstrap.UploadCropbox} this
27862          * @param {String} pos
27863          */
27864         "rotate" : true,
27865         /**
27866          * @event inspect
27867          * Fire when inspect the file
27868          * @param {Roo.bootstrap.UploadCropbox} this
27869          * @param {Object} file
27870          */
27871         "inspect" : true,
27872         /**
27873          * @event upload
27874          * Fire when xhr upload the file
27875          * @param {Roo.bootstrap.UploadCropbox} this
27876          * @param {Object} data
27877          */
27878         "upload" : true,
27879         /**
27880          * @event arrange
27881          * Fire when arrange the file data
27882          * @param {Roo.bootstrap.UploadCropbox} this
27883          * @param {Object} formData
27884          */
27885         "arrange" : true
27886     });
27887     
27888     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27889 };
27890
27891 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27892     
27893     emptyText : 'Click to upload image',
27894     rotateNotify : 'Image is too small to rotate',
27895     errorTimeout : 3000,
27896     scale : 0,
27897     baseScale : 1,
27898     rotate : 0,
27899     dragable : false,
27900     pinching : false,
27901     mouseX : 0,
27902     mouseY : 0,
27903     cropData : false,
27904     minWidth : 300,
27905     minHeight : 300,
27906     file : false,
27907     exif : {},
27908     baseRotate : 1,
27909     cropType : 'image/jpeg',
27910     buttons : false,
27911     canvasLoaded : false,
27912     isDocument : false,
27913     method : 'POST',
27914     paramName : 'imageUpload',
27915     loadMask : true,
27916     loadingText : 'Loading...',
27917     maskEl : false,
27918     
27919     getAutoCreate : function()
27920     {
27921         var cfg = {
27922             tag : 'div',
27923             cls : 'roo-upload-cropbox',
27924             cn : [
27925                 {
27926                     tag : 'input',
27927                     cls : 'roo-upload-cropbox-selector',
27928                     type : 'file'
27929                 },
27930                 {
27931                     tag : 'div',
27932                     cls : 'roo-upload-cropbox-body',
27933                     style : 'cursor:pointer',
27934                     cn : [
27935                         {
27936                             tag : 'div',
27937                             cls : 'roo-upload-cropbox-preview'
27938                         },
27939                         {
27940                             tag : 'div',
27941                             cls : 'roo-upload-cropbox-thumb'
27942                         },
27943                         {
27944                             tag : 'div',
27945                             cls : 'roo-upload-cropbox-empty-notify',
27946                             html : this.emptyText
27947                         },
27948                         {
27949                             tag : 'div',
27950                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27951                             html : this.rotateNotify
27952                         }
27953                     ]
27954                 },
27955                 {
27956                     tag : 'div',
27957                     cls : 'roo-upload-cropbox-footer',
27958                     cn : {
27959                         tag : 'div',
27960                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27961                         cn : []
27962                     }
27963                 }
27964             ]
27965         };
27966         
27967         return cfg;
27968     },
27969     
27970     onRender : function(ct, position)
27971     {
27972         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27973         
27974         if (this.buttons.length) {
27975             
27976             Roo.each(this.buttons, function(bb) {
27977                 
27978                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27979                 
27980                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27981                 
27982             }, this);
27983         }
27984         
27985         if(this.loadMask){
27986             this.maskEl = this.el;
27987         }
27988     },
27989     
27990     initEvents : function()
27991     {
27992         this.urlAPI = (window.createObjectURL && window) || 
27993                                 (window.URL && URL.revokeObjectURL && URL) || 
27994                                 (window.webkitURL && webkitURL);
27995                         
27996         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27997         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27998         
27999         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28000         this.selectorEl.hide();
28001         
28002         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28003         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28004         
28005         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28006         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28007         this.thumbEl.hide();
28008         
28009         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28010         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28011         
28012         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28013         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28014         this.errorEl.hide();
28015         
28016         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28017         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28018         this.footerEl.hide();
28019         
28020         this.setThumbBoxSize();
28021         
28022         this.bind();
28023         
28024         this.resize();
28025         
28026         this.fireEvent('initial', this);
28027     },
28028
28029     bind : function()
28030     {
28031         var _this = this;
28032         
28033         window.addEventListener("resize", function() { _this.resize(); } );
28034         
28035         this.bodyEl.on('click', this.beforeSelectFile, this);
28036         
28037         if(Roo.isTouch){
28038             this.bodyEl.on('touchstart', this.onTouchStart, this);
28039             this.bodyEl.on('touchmove', this.onTouchMove, this);
28040             this.bodyEl.on('touchend', this.onTouchEnd, this);
28041         }
28042         
28043         if(!Roo.isTouch){
28044             this.bodyEl.on('mousedown', this.onMouseDown, this);
28045             this.bodyEl.on('mousemove', this.onMouseMove, this);
28046             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28047             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28048             Roo.get(document).on('mouseup', this.onMouseUp, this);
28049         }
28050         
28051         this.selectorEl.on('change', this.onFileSelected, this);
28052     },
28053     
28054     reset : function()
28055     {    
28056         this.scale = 0;
28057         this.baseScale = 1;
28058         this.rotate = 0;
28059         this.baseRotate = 1;
28060         this.dragable = false;
28061         this.pinching = false;
28062         this.mouseX = 0;
28063         this.mouseY = 0;
28064         this.cropData = false;
28065         this.notifyEl.dom.innerHTML = this.emptyText;
28066         
28067         this.selectorEl.dom.value = '';
28068         
28069     },
28070     
28071     resize : function()
28072     {
28073         if(this.fireEvent('resize', this) != false){
28074             this.setThumbBoxPosition();
28075             this.setCanvasPosition();
28076         }
28077     },
28078     
28079     onFooterButtonClick : function(e, el, o, type)
28080     {
28081         switch (type) {
28082             case 'rotate-left' :
28083                 this.onRotateLeft(e);
28084                 break;
28085             case 'rotate-right' :
28086                 this.onRotateRight(e);
28087                 break;
28088             case 'picture' :
28089                 this.beforeSelectFile(e);
28090                 break;
28091             case 'trash' :
28092                 this.trash(e);
28093                 break;
28094             case 'crop' :
28095                 this.crop(e);
28096                 break;
28097             case 'download' :
28098                 this.download(e);
28099                 break;
28100             default :
28101                 break;
28102         }
28103         
28104         this.fireEvent('footerbuttonclick', this, type);
28105     },
28106     
28107     beforeSelectFile : function(e)
28108     {
28109         e.preventDefault();
28110         
28111         if(this.fireEvent('beforeselectfile', this) != false){
28112             this.selectorEl.dom.click();
28113         }
28114     },
28115     
28116     onFileSelected : function(e)
28117     {
28118         e.preventDefault();
28119         
28120         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28121             return;
28122         }
28123         
28124         var file = this.selectorEl.dom.files[0];
28125         
28126         if(this.fireEvent('inspect', this, file) != false){
28127             this.prepare(file);
28128         }
28129         
28130     },
28131     
28132     trash : function(e)
28133     {
28134         this.fireEvent('trash', this);
28135     },
28136     
28137     download : function(e)
28138     {
28139         this.fireEvent('download', this);
28140     },
28141     
28142     loadCanvas : function(src)
28143     {   
28144         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28145             
28146             this.reset();
28147             
28148             this.imageEl = document.createElement('img');
28149             
28150             var _this = this;
28151             
28152             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28153             
28154             this.imageEl.src = src;
28155         }
28156     },
28157     
28158     onLoadCanvas : function()
28159     {   
28160         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28161         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28162         
28163         this.bodyEl.un('click', this.beforeSelectFile, this);
28164         
28165         this.notifyEl.hide();
28166         this.thumbEl.show();
28167         this.footerEl.show();
28168         
28169         this.baseRotateLevel();
28170         
28171         if(this.isDocument){
28172             this.setThumbBoxSize();
28173         }
28174         
28175         this.setThumbBoxPosition();
28176         
28177         this.baseScaleLevel();
28178         
28179         this.draw();
28180         
28181         this.resize();
28182         
28183         this.canvasLoaded = true;
28184         
28185         if(this.loadMask){
28186             this.maskEl.unmask();
28187         }
28188         
28189     },
28190     
28191     setCanvasPosition : function()
28192     {   
28193         if(!this.canvasEl){
28194             return;
28195         }
28196         
28197         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28198         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28199         
28200         this.previewEl.setLeft(pw);
28201         this.previewEl.setTop(ph);
28202         
28203     },
28204     
28205     onMouseDown : function(e)
28206     {   
28207         e.stopEvent();
28208         
28209         this.dragable = true;
28210         this.pinching = false;
28211         
28212         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28213             this.dragable = false;
28214             return;
28215         }
28216         
28217         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28218         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28219         
28220     },
28221     
28222     onMouseMove : function(e)
28223     {   
28224         e.stopEvent();
28225         
28226         if(!this.canvasLoaded){
28227             return;
28228         }
28229         
28230         if (!this.dragable){
28231             return;
28232         }
28233         
28234         var minX = Math.ceil(this.thumbEl.getLeft(true));
28235         var minY = Math.ceil(this.thumbEl.getTop(true));
28236         
28237         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28238         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28239         
28240         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28241         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28242         
28243         x = x - this.mouseX;
28244         y = y - this.mouseY;
28245         
28246         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28247         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28248         
28249         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28250         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28251         
28252         this.previewEl.setLeft(bgX);
28253         this.previewEl.setTop(bgY);
28254         
28255         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28256         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28257     },
28258     
28259     onMouseUp : function(e)
28260     {   
28261         e.stopEvent();
28262         
28263         this.dragable = false;
28264     },
28265     
28266     onMouseWheel : function(e)
28267     {   
28268         e.stopEvent();
28269         
28270         this.startScale = this.scale;
28271         
28272         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28273         
28274         if(!this.zoomable()){
28275             this.scale = this.startScale;
28276             return;
28277         }
28278         
28279         this.draw();
28280         
28281         return;
28282     },
28283     
28284     zoomable : function()
28285     {
28286         var minScale = this.thumbEl.getWidth() / this.minWidth;
28287         
28288         if(this.minWidth < this.minHeight){
28289             minScale = this.thumbEl.getHeight() / this.minHeight;
28290         }
28291         
28292         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28293         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28294         
28295         if(
28296                 this.isDocument &&
28297                 (this.rotate == 0 || this.rotate == 180) && 
28298                 (
28299                     width > this.imageEl.OriginWidth || 
28300                     height > this.imageEl.OriginHeight ||
28301                     (width < this.minWidth && height < this.minHeight)
28302                 )
28303         ){
28304             return false;
28305         }
28306         
28307         if(
28308                 this.isDocument &&
28309                 (this.rotate == 90 || this.rotate == 270) && 
28310                 (
28311                     width > this.imageEl.OriginWidth || 
28312                     height > this.imageEl.OriginHeight ||
28313                     (width < this.minHeight && height < this.minWidth)
28314                 )
28315         ){
28316             return false;
28317         }
28318         
28319         if(
28320                 !this.isDocument &&
28321                 (this.rotate == 0 || this.rotate == 180) && 
28322                 (
28323                     width < this.minWidth || 
28324                     width > this.imageEl.OriginWidth || 
28325                     height < this.minHeight || 
28326                     height > this.imageEl.OriginHeight
28327                 )
28328         ){
28329             return false;
28330         }
28331         
28332         if(
28333                 !this.isDocument &&
28334                 (this.rotate == 90 || this.rotate == 270) && 
28335                 (
28336                     width < this.minHeight || 
28337                     width > this.imageEl.OriginWidth || 
28338                     height < this.minWidth || 
28339                     height > this.imageEl.OriginHeight
28340                 )
28341         ){
28342             return false;
28343         }
28344         
28345         return true;
28346         
28347     },
28348     
28349     onRotateLeft : function(e)
28350     {   
28351         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28352             
28353             var minScale = this.thumbEl.getWidth() / this.minWidth;
28354             
28355             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28356             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28357             
28358             this.startScale = this.scale;
28359             
28360             while (this.getScaleLevel() < minScale){
28361             
28362                 this.scale = this.scale + 1;
28363                 
28364                 if(!this.zoomable()){
28365                     break;
28366                 }
28367                 
28368                 if(
28369                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28370                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28371                 ){
28372                     continue;
28373                 }
28374                 
28375                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28376
28377                 this.draw();
28378                 
28379                 return;
28380             }
28381             
28382             this.scale = this.startScale;
28383             
28384             this.onRotateFail();
28385             
28386             return false;
28387         }
28388         
28389         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28390
28391         if(this.isDocument){
28392             this.setThumbBoxSize();
28393             this.setThumbBoxPosition();
28394             this.setCanvasPosition();
28395         }
28396         
28397         this.draw();
28398         
28399         this.fireEvent('rotate', this, 'left');
28400         
28401     },
28402     
28403     onRotateRight : function(e)
28404     {
28405         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28406             
28407             var minScale = this.thumbEl.getWidth() / this.minWidth;
28408         
28409             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28410             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28411             
28412             this.startScale = this.scale;
28413             
28414             while (this.getScaleLevel() < minScale){
28415             
28416                 this.scale = this.scale + 1;
28417                 
28418                 if(!this.zoomable()){
28419                     break;
28420                 }
28421                 
28422                 if(
28423                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28424                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28425                 ){
28426                     continue;
28427                 }
28428                 
28429                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28430
28431                 this.draw();
28432                 
28433                 return;
28434             }
28435             
28436             this.scale = this.startScale;
28437             
28438             this.onRotateFail();
28439             
28440             return false;
28441         }
28442         
28443         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28444
28445         if(this.isDocument){
28446             this.setThumbBoxSize();
28447             this.setThumbBoxPosition();
28448             this.setCanvasPosition();
28449         }
28450         
28451         this.draw();
28452         
28453         this.fireEvent('rotate', this, 'right');
28454     },
28455     
28456     onRotateFail : function()
28457     {
28458         this.errorEl.show(true);
28459         
28460         var _this = this;
28461         
28462         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28463     },
28464     
28465     draw : function()
28466     {
28467         this.previewEl.dom.innerHTML = '';
28468         
28469         var canvasEl = document.createElement("canvas");
28470         
28471         var contextEl = canvasEl.getContext("2d");
28472         
28473         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28474         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28475         var center = this.imageEl.OriginWidth / 2;
28476         
28477         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28478             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28479             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28480             center = this.imageEl.OriginHeight / 2;
28481         }
28482         
28483         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28484         
28485         contextEl.translate(center, center);
28486         contextEl.rotate(this.rotate * Math.PI / 180);
28487
28488         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28489         
28490         this.canvasEl = document.createElement("canvas");
28491         
28492         this.contextEl = this.canvasEl.getContext("2d");
28493         
28494         switch (this.rotate) {
28495             case 0 :
28496                 
28497                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28498                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28499                 
28500                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28501                 
28502                 break;
28503             case 90 : 
28504                 
28505                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28506                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28507                 
28508                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28509                     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);
28510                     break;
28511                 }
28512                 
28513                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28514                 
28515                 break;
28516             case 180 :
28517                 
28518                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28519                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28520                 
28521                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28522                     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);
28523                     break;
28524                 }
28525                 
28526                 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);
28527                 
28528                 break;
28529             case 270 :
28530                 
28531                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28532                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28533         
28534                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28535                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28536                     break;
28537                 }
28538                 
28539                 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);
28540                 
28541                 break;
28542             default : 
28543                 break;
28544         }
28545         
28546         this.previewEl.appendChild(this.canvasEl);
28547         
28548         this.setCanvasPosition();
28549     },
28550     
28551     crop : function()
28552     {
28553         if(!this.canvasLoaded){
28554             return;
28555         }
28556         
28557         var imageCanvas = document.createElement("canvas");
28558         
28559         var imageContext = imageCanvas.getContext("2d");
28560         
28561         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28562         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28563         
28564         var center = imageCanvas.width / 2;
28565         
28566         imageContext.translate(center, center);
28567         
28568         imageContext.rotate(this.rotate * Math.PI / 180);
28569         
28570         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28571         
28572         var canvas = document.createElement("canvas");
28573         
28574         var context = canvas.getContext("2d");
28575                 
28576         canvas.width = this.minWidth;
28577         canvas.height = this.minHeight;
28578
28579         switch (this.rotate) {
28580             case 0 :
28581                 
28582                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28583                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28584                 
28585                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28586                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28587                 
28588                 var targetWidth = this.minWidth - 2 * x;
28589                 var targetHeight = this.minHeight - 2 * y;
28590                 
28591                 var scale = 1;
28592                 
28593                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28594                     scale = targetWidth / width;
28595                 }
28596                 
28597                 if(x > 0 && y == 0){
28598                     scale = targetHeight / height;
28599                 }
28600                 
28601                 if(x > 0 && y > 0){
28602                     scale = targetWidth / width;
28603                     
28604                     if(width < height){
28605                         scale = targetHeight / height;
28606                     }
28607                 }
28608                 
28609                 context.scale(scale, scale);
28610                 
28611                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28612                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28613
28614                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28615                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28616
28617                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28618                 
28619                 break;
28620             case 90 : 
28621                 
28622                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28623                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28624                 
28625                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28626                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28627                 
28628                 var targetWidth = this.minWidth - 2 * x;
28629                 var targetHeight = this.minHeight - 2 * y;
28630                 
28631                 var scale = 1;
28632                 
28633                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28634                     scale = targetWidth / width;
28635                 }
28636                 
28637                 if(x > 0 && y == 0){
28638                     scale = targetHeight / height;
28639                 }
28640                 
28641                 if(x > 0 && y > 0){
28642                     scale = targetWidth / width;
28643                     
28644                     if(width < height){
28645                         scale = targetHeight / height;
28646                     }
28647                 }
28648                 
28649                 context.scale(scale, scale);
28650                 
28651                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28652                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28653
28654                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28655                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28656                 
28657                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28658                 
28659                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28660                 
28661                 break;
28662             case 180 :
28663                 
28664                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28665                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28666                 
28667                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28668                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28669                 
28670                 var targetWidth = this.minWidth - 2 * x;
28671                 var targetHeight = this.minHeight - 2 * y;
28672                 
28673                 var scale = 1;
28674                 
28675                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28676                     scale = targetWidth / width;
28677                 }
28678                 
28679                 if(x > 0 && y == 0){
28680                     scale = targetHeight / height;
28681                 }
28682                 
28683                 if(x > 0 && y > 0){
28684                     scale = targetWidth / width;
28685                     
28686                     if(width < height){
28687                         scale = targetHeight / height;
28688                     }
28689                 }
28690                 
28691                 context.scale(scale, scale);
28692                 
28693                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28694                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28695
28696                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28697                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28698
28699                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28700                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28701                 
28702                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28703                 
28704                 break;
28705             case 270 :
28706                 
28707                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28708                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28709                 
28710                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28711                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28712                 
28713                 var targetWidth = this.minWidth - 2 * x;
28714                 var targetHeight = this.minHeight - 2 * y;
28715                 
28716                 var scale = 1;
28717                 
28718                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28719                     scale = targetWidth / width;
28720                 }
28721                 
28722                 if(x > 0 && y == 0){
28723                     scale = targetHeight / height;
28724                 }
28725                 
28726                 if(x > 0 && y > 0){
28727                     scale = targetWidth / width;
28728                     
28729                     if(width < height){
28730                         scale = targetHeight / height;
28731                     }
28732                 }
28733                 
28734                 context.scale(scale, scale);
28735                 
28736                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28737                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28738
28739                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28740                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28741                 
28742                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28743                 
28744                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28745                 
28746                 break;
28747             default : 
28748                 break;
28749         }
28750         
28751         this.cropData = canvas.toDataURL(this.cropType);
28752         
28753         if(this.fireEvent('crop', this, this.cropData) !== false){
28754             this.process(this.file, this.cropData);
28755         }
28756         
28757         return;
28758         
28759     },
28760     
28761     setThumbBoxSize : function()
28762     {
28763         var width, height;
28764         
28765         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28766             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28767             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28768             
28769             this.minWidth = width;
28770             this.minHeight = height;
28771             
28772             if(this.rotate == 90 || this.rotate == 270){
28773                 this.minWidth = height;
28774                 this.minHeight = width;
28775             }
28776         }
28777         
28778         height = 300;
28779         width = Math.ceil(this.minWidth * height / this.minHeight);
28780         
28781         if(this.minWidth > this.minHeight){
28782             width = 300;
28783             height = Math.ceil(this.minHeight * width / this.minWidth);
28784         }
28785         
28786         this.thumbEl.setStyle({
28787             width : width + 'px',
28788             height : height + 'px'
28789         });
28790
28791         return;
28792             
28793     },
28794     
28795     setThumbBoxPosition : function()
28796     {
28797         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28798         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28799         
28800         this.thumbEl.setLeft(x);
28801         this.thumbEl.setTop(y);
28802         
28803     },
28804     
28805     baseRotateLevel : function()
28806     {
28807         this.baseRotate = 1;
28808         
28809         if(
28810                 typeof(this.exif) != 'undefined' &&
28811                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28812                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28813         ){
28814             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28815         }
28816         
28817         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28818         
28819     },
28820     
28821     baseScaleLevel : function()
28822     {
28823         var width, height;
28824         
28825         if(this.isDocument){
28826             
28827             if(this.baseRotate == 6 || this.baseRotate == 8){
28828             
28829                 height = this.thumbEl.getHeight();
28830                 this.baseScale = height / this.imageEl.OriginWidth;
28831
28832                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28833                     width = this.thumbEl.getWidth();
28834                     this.baseScale = width / this.imageEl.OriginHeight;
28835                 }
28836
28837                 return;
28838             }
28839
28840             height = this.thumbEl.getHeight();
28841             this.baseScale = height / this.imageEl.OriginHeight;
28842
28843             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28844                 width = this.thumbEl.getWidth();
28845                 this.baseScale = width / this.imageEl.OriginWidth;
28846             }
28847
28848             return;
28849         }
28850         
28851         if(this.baseRotate == 6 || this.baseRotate == 8){
28852             
28853             width = this.thumbEl.getHeight();
28854             this.baseScale = width / this.imageEl.OriginHeight;
28855             
28856             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28857                 height = this.thumbEl.getWidth();
28858                 this.baseScale = height / this.imageEl.OriginHeight;
28859             }
28860             
28861             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28862                 height = this.thumbEl.getWidth();
28863                 this.baseScale = height / this.imageEl.OriginHeight;
28864                 
28865                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28866                     width = this.thumbEl.getHeight();
28867                     this.baseScale = width / this.imageEl.OriginWidth;
28868                 }
28869             }
28870             
28871             return;
28872         }
28873         
28874         width = this.thumbEl.getWidth();
28875         this.baseScale = width / this.imageEl.OriginWidth;
28876         
28877         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28878             height = this.thumbEl.getHeight();
28879             this.baseScale = height / this.imageEl.OriginHeight;
28880         }
28881         
28882         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28883             
28884             height = this.thumbEl.getHeight();
28885             this.baseScale = height / this.imageEl.OriginHeight;
28886             
28887             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28888                 width = this.thumbEl.getWidth();
28889                 this.baseScale = width / this.imageEl.OriginWidth;
28890             }
28891             
28892         }
28893         
28894         return;
28895     },
28896     
28897     getScaleLevel : function()
28898     {
28899         return this.baseScale * Math.pow(1.1, this.scale);
28900     },
28901     
28902     onTouchStart : function(e)
28903     {
28904         if(!this.canvasLoaded){
28905             this.beforeSelectFile(e);
28906             return;
28907         }
28908         
28909         var touches = e.browserEvent.touches;
28910         
28911         if(!touches){
28912             return;
28913         }
28914         
28915         if(touches.length == 1){
28916             this.onMouseDown(e);
28917             return;
28918         }
28919         
28920         if(touches.length != 2){
28921             return;
28922         }
28923         
28924         var coords = [];
28925         
28926         for(var i = 0, finger; finger = touches[i]; i++){
28927             coords.push(finger.pageX, finger.pageY);
28928         }
28929         
28930         var x = Math.pow(coords[0] - coords[2], 2);
28931         var y = Math.pow(coords[1] - coords[3], 2);
28932         
28933         this.startDistance = Math.sqrt(x + y);
28934         
28935         this.startScale = this.scale;
28936         
28937         this.pinching = true;
28938         this.dragable = false;
28939         
28940     },
28941     
28942     onTouchMove : function(e)
28943     {
28944         if(!this.pinching && !this.dragable){
28945             return;
28946         }
28947         
28948         var touches = e.browserEvent.touches;
28949         
28950         if(!touches){
28951             return;
28952         }
28953         
28954         if(this.dragable){
28955             this.onMouseMove(e);
28956             return;
28957         }
28958         
28959         var coords = [];
28960         
28961         for(var i = 0, finger; finger = touches[i]; i++){
28962             coords.push(finger.pageX, finger.pageY);
28963         }
28964         
28965         var x = Math.pow(coords[0] - coords[2], 2);
28966         var y = Math.pow(coords[1] - coords[3], 2);
28967         
28968         this.endDistance = Math.sqrt(x + y);
28969         
28970         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28971         
28972         if(!this.zoomable()){
28973             this.scale = this.startScale;
28974             return;
28975         }
28976         
28977         this.draw();
28978         
28979     },
28980     
28981     onTouchEnd : function(e)
28982     {
28983         this.pinching = false;
28984         this.dragable = false;
28985         
28986     },
28987     
28988     process : function(file, crop)
28989     {
28990         if(this.loadMask){
28991             this.maskEl.mask(this.loadingText);
28992         }
28993         
28994         this.xhr = new XMLHttpRequest();
28995         
28996         file.xhr = this.xhr;
28997
28998         this.xhr.open(this.method, this.url, true);
28999         
29000         var headers = {
29001             "Accept": "application/json",
29002             "Cache-Control": "no-cache",
29003             "X-Requested-With": "XMLHttpRequest"
29004         };
29005         
29006         for (var headerName in headers) {
29007             var headerValue = headers[headerName];
29008             if (headerValue) {
29009                 this.xhr.setRequestHeader(headerName, headerValue);
29010             }
29011         }
29012         
29013         var _this = this;
29014         
29015         this.xhr.onload = function()
29016         {
29017             _this.xhrOnLoad(_this.xhr);
29018         }
29019         
29020         this.xhr.onerror = function()
29021         {
29022             _this.xhrOnError(_this.xhr);
29023         }
29024         
29025         var formData = new FormData();
29026
29027         formData.append('returnHTML', 'NO');
29028         
29029         if(crop){
29030             formData.append('crop', crop);
29031         }
29032         
29033         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29034             formData.append(this.paramName, file, file.name);
29035         }
29036         
29037         if(typeof(file.filename) != 'undefined'){
29038             formData.append('filename', file.filename);
29039         }
29040         
29041         if(typeof(file.mimetype) != 'undefined'){
29042             formData.append('mimetype', file.mimetype);
29043         }
29044         
29045         if(this.fireEvent('arrange', this, formData) != false){
29046             this.xhr.send(formData);
29047         };
29048     },
29049     
29050     xhrOnLoad : function(xhr)
29051     {
29052         if(this.loadMask){
29053             this.maskEl.unmask();
29054         }
29055         
29056         if (xhr.readyState !== 4) {
29057             this.fireEvent('exception', this, xhr);
29058             return;
29059         }
29060
29061         var response = Roo.decode(xhr.responseText);
29062         
29063         if(!response.success){
29064             this.fireEvent('exception', this, xhr);
29065             return;
29066         }
29067         
29068         var response = Roo.decode(xhr.responseText);
29069         
29070         this.fireEvent('upload', this, response);
29071         
29072     },
29073     
29074     xhrOnError : function()
29075     {
29076         if(this.loadMask){
29077             this.maskEl.unmask();
29078         }
29079         
29080         Roo.log('xhr on error');
29081         
29082         var response = Roo.decode(xhr.responseText);
29083           
29084         Roo.log(response);
29085         
29086     },
29087     
29088     prepare : function(file)
29089     {   
29090         if(this.loadMask){
29091             this.maskEl.mask(this.loadingText);
29092         }
29093         
29094         this.file = false;
29095         this.exif = {};
29096         
29097         if(typeof(file) === 'string'){
29098             this.loadCanvas(file);
29099             return;
29100         }
29101         
29102         if(!file || !this.urlAPI){
29103             return;
29104         }
29105         
29106         this.file = file;
29107         this.cropType = file.type;
29108         
29109         var _this = this;
29110         
29111         if(this.fireEvent('prepare', this, this.file) != false){
29112             
29113             var reader = new FileReader();
29114             
29115             reader.onload = function (e) {
29116                 if (e.target.error) {
29117                     Roo.log(e.target.error);
29118                     return;
29119                 }
29120                 
29121                 var buffer = e.target.result,
29122                     dataView = new DataView(buffer),
29123                     offset = 2,
29124                     maxOffset = dataView.byteLength - 4,
29125                     markerBytes,
29126                     markerLength;
29127                 
29128                 if (dataView.getUint16(0) === 0xffd8) {
29129                     while (offset < maxOffset) {
29130                         markerBytes = dataView.getUint16(offset);
29131                         
29132                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29133                             markerLength = dataView.getUint16(offset + 2) + 2;
29134                             if (offset + markerLength > dataView.byteLength) {
29135                                 Roo.log('Invalid meta data: Invalid segment size.');
29136                                 break;
29137                             }
29138                             
29139                             if(markerBytes == 0xffe1){
29140                                 _this.parseExifData(
29141                                     dataView,
29142                                     offset,
29143                                     markerLength
29144                                 );
29145                             }
29146                             
29147                             offset += markerLength;
29148                             
29149                             continue;
29150                         }
29151                         
29152                         break;
29153                     }
29154                     
29155                 }
29156                 
29157                 var url = _this.urlAPI.createObjectURL(_this.file);
29158                 
29159                 _this.loadCanvas(url);
29160                 
29161                 return;
29162             }
29163             
29164             reader.readAsArrayBuffer(this.file);
29165             
29166         }
29167         
29168     },
29169     
29170     parseExifData : function(dataView, offset, length)
29171     {
29172         var tiffOffset = offset + 10,
29173             littleEndian,
29174             dirOffset;
29175     
29176         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29177             // No Exif data, might be XMP data instead
29178             return;
29179         }
29180         
29181         // Check for the ASCII code for "Exif" (0x45786966):
29182         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29183             // No Exif data, might be XMP data instead
29184             return;
29185         }
29186         if (tiffOffset + 8 > dataView.byteLength) {
29187             Roo.log('Invalid Exif data: Invalid segment size.');
29188             return;
29189         }
29190         // Check for the two null bytes:
29191         if (dataView.getUint16(offset + 8) !== 0x0000) {
29192             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29193             return;
29194         }
29195         // Check the byte alignment:
29196         switch (dataView.getUint16(tiffOffset)) {
29197         case 0x4949:
29198             littleEndian = true;
29199             break;
29200         case 0x4D4D:
29201             littleEndian = false;
29202             break;
29203         default:
29204             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29205             return;
29206         }
29207         // Check for the TIFF tag marker (0x002A):
29208         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29209             Roo.log('Invalid Exif data: Missing TIFF marker.');
29210             return;
29211         }
29212         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29213         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29214         
29215         this.parseExifTags(
29216             dataView,
29217             tiffOffset,
29218             tiffOffset + dirOffset,
29219             littleEndian
29220         );
29221     },
29222     
29223     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29224     {
29225         var tagsNumber,
29226             dirEndOffset,
29227             i;
29228         if (dirOffset + 6 > dataView.byteLength) {
29229             Roo.log('Invalid Exif data: Invalid directory offset.');
29230             return;
29231         }
29232         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29233         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29234         if (dirEndOffset + 4 > dataView.byteLength) {
29235             Roo.log('Invalid Exif data: Invalid directory size.');
29236             return;
29237         }
29238         for (i = 0; i < tagsNumber; i += 1) {
29239             this.parseExifTag(
29240                 dataView,
29241                 tiffOffset,
29242                 dirOffset + 2 + 12 * i, // tag offset
29243                 littleEndian
29244             );
29245         }
29246         // Return the offset to the next directory:
29247         return dataView.getUint32(dirEndOffset, littleEndian);
29248     },
29249     
29250     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29251     {
29252         var tag = dataView.getUint16(offset, littleEndian);
29253         
29254         this.exif[tag] = this.getExifValue(
29255             dataView,
29256             tiffOffset,
29257             offset,
29258             dataView.getUint16(offset + 2, littleEndian), // tag type
29259             dataView.getUint32(offset + 4, littleEndian), // tag length
29260             littleEndian
29261         );
29262     },
29263     
29264     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29265     {
29266         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29267             tagSize,
29268             dataOffset,
29269             values,
29270             i,
29271             str,
29272             c;
29273     
29274         if (!tagType) {
29275             Roo.log('Invalid Exif data: Invalid tag type.');
29276             return;
29277         }
29278         
29279         tagSize = tagType.size * length;
29280         // Determine if the value is contained in the dataOffset bytes,
29281         // or if the value at the dataOffset is a pointer to the actual data:
29282         dataOffset = tagSize > 4 ?
29283                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29284         if (dataOffset + tagSize > dataView.byteLength) {
29285             Roo.log('Invalid Exif data: Invalid data offset.');
29286             return;
29287         }
29288         if (length === 1) {
29289             return tagType.getValue(dataView, dataOffset, littleEndian);
29290         }
29291         values = [];
29292         for (i = 0; i < length; i += 1) {
29293             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29294         }
29295         
29296         if (tagType.ascii) {
29297             str = '';
29298             // Concatenate the chars:
29299             for (i = 0; i < values.length; i += 1) {
29300                 c = values[i];
29301                 // Ignore the terminating NULL byte(s):
29302                 if (c === '\u0000') {
29303                     break;
29304                 }
29305                 str += c;
29306             }
29307             return str;
29308         }
29309         return values;
29310     }
29311     
29312 });
29313
29314 Roo.apply(Roo.bootstrap.UploadCropbox, {
29315     tags : {
29316         'Orientation': 0x0112
29317     },
29318     
29319     Orientation: {
29320             1: 0, //'top-left',
29321 //            2: 'top-right',
29322             3: 180, //'bottom-right',
29323 //            4: 'bottom-left',
29324 //            5: 'left-top',
29325             6: 90, //'right-top',
29326 //            7: 'right-bottom',
29327             8: 270 //'left-bottom'
29328     },
29329     
29330     exifTagTypes : {
29331         // byte, 8-bit unsigned int:
29332         1: {
29333             getValue: function (dataView, dataOffset) {
29334                 return dataView.getUint8(dataOffset);
29335             },
29336             size: 1
29337         },
29338         // ascii, 8-bit byte:
29339         2: {
29340             getValue: function (dataView, dataOffset) {
29341                 return String.fromCharCode(dataView.getUint8(dataOffset));
29342             },
29343             size: 1,
29344             ascii: true
29345         },
29346         // short, 16 bit int:
29347         3: {
29348             getValue: function (dataView, dataOffset, littleEndian) {
29349                 return dataView.getUint16(dataOffset, littleEndian);
29350             },
29351             size: 2
29352         },
29353         // long, 32 bit int:
29354         4: {
29355             getValue: function (dataView, dataOffset, littleEndian) {
29356                 return dataView.getUint32(dataOffset, littleEndian);
29357             },
29358             size: 4
29359         },
29360         // rational = two long values, first is numerator, second is denominator:
29361         5: {
29362             getValue: function (dataView, dataOffset, littleEndian) {
29363                 return dataView.getUint32(dataOffset, littleEndian) /
29364                     dataView.getUint32(dataOffset + 4, littleEndian);
29365             },
29366             size: 8
29367         },
29368         // slong, 32 bit signed int:
29369         9: {
29370             getValue: function (dataView, dataOffset, littleEndian) {
29371                 return dataView.getInt32(dataOffset, littleEndian);
29372             },
29373             size: 4
29374         },
29375         // srational, two slongs, first is numerator, second is denominator:
29376         10: {
29377             getValue: function (dataView, dataOffset, littleEndian) {
29378                 return dataView.getInt32(dataOffset, littleEndian) /
29379                     dataView.getInt32(dataOffset + 4, littleEndian);
29380             },
29381             size: 8
29382         }
29383     },
29384     
29385     footer : {
29386         STANDARD : [
29387             {
29388                 tag : 'div',
29389                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29390                 action : 'rotate-left',
29391                 cn : [
29392                     {
29393                         tag : 'button',
29394                         cls : 'btn btn-default',
29395                         html : '<i class="fa fa-undo"></i>'
29396                     }
29397                 ]
29398             },
29399             {
29400                 tag : 'div',
29401                 cls : 'btn-group roo-upload-cropbox-picture',
29402                 action : 'picture',
29403                 cn : [
29404                     {
29405                         tag : 'button',
29406                         cls : 'btn btn-default',
29407                         html : '<i class="fa fa-picture-o"></i>'
29408                     }
29409                 ]
29410             },
29411             {
29412                 tag : 'div',
29413                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29414                 action : 'rotate-right',
29415                 cn : [
29416                     {
29417                         tag : 'button',
29418                         cls : 'btn btn-default',
29419                         html : '<i class="fa fa-repeat"></i>'
29420                     }
29421                 ]
29422             }
29423         ],
29424         DOCUMENT : [
29425             {
29426                 tag : 'div',
29427                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29428                 action : 'rotate-left',
29429                 cn : [
29430                     {
29431                         tag : 'button',
29432                         cls : 'btn btn-default',
29433                         html : '<i class="fa fa-undo"></i>'
29434                     }
29435                 ]
29436             },
29437             {
29438                 tag : 'div',
29439                 cls : 'btn-group roo-upload-cropbox-download',
29440                 action : 'download',
29441                 cn : [
29442                     {
29443                         tag : 'button',
29444                         cls : 'btn btn-default',
29445                         html : '<i class="fa fa-download"></i>'
29446                     }
29447                 ]
29448             },
29449             {
29450                 tag : 'div',
29451                 cls : 'btn-group roo-upload-cropbox-crop',
29452                 action : 'crop',
29453                 cn : [
29454                     {
29455                         tag : 'button',
29456                         cls : 'btn btn-default',
29457                         html : '<i class="fa fa-crop"></i>'
29458                     }
29459                 ]
29460             },
29461             {
29462                 tag : 'div',
29463                 cls : 'btn-group roo-upload-cropbox-trash',
29464                 action : 'trash',
29465                 cn : [
29466                     {
29467                         tag : 'button',
29468                         cls : 'btn btn-default',
29469                         html : '<i class="fa fa-trash"></i>'
29470                     }
29471                 ]
29472             },
29473             {
29474                 tag : 'div',
29475                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29476                 action : 'rotate-right',
29477                 cn : [
29478                     {
29479                         tag : 'button',
29480                         cls : 'btn btn-default',
29481                         html : '<i class="fa fa-repeat"></i>'
29482                     }
29483                 ]
29484             }
29485         ],
29486         ROTATOR : [
29487             {
29488                 tag : 'div',
29489                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29490                 action : 'rotate-left',
29491                 cn : [
29492                     {
29493                         tag : 'button',
29494                         cls : 'btn btn-default',
29495                         html : '<i class="fa fa-undo"></i>'
29496                     }
29497                 ]
29498             },
29499             {
29500                 tag : 'div',
29501                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29502                 action : 'rotate-right',
29503                 cn : [
29504                     {
29505                         tag : 'button',
29506                         cls : 'btn btn-default',
29507                         html : '<i class="fa fa-repeat"></i>'
29508                     }
29509                 ]
29510             }
29511         ]
29512     }
29513 });
29514
29515 /*
29516 * Licence: LGPL
29517 */
29518
29519 /**
29520  * @class Roo.bootstrap.DocumentManager
29521  * @extends Roo.bootstrap.Component
29522  * Bootstrap DocumentManager class
29523  * @cfg {String} paramName default 'imageUpload'
29524  * @cfg {String} toolTipName default 'filename'
29525  * @cfg {String} method default POST
29526  * @cfg {String} url action url
29527  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29528  * @cfg {Boolean} multiple multiple upload default true
29529  * @cfg {Number} thumbSize default 300
29530  * @cfg {String} fieldLabel
29531  * @cfg {Number} labelWidth default 4
29532  * @cfg {String} labelAlign (left|top) default left
29533  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29534 * @cfg {Number} labellg set the width of label (1-12)
29535  * @cfg {Number} labelmd set the width of label (1-12)
29536  * @cfg {Number} labelsm set the width of label (1-12)
29537  * @cfg {Number} labelxs set the width of label (1-12)
29538  * 
29539  * @constructor
29540  * Create a new DocumentManager
29541  * @param {Object} config The config object
29542  */
29543
29544 Roo.bootstrap.DocumentManager = function(config){
29545     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29546     
29547     this.files = [];
29548     this.delegates = [];
29549     
29550     this.addEvents({
29551         /**
29552          * @event initial
29553          * Fire when initial the DocumentManager
29554          * @param {Roo.bootstrap.DocumentManager} this
29555          */
29556         "initial" : true,
29557         /**
29558          * @event inspect
29559          * inspect selected file
29560          * @param {Roo.bootstrap.DocumentManager} this
29561          * @param {File} file
29562          */
29563         "inspect" : true,
29564         /**
29565          * @event exception
29566          * Fire when xhr load exception
29567          * @param {Roo.bootstrap.DocumentManager} this
29568          * @param {XMLHttpRequest} xhr
29569          */
29570         "exception" : true,
29571         /**
29572          * @event afterupload
29573          * Fire when xhr load exception
29574          * @param {Roo.bootstrap.DocumentManager} this
29575          * @param {XMLHttpRequest} xhr
29576          */
29577         "afterupload" : true,
29578         /**
29579          * @event prepare
29580          * prepare the form data
29581          * @param {Roo.bootstrap.DocumentManager} this
29582          * @param {Object} formData
29583          */
29584         "prepare" : true,
29585         /**
29586          * @event remove
29587          * Fire when remove the file
29588          * @param {Roo.bootstrap.DocumentManager} this
29589          * @param {Object} file
29590          */
29591         "remove" : true,
29592         /**
29593          * @event refresh
29594          * Fire after refresh the file
29595          * @param {Roo.bootstrap.DocumentManager} this
29596          */
29597         "refresh" : true,
29598         /**
29599          * @event click
29600          * Fire after click the image
29601          * @param {Roo.bootstrap.DocumentManager} this
29602          * @param {Object} file
29603          */
29604         "click" : true,
29605         /**
29606          * @event edit
29607          * Fire when upload a image and editable set to true
29608          * @param {Roo.bootstrap.DocumentManager} this
29609          * @param {Object} file
29610          */
29611         "edit" : true,
29612         /**
29613          * @event beforeselectfile
29614          * Fire before select file
29615          * @param {Roo.bootstrap.DocumentManager} this
29616          */
29617         "beforeselectfile" : true,
29618         /**
29619          * @event process
29620          * Fire before process file
29621          * @param {Roo.bootstrap.DocumentManager} this
29622          * @param {Object} file
29623          */
29624         "process" : true,
29625         /**
29626          * @event previewrendered
29627          * Fire when preview rendered
29628          * @param {Roo.bootstrap.DocumentManager} this
29629          * @param {Object} file
29630          */
29631         "previewrendered" : true,
29632         /**
29633          */
29634         "previewResize" : true
29635         
29636     });
29637 };
29638
29639 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29640     
29641     boxes : 0,
29642     inputName : '',
29643     thumbSize : 300,
29644     multiple : true,
29645     files : false,
29646     method : 'POST',
29647     url : '',
29648     paramName : 'imageUpload',
29649     toolTipName : 'filename',
29650     fieldLabel : '',
29651     labelWidth : 4,
29652     labelAlign : 'left',
29653     editable : true,
29654     delegates : false,
29655     xhr : false, 
29656     
29657     labellg : 0,
29658     labelmd : 0,
29659     labelsm : 0,
29660     labelxs : 0,
29661     
29662     getAutoCreate : function()
29663     {   
29664         var managerWidget = {
29665             tag : 'div',
29666             cls : 'roo-document-manager',
29667             cn : [
29668                 {
29669                     tag : 'input',
29670                     cls : 'roo-document-manager-selector',
29671                     type : 'file'
29672                 },
29673                 {
29674                     tag : 'div',
29675                     cls : 'roo-document-manager-uploader',
29676                     cn : [
29677                         {
29678                             tag : 'div',
29679                             cls : 'roo-document-manager-upload-btn',
29680                             html : '<i class="fa fa-plus"></i>'
29681                         }
29682                     ]
29683                     
29684                 }
29685             ]
29686         };
29687         
29688         var content = [
29689             {
29690                 tag : 'div',
29691                 cls : 'column col-md-12',
29692                 cn : managerWidget
29693             }
29694         ];
29695         
29696         if(this.fieldLabel.length){
29697             
29698             content = [
29699                 {
29700                     tag : 'div',
29701                     cls : 'column col-md-12',
29702                     html : this.fieldLabel
29703                 },
29704                 {
29705                     tag : 'div',
29706                     cls : 'column col-md-12',
29707                     cn : managerWidget
29708                 }
29709             ];
29710
29711             if(this.labelAlign == 'left'){
29712                 content = [
29713                     {
29714                         tag : 'div',
29715                         cls : 'column',
29716                         html : this.fieldLabel
29717                     },
29718                     {
29719                         tag : 'div',
29720                         cls : 'column',
29721                         cn : managerWidget
29722                     }
29723                 ];
29724                 
29725                 if(this.labelWidth > 12){
29726                     content[0].style = "width: " + this.labelWidth + 'px';
29727                 }
29728
29729                 if(this.labelWidth < 13 && this.labelmd == 0){
29730                     this.labelmd = this.labelWidth;
29731                 }
29732
29733                 if(this.labellg > 0){
29734                     content[0].cls += ' col-lg-' + this.labellg;
29735                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29736                 }
29737
29738                 if(this.labelmd > 0){
29739                     content[0].cls += ' col-md-' + this.labelmd;
29740                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29741                 }
29742
29743                 if(this.labelsm > 0){
29744                     content[0].cls += ' col-sm-' + this.labelsm;
29745                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29746                 }
29747
29748                 if(this.labelxs > 0){
29749                     content[0].cls += ' col-xs-' + this.labelxs;
29750                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29751                 }
29752                 
29753             }
29754         }
29755         
29756         var cfg = {
29757             tag : 'div',
29758             cls : 'row clearfix',
29759             cn : content
29760         };
29761         
29762         return cfg;
29763         
29764     },
29765     
29766     initEvents : function()
29767     {
29768         this.managerEl = this.el.select('.roo-document-manager', true).first();
29769         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29770         
29771         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29772         this.selectorEl.hide();
29773         
29774         if(this.multiple){
29775             this.selectorEl.attr('multiple', 'multiple');
29776         }
29777         
29778         this.selectorEl.on('change', this.onFileSelected, this);
29779         
29780         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29781         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29782         
29783         this.uploader.on('click', this.onUploaderClick, this);
29784         
29785         this.renderProgressDialog();
29786         
29787         var _this = this;
29788         
29789         window.addEventListener("resize", function() { _this.refresh(); } );
29790         
29791         this.fireEvent('initial', this);
29792     },
29793     
29794     renderProgressDialog : function()
29795     {
29796         var _this = this;
29797         
29798         this.progressDialog = new Roo.bootstrap.Modal({
29799             cls : 'roo-document-manager-progress-dialog',
29800             allow_close : false,
29801             animate : false,
29802             title : '',
29803             buttons : [
29804                 {
29805                     name  :'cancel',
29806                     weight : 'danger',
29807                     html : 'Cancel'
29808                 }
29809             ], 
29810             listeners : { 
29811                 btnclick : function() {
29812                     _this.uploadCancel();
29813                     this.hide();
29814                 }
29815             }
29816         });
29817          
29818         this.progressDialog.render(Roo.get(document.body));
29819          
29820         this.progress = new Roo.bootstrap.Progress({
29821             cls : 'roo-document-manager-progress',
29822             active : true,
29823             striped : true
29824         });
29825         
29826         this.progress.render(this.progressDialog.getChildContainer());
29827         
29828         this.progressBar = new Roo.bootstrap.ProgressBar({
29829             cls : 'roo-document-manager-progress-bar',
29830             aria_valuenow : 0,
29831             aria_valuemin : 0,
29832             aria_valuemax : 12,
29833             panel : 'success'
29834         });
29835         
29836         this.progressBar.render(this.progress.getChildContainer());
29837     },
29838     
29839     onUploaderClick : function(e)
29840     {
29841         e.preventDefault();
29842      
29843         if(this.fireEvent('beforeselectfile', this) != false){
29844             this.selectorEl.dom.click();
29845         }
29846         
29847     },
29848     
29849     onFileSelected : function(e)
29850     {
29851         e.preventDefault();
29852         
29853         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29854             return;
29855         }
29856         
29857         Roo.each(this.selectorEl.dom.files, function(file){
29858             if(this.fireEvent('inspect', this, file) != false){
29859                 this.files.push(file);
29860             }
29861         }, this);
29862         
29863         this.queue();
29864         
29865     },
29866     
29867     queue : function()
29868     {
29869         this.selectorEl.dom.value = '';
29870         
29871         if(!this.files || !this.files.length){
29872             return;
29873         }
29874         
29875         if(this.boxes > 0 && this.files.length > this.boxes){
29876             this.files = this.files.slice(0, this.boxes);
29877         }
29878         
29879         this.uploader.show();
29880         
29881         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29882             this.uploader.hide();
29883         }
29884         
29885         var _this = this;
29886         
29887         var files = [];
29888         
29889         var docs = [];
29890         
29891         Roo.each(this.files, function(file){
29892             
29893             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29894                 var f = this.renderPreview(file);
29895                 files.push(f);
29896                 return;
29897             }
29898             
29899             if(file.type.indexOf('image') != -1){
29900                 this.delegates.push(
29901                     (function(){
29902                         _this.process(file);
29903                     }).createDelegate(this)
29904                 );
29905         
29906                 return;
29907             }
29908             
29909             docs.push(
29910                 (function(){
29911                     _this.process(file);
29912                 }).createDelegate(this)
29913             );
29914             
29915         }, this);
29916         
29917         this.files = files;
29918         
29919         this.delegates = this.delegates.concat(docs);
29920         
29921         if(!this.delegates.length){
29922             this.refresh();
29923             return;
29924         }
29925         
29926         this.progressBar.aria_valuemax = this.delegates.length;
29927         
29928         this.arrange();
29929         
29930         return;
29931     },
29932     
29933     arrange : function()
29934     {
29935         if(!this.delegates.length){
29936             this.progressDialog.hide();
29937             this.refresh();
29938             return;
29939         }
29940         
29941         var delegate = this.delegates.shift();
29942         
29943         this.progressDialog.show();
29944         
29945         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29946         
29947         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29948         
29949         delegate();
29950     },
29951     
29952     refresh : function()
29953     {
29954         this.uploader.show();
29955         
29956         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29957             this.uploader.hide();
29958         }
29959         
29960         Roo.isTouch ? this.closable(false) : this.closable(true);
29961         
29962         this.fireEvent('refresh', this);
29963     },
29964     
29965     onRemove : function(e, el, o)
29966     {
29967         e.preventDefault();
29968         
29969         this.fireEvent('remove', this, o);
29970         
29971     },
29972     
29973     remove : function(o)
29974     {
29975         var files = [];
29976         
29977         Roo.each(this.files, function(file){
29978             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29979                 files.push(file);
29980                 return;
29981             }
29982
29983             o.target.remove();
29984
29985         }, this);
29986         
29987         this.files = files;
29988         
29989         this.refresh();
29990     },
29991     
29992     clear : function()
29993     {
29994         Roo.each(this.files, function(file){
29995             if(!file.target){
29996                 return;
29997             }
29998             
29999             file.target.remove();
30000
30001         }, this);
30002         
30003         this.files = [];
30004         
30005         this.refresh();
30006     },
30007     
30008     onClick : function(e, el, o)
30009     {
30010         e.preventDefault();
30011         
30012         this.fireEvent('click', this, o);
30013         
30014     },
30015     
30016     closable : function(closable)
30017     {
30018         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30019             
30020             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30021             
30022             if(closable){
30023                 el.show();
30024                 return;
30025             }
30026             
30027             el.hide();
30028             
30029         }, this);
30030     },
30031     
30032     xhrOnLoad : function(xhr)
30033     {
30034         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30035             el.remove();
30036         }, this);
30037         
30038         if (xhr.readyState !== 4) {
30039             this.arrange();
30040             this.fireEvent('exception', this, xhr);
30041             return;
30042         }
30043
30044         var response = Roo.decode(xhr.responseText);
30045         
30046         if(!response.success){
30047             this.arrange();
30048             this.fireEvent('exception', this, xhr);
30049             return;
30050         }
30051         
30052         var file = this.renderPreview(response.data);
30053         
30054         this.files.push(file);
30055         
30056         this.arrange();
30057         
30058         this.fireEvent('afterupload', this, xhr);
30059         
30060     },
30061     
30062     xhrOnError : function(xhr)
30063     {
30064         Roo.log('xhr on error');
30065         
30066         var response = Roo.decode(xhr.responseText);
30067           
30068         Roo.log(response);
30069         
30070         this.arrange();
30071     },
30072     
30073     process : function(file)
30074     {
30075         if(this.fireEvent('process', this, file) !== false){
30076             if(this.editable && file.type.indexOf('image') != -1){
30077                 this.fireEvent('edit', this, file);
30078                 return;
30079             }
30080
30081             this.uploadStart(file, false);
30082
30083             return;
30084         }
30085         
30086     },
30087     
30088     uploadStart : function(file, crop)
30089     {
30090         this.xhr = new XMLHttpRequest();
30091         
30092         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30093             this.arrange();
30094             return;
30095         }
30096         
30097         file.xhr = this.xhr;
30098             
30099         this.managerEl.createChild({
30100             tag : 'div',
30101             cls : 'roo-document-manager-loading',
30102             cn : [
30103                 {
30104                     tag : 'div',
30105                     tooltip : file.name,
30106                     cls : 'roo-document-manager-thumb',
30107                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30108                 }
30109             ]
30110
30111         });
30112
30113         this.xhr.open(this.method, this.url, true);
30114         
30115         var headers = {
30116             "Accept": "application/json",
30117             "Cache-Control": "no-cache",
30118             "X-Requested-With": "XMLHttpRequest"
30119         };
30120         
30121         for (var headerName in headers) {
30122             var headerValue = headers[headerName];
30123             if (headerValue) {
30124                 this.xhr.setRequestHeader(headerName, headerValue);
30125             }
30126         }
30127         
30128         var _this = this;
30129         
30130         this.xhr.onload = function()
30131         {
30132             _this.xhrOnLoad(_this.xhr);
30133         }
30134         
30135         this.xhr.onerror = function()
30136         {
30137             _this.xhrOnError(_this.xhr);
30138         }
30139         
30140         var formData = new FormData();
30141
30142         formData.append('returnHTML', 'NO');
30143         
30144         if(crop){
30145             formData.append('crop', crop);
30146         }
30147         
30148         formData.append(this.paramName, file, file.name);
30149         
30150         var options = {
30151             file : file, 
30152             manually : false
30153         };
30154         
30155         if(this.fireEvent('prepare', this, formData, options) != false){
30156             
30157             if(options.manually){
30158                 return;
30159             }
30160             
30161             this.xhr.send(formData);
30162             return;
30163         };
30164         
30165         this.uploadCancel();
30166     },
30167     
30168     uploadCancel : function()
30169     {
30170         if (this.xhr) {
30171             this.xhr.abort();
30172         }
30173         
30174         this.delegates = [];
30175         
30176         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30177             el.remove();
30178         }, this);
30179         
30180         this.arrange();
30181     },
30182     
30183     renderPreview : function(file)
30184     {
30185         if(typeof(file.target) != 'undefined' && file.target){
30186             return file;
30187         }
30188         
30189         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30190         
30191         var previewEl = this.managerEl.createChild({
30192             tag : 'div',
30193             cls : 'roo-document-manager-preview',
30194             cn : [
30195                 {
30196                     tag : 'div',
30197                     tooltip : file[this.toolTipName],
30198                     cls : 'roo-document-manager-thumb',
30199                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30200                 },
30201                 {
30202                     tag : 'button',
30203                     cls : 'close',
30204                     html : '<i class="fa fa-times-circle"></i>'
30205                 }
30206             ]
30207         });
30208
30209         var close = previewEl.select('button.close', true).first();
30210
30211         close.on('click', this.onRemove, this, file);
30212
30213         file.target = previewEl;
30214
30215         var image = previewEl.select('img', true).first();
30216         
30217         var _this = this;
30218         
30219         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30220         
30221         image.on('click', this.onClick, this, file);
30222         
30223         this.fireEvent('previewrendered', this, file);
30224         
30225         return file;
30226         
30227     },
30228     
30229     onPreviewLoad : function(file, image)
30230     {
30231         if(typeof(file.target) == 'undefined' || !file.target){
30232             return;
30233         }
30234         
30235         var width = image.dom.naturalWidth || image.dom.width;
30236         var height = image.dom.naturalHeight || image.dom.height;
30237         
30238         if(!this.previewResize) {
30239             return;
30240         }
30241         
30242         if(width > height){
30243             file.target.addClass('wide');
30244             return;
30245         }
30246         
30247         file.target.addClass('tall');
30248         return;
30249         
30250     },
30251     
30252     uploadFromSource : function(file, crop)
30253     {
30254         this.xhr = new XMLHttpRequest();
30255         
30256         this.managerEl.createChild({
30257             tag : 'div',
30258             cls : 'roo-document-manager-loading',
30259             cn : [
30260                 {
30261                     tag : 'div',
30262                     tooltip : file.name,
30263                     cls : 'roo-document-manager-thumb',
30264                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30265                 }
30266             ]
30267
30268         });
30269
30270         this.xhr.open(this.method, this.url, true);
30271         
30272         var headers = {
30273             "Accept": "application/json",
30274             "Cache-Control": "no-cache",
30275             "X-Requested-With": "XMLHttpRequest"
30276         };
30277         
30278         for (var headerName in headers) {
30279             var headerValue = headers[headerName];
30280             if (headerValue) {
30281                 this.xhr.setRequestHeader(headerName, headerValue);
30282             }
30283         }
30284         
30285         var _this = this;
30286         
30287         this.xhr.onload = function()
30288         {
30289             _this.xhrOnLoad(_this.xhr);
30290         }
30291         
30292         this.xhr.onerror = function()
30293         {
30294             _this.xhrOnError(_this.xhr);
30295         }
30296         
30297         var formData = new FormData();
30298
30299         formData.append('returnHTML', 'NO');
30300         
30301         formData.append('crop', crop);
30302         
30303         if(typeof(file.filename) != 'undefined'){
30304             formData.append('filename', file.filename);
30305         }
30306         
30307         if(typeof(file.mimetype) != 'undefined'){
30308             formData.append('mimetype', file.mimetype);
30309         }
30310         
30311         Roo.log(formData);
30312         
30313         if(this.fireEvent('prepare', this, formData) != false){
30314             this.xhr.send(formData);
30315         };
30316     }
30317 });
30318
30319 /*
30320 * Licence: LGPL
30321 */
30322
30323 /**
30324  * @class Roo.bootstrap.DocumentViewer
30325  * @extends Roo.bootstrap.Component
30326  * Bootstrap DocumentViewer class
30327  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30328  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30329  * 
30330  * @constructor
30331  * Create a new DocumentViewer
30332  * @param {Object} config The config object
30333  */
30334
30335 Roo.bootstrap.DocumentViewer = function(config){
30336     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30337     
30338     this.addEvents({
30339         /**
30340          * @event initial
30341          * Fire after initEvent
30342          * @param {Roo.bootstrap.DocumentViewer} this
30343          */
30344         "initial" : true,
30345         /**
30346          * @event click
30347          * Fire after click
30348          * @param {Roo.bootstrap.DocumentViewer} this
30349          */
30350         "click" : true,
30351         /**
30352          * @event download
30353          * Fire after download button
30354          * @param {Roo.bootstrap.DocumentViewer} this
30355          */
30356         "download" : true,
30357         /**
30358          * @event trash
30359          * Fire after trash button
30360          * @param {Roo.bootstrap.DocumentViewer} this
30361          */
30362         "trash" : true
30363         
30364     });
30365 };
30366
30367 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30368     
30369     showDownload : true,
30370     
30371     showTrash : true,
30372     
30373     getAutoCreate : function()
30374     {
30375         var cfg = {
30376             tag : 'div',
30377             cls : 'roo-document-viewer',
30378             cn : [
30379                 {
30380                     tag : 'div',
30381                     cls : 'roo-document-viewer-body',
30382                     cn : [
30383                         {
30384                             tag : 'div',
30385                             cls : 'roo-document-viewer-thumb',
30386                             cn : [
30387                                 {
30388                                     tag : 'img',
30389                                     cls : 'roo-document-viewer-image'
30390                                 }
30391                             ]
30392                         }
30393                     ]
30394                 },
30395                 {
30396                     tag : 'div',
30397                     cls : 'roo-document-viewer-footer',
30398                     cn : {
30399                         tag : 'div',
30400                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30401                         cn : [
30402                             {
30403                                 tag : 'div',
30404                                 cls : 'btn-group roo-document-viewer-download',
30405                                 cn : [
30406                                     {
30407                                         tag : 'button',
30408                                         cls : 'btn btn-default',
30409                                         html : '<i class="fa fa-download"></i>'
30410                                     }
30411                                 ]
30412                             },
30413                             {
30414                                 tag : 'div',
30415                                 cls : 'btn-group roo-document-viewer-trash',
30416                                 cn : [
30417                                     {
30418                                         tag : 'button',
30419                                         cls : 'btn btn-default',
30420                                         html : '<i class="fa fa-trash"></i>'
30421                                     }
30422                                 ]
30423                             }
30424                         ]
30425                     }
30426                 }
30427             ]
30428         };
30429         
30430         return cfg;
30431     },
30432     
30433     initEvents : function()
30434     {
30435         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30436         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30437         
30438         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30439         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30440         
30441         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30442         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30443         
30444         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30445         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30446         
30447         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30448         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30449         
30450         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30451         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30452         
30453         this.bodyEl.on('click', this.onClick, this);
30454         this.downloadBtn.on('click', this.onDownload, this);
30455         this.trashBtn.on('click', this.onTrash, this);
30456         
30457         this.downloadBtn.hide();
30458         this.trashBtn.hide();
30459         
30460         if(this.showDownload){
30461             this.downloadBtn.show();
30462         }
30463         
30464         if(this.showTrash){
30465             this.trashBtn.show();
30466         }
30467         
30468         if(!this.showDownload && !this.showTrash) {
30469             this.footerEl.hide();
30470         }
30471         
30472     },
30473     
30474     initial : function()
30475     {
30476         this.fireEvent('initial', this);
30477         
30478     },
30479     
30480     onClick : function(e)
30481     {
30482         e.preventDefault();
30483         
30484         this.fireEvent('click', this);
30485     },
30486     
30487     onDownload : function(e)
30488     {
30489         e.preventDefault();
30490         
30491         this.fireEvent('download', this);
30492     },
30493     
30494     onTrash : function(e)
30495     {
30496         e.preventDefault();
30497         
30498         this.fireEvent('trash', this);
30499     }
30500     
30501 });
30502 /*
30503  * - LGPL
30504  *
30505  * nav progress bar
30506  * 
30507  */
30508
30509 /**
30510  * @class Roo.bootstrap.NavProgressBar
30511  * @extends Roo.bootstrap.Component
30512  * Bootstrap NavProgressBar class
30513  * 
30514  * @constructor
30515  * Create a new nav progress bar
30516  * @param {Object} config The config object
30517  */
30518
30519 Roo.bootstrap.NavProgressBar = function(config){
30520     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30521
30522     this.bullets = this.bullets || [];
30523    
30524 //    Roo.bootstrap.NavProgressBar.register(this);
30525      this.addEvents({
30526         /**
30527              * @event changed
30528              * Fires when the active item changes
30529              * @param {Roo.bootstrap.NavProgressBar} this
30530              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30531              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30532          */
30533         'changed': true
30534      });
30535     
30536 };
30537
30538 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30539     
30540     bullets : [],
30541     barItems : [],
30542     
30543     getAutoCreate : function()
30544     {
30545         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30546         
30547         cfg = {
30548             tag : 'div',
30549             cls : 'roo-navigation-bar-group',
30550             cn : [
30551                 {
30552                     tag : 'div',
30553                     cls : 'roo-navigation-top-bar'
30554                 },
30555                 {
30556                     tag : 'div',
30557                     cls : 'roo-navigation-bullets-bar',
30558                     cn : [
30559                         {
30560                             tag : 'ul',
30561                             cls : 'roo-navigation-bar'
30562                         }
30563                     ]
30564                 },
30565                 
30566                 {
30567                     tag : 'div',
30568                     cls : 'roo-navigation-bottom-bar'
30569                 }
30570             ]
30571             
30572         };
30573         
30574         return cfg;
30575         
30576     },
30577     
30578     initEvents: function() 
30579     {
30580         
30581     },
30582     
30583     onRender : function(ct, position) 
30584     {
30585         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30586         
30587         if(this.bullets.length){
30588             Roo.each(this.bullets, function(b){
30589                this.addItem(b);
30590             }, this);
30591         }
30592         
30593         this.format();
30594         
30595     },
30596     
30597     addItem : function(cfg)
30598     {
30599         var item = new Roo.bootstrap.NavProgressItem(cfg);
30600         
30601         item.parentId = this.id;
30602         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30603         
30604         if(cfg.html){
30605             var top = new Roo.bootstrap.Element({
30606                 tag : 'div',
30607                 cls : 'roo-navigation-bar-text'
30608             });
30609             
30610             var bottom = new Roo.bootstrap.Element({
30611                 tag : 'div',
30612                 cls : 'roo-navigation-bar-text'
30613             });
30614             
30615             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30616             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30617             
30618             var topText = new Roo.bootstrap.Element({
30619                 tag : 'span',
30620                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30621             });
30622             
30623             var bottomText = new Roo.bootstrap.Element({
30624                 tag : 'span',
30625                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30626             });
30627             
30628             topText.onRender(top.el, null);
30629             bottomText.onRender(bottom.el, null);
30630             
30631             item.topEl = top;
30632             item.bottomEl = bottom;
30633         }
30634         
30635         this.barItems.push(item);
30636         
30637         return item;
30638     },
30639     
30640     getActive : function()
30641     {
30642         var active = false;
30643         
30644         Roo.each(this.barItems, function(v){
30645             
30646             if (!v.isActive()) {
30647                 return;
30648             }
30649             
30650             active = v;
30651             return false;
30652             
30653         });
30654         
30655         return active;
30656     },
30657     
30658     setActiveItem : function(item)
30659     {
30660         var prev = false;
30661         
30662         Roo.each(this.barItems, function(v){
30663             if (v.rid == item.rid) {
30664                 return ;
30665             }
30666             
30667             if (v.isActive()) {
30668                 v.setActive(false);
30669                 prev = v;
30670             }
30671         });
30672
30673         item.setActive(true);
30674         
30675         this.fireEvent('changed', this, item, prev);
30676     },
30677     
30678     getBarItem: function(rid)
30679     {
30680         var ret = false;
30681         
30682         Roo.each(this.barItems, function(e) {
30683             if (e.rid != rid) {
30684                 return;
30685             }
30686             
30687             ret =  e;
30688             return false;
30689         });
30690         
30691         return ret;
30692     },
30693     
30694     indexOfItem : function(item)
30695     {
30696         var index = false;
30697         
30698         Roo.each(this.barItems, function(v, i){
30699             
30700             if (v.rid != item.rid) {
30701                 return;
30702             }
30703             
30704             index = i;
30705             return false
30706         });
30707         
30708         return index;
30709     },
30710     
30711     setActiveNext : function()
30712     {
30713         var i = this.indexOfItem(this.getActive());
30714         
30715         if (i > this.barItems.length) {
30716             return;
30717         }
30718         
30719         this.setActiveItem(this.barItems[i+1]);
30720     },
30721     
30722     setActivePrev : function()
30723     {
30724         var i = this.indexOfItem(this.getActive());
30725         
30726         if (i  < 1) {
30727             return;
30728         }
30729         
30730         this.setActiveItem(this.barItems[i-1]);
30731     },
30732     
30733     format : function()
30734     {
30735         if(!this.barItems.length){
30736             return;
30737         }
30738      
30739         var width = 100 / this.barItems.length;
30740         
30741         Roo.each(this.barItems, function(i){
30742             i.el.setStyle('width', width + '%');
30743             i.topEl.el.setStyle('width', width + '%');
30744             i.bottomEl.el.setStyle('width', width + '%');
30745         }, this);
30746         
30747     }
30748     
30749 });
30750 /*
30751  * - LGPL
30752  *
30753  * Nav Progress Item
30754  * 
30755  */
30756
30757 /**
30758  * @class Roo.bootstrap.NavProgressItem
30759  * @extends Roo.bootstrap.Component
30760  * Bootstrap NavProgressItem class
30761  * @cfg {String} rid the reference id
30762  * @cfg {Boolean} active (true|false) Is item active default false
30763  * @cfg {Boolean} disabled (true|false) Is item active default false
30764  * @cfg {String} html
30765  * @cfg {String} position (top|bottom) text position default bottom
30766  * @cfg {String} icon show icon instead of number
30767  * 
30768  * @constructor
30769  * Create a new NavProgressItem
30770  * @param {Object} config The config object
30771  */
30772 Roo.bootstrap.NavProgressItem = function(config){
30773     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30774     this.addEvents({
30775         // raw events
30776         /**
30777          * @event click
30778          * The raw click event for the entire grid.
30779          * @param {Roo.bootstrap.NavProgressItem} this
30780          * @param {Roo.EventObject} e
30781          */
30782         "click" : true
30783     });
30784    
30785 };
30786
30787 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30788     
30789     rid : '',
30790     active : false,
30791     disabled : false,
30792     html : '',
30793     position : 'bottom',
30794     icon : false,
30795     
30796     getAutoCreate : function()
30797     {
30798         var iconCls = 'roo-navigation-bar-item-icon';
30799         
30800         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30801         
30802         var cfg = {
30803             tag: 'li',
30804             cls: 'roo-navigation-bar-item',
30805             cn : [
30806                 {
30807                     tag : 'i',
30808                     cls : iconCls
30809                 }
30810             ]
30811         };
30812         
30813         if(this.active){
30814             cfg.cls += ' active';
30815         }
30816         if(this.disabled){
30817             cfg.cls += ' disabled';
30818         }
30819         
30820         return cfg;
30821     },
30822     
30823     disable : function()
30824     {
30825         this.setDisabled(true);
30826     },
30827     
30828     enable : function()
30829     {
30830         this.setDisabled(false);
30831     },
30832     
30833     initEvents: function() 
30834     {
30835         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30836         
30837         this.iconEl.on('click', this.onClick, this);
30838     },
30839     
30840     onClick : function(e)
30841     {
30842         e.preventDefault();
30843         
30844         if(this.disabled){
30845             return;
30846         }
30847         
30848         if(this.fireEvent('click', this, e) === false){
30849             return;
30850         };
30851         
30852         this.parent().setActiveItem(this);
30853     },
30854     
30855     isActive: function () 
30856     {
30857         return this.active;
30858     },
30859     
30860     setActive : function(state)
30861     {
30862         if(this.active == state){
30863             return;
30864         }
30865         
30866         this.active = state;
30867         
30868         if (state) {
30869             this.el.addClass('active');
30870             return;
30871         }
30872         
30873         this.el.removeClass('active');
30874         
30875         return;
30876     },
30877     
30878     setDisabled : function(state)
30879     {
30880         if(this.disabled == state){
30881             return;
30882         }
30883         
30884         this.disabled = state;
30885         
30886         if (state) {
30887             this.el.addClass('disabled');
30888             return;
30889         }
30890         
30891         this.el.removeClass('disabled');
30892     },
30893     
30894     tooltipEl : function()
30895     {
30896         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30897     }
30898 });
30899  
30900
30901  /*
30902  * - LGPL
30903  *
30904  * FieldLabel
30905  * 
30906  */
30907
30908 /**
30909  * @class Roo.bootstrap.FieldLabel
30910  * @extends Roo.bootstrap.Component
30911  * Bootstrap FieldLabel class
30912  * @cfg {String} html contents of the element
30913  * @cfg {String} tag tag of the element default label
30914  * @cfg {String} cls class of the element
30915  * @cfg {String} target label target 
30916  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30917  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30918  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30919  * @cfg {String} iconTooltip default "This field is required"
30920  * @cfg {String} indicatorpos (left|right) default left
30921  * 
30922  * @constructor
30923  * Create a new FieldLabel
30924  * @param {Object} config The config object
30925  */
30926
30927 Roo.bootstrap.FieldLabel = function(config){
30928     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30929     
30930     this.addEvents({
30931             /**
30932              * @event invalid
30933              * Fires after the field has been marked as invalid.
30934              * @param {Roo.form.FieldLabel} this
30935              * @param {String} msg The validation message
30936              */
30937             invalid : true,
30938             /**
30939              * @event valid
30940              * Fires after the field has been validated with no errors.
30941              * @param {Roo.form.FieldLabel} this
30942              */
30943             valid : true
30944         });
30945 };
30946
30947 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30948     
30949     tag: 'label',
30950     cls: '',
30951     html: '',
30952     target: '',
30953     allowBlank : true,
30954     invalidClass : 'has-warning',
30955     validClass : 'has-success',
30956     iconTooltip : 'This field is required',
30957     indicatorpos : 'left',
30958     
30959     getAutoCreate : function(){
30960         
30961         var cls = "";
30962         if (!this.allowBlank) {
30963             cls  = "visible";
30964         }
30965         
30966         var cfg = {
30967             tag : this.tag,
30968             cls : 'roo-bootstrap-field-label ' + this.cls,
30969             for : this.target,
30970             cn : [
30971                 {
30972                     tag : 'i',
30973                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30974                     tooltip : this.iconTooltip
30975                 },
30976                 {
30977                     tag : 'span',
30978                     html : this.html
30979                 }
30980             ] 
30981         };
30982         
30983         if(this.indicatorpos == 'right'){
30984             var cfg = {
30985                 tag : this.tag,
30986                 cls : 'roo-bootstrap-field-label ' + this.cls,
30987                 for : this.target,
30988                 cn : [
30989                     {
30990                         tag : 'span',
30991                         html : this.html
30992                     },
30993                     {
30994                         tag : 'i',
30995                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30996                         tooltip : this.iconTooltip
30997                     }
30998                 ] 
30999             };
31000         }
31001         
31002         return cfg;
31003     },
31004     
31005     initEvents: function() 
31006     {
31007         Roo.bootstrap.Element.superclass.initEvents.call(this);
31008         
31009         this.indicator = this.indicatorEl();
31010         
31011         if(this.indicator){
31012             this.indicator.removeClass('visible');
31013             this.indicator.addClass('invisible');
31014         }
31015         
31016         Roo.bootstrap.FieldLabel.register(this);
31017     },
31018     
31019     indicatorEl : function()
31020     {
31021         var indicator = this.el.select('i.roo-required-indicator',true).first();
31022         
31023         if(!indicator){
31024             return false;
31025         }
31026         
31027         return indicator;
31028         
31029     },
31030     
31031     /**
31032      * Mark this field as valid
31033      */
31034     markValid : function()
31035     {
31036         if(this.indicator){
31037             this.indicator.removeClass('visible');
31038             this.indicator.addClass('invisible');
31039         }
31040         if (Roo.bootstrap.version == 3) {
31041             this.el.removeClass(this.invalidClass);
31042             this.el.addClass(this.validClass);
31043         } else {
31044             this.el.removeClass('is-invalid');
31045             this.el.addClass('is-valid');
31046         }
31047         
31048         
31049         this.fireEvent('valid', this);
31050     },
31051     
31052     /**
31053      * Mark this field as invalid
31054      * @param {String} msg The validation message
31055      */
31056     markInvalid : function(msg)
31057     {
31058         if(this.indicator){
31059             this.indicator.removeClass('invisible');
31060             this.indicator.addClass('visible');
31061         }
31062           if (Roo.bootstrap.version == 3) {
31063             this.el.removeClass(this.validClass);
31064             this.el.addClass(this.invalidClass);
31065         } else {
31066             this.el.removeClass('is-valid');
31067             this.el.addClass('is-invalid');
31068         }
31069         
31070         
31071         this.fireEvent('invalid', this, msg);
31072     }
31073     
31074    
31075 });
31076
31077 Roo.apply(Roo.bootstrap.FieldLabel, {
31078     
31079     groups: {},
31080     
31081      /**
31082     * register a FieldLabel Group
31083     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31084     */
31085     register : function(label)
31086     {
31087         if(this.groups.hasOwnProperty(label.target)){
31088             return;
31089         }
31090      
31091         this.groups[label.target] = label;
31092         
31093     },
31094     /**
31095     * fetch a FieldLabel Group based on the target
31096     * @param {string} target
31097     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31098     */
31099     get: function(target) {
31100         if (typeof(this.groups[target]) == 'undefined') {
31101             return false;
31102         }
31103         
31104         return this.groups[target] ;
31105     }
31106 });
31107
31108  
31109
31110  /*
31111  * - LGPL
31112  *
31113  * page DateSplitField.
31114  * 
31115  */
31116
31117
31118 /**
31119  * @class Roo.bootstrap.DateSplitField
31120  * @extends Roo.bootstrap.Component
31121  * Bootstrap DateSplitField class
31122  * @cfg {string} fieldLabel - the label associated
31123  * @cfg {Number} labelWidth set the width of label (0-12)
31124  * @cfg {String} labelAlign (top|left)
31125  * @cfg {Boolean} dayAllowBlank (true|false) default false
31126  * @cfg {Boolean} monthAllowBlank (true|false) default false
31127  * @cfg {Boolean} yearAllowBlank (true|false) default false
31128  * @cfg {string} dayPlaceholder 
31129  * @cfg {string} monthPlaceholder
31130  * @cfg {string} yearPlaceholder
31131  * @cfg {string} dayFormat default 'd'
31132  * @cfg {string} monthFormat default 'm'
31133  * @cfg {string} yearFormat default 'Y'
31134  * @cfg {Number} labellg set the width of label (1-12)
31135  * @cfg {Number} labelmd set the width of label (1-12)
31136  * @cfg {Number} labelsm set the width of label (1-12)
31137  * @cfg {Number} labelxs set the width of label (1-12)
31138
31139  *     
31140  * @constructor
31141  * Create a new DateSplitField
31142  * @param {Object} config The config object
31143  */
31144
31145 Roo.bootstrap.DateSplitField = function(config){
31146     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31147     
31148     this.addEvents({
31149         // raw events
31150          /**
31151          * @event years
31152          * getting the data of years
31153          * @param {Roo.bootstrap.DateSplitField} this
31154          * @param {Object} years
31155          */
31156         "years" : true,
31157         /**
31158          * @event days
31159          * getting the data of days
31160          * @param {Roo.bootstrap.DateSplitField} this
31161          * @param {Object} days
31162          */
31163         "days" : true,
31164         /**
31165          * @event invalid
31166          * Fires after the field has been marked as invalid.
31167          * @param {Roo.form.Field} this
31168          * @param {String} msg The validation message
31169          */
31170         invalid : true,
31171        /**
31172          * @event valid
31173          * Fires after the field has been validated with no errors.
31174          * @param {Roo.form.Field} this
31175          */
31176         valid : true
31177     });
31178 };
31179
31180 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31181     
31182     fieldLabel : '',
31183     labelAlign : 'top',
31184     labelWidth : 3,
31185     dayAllowBlank : false,
31186     monthAllowBlank : false,
31187     yearAllowBlank : false,
31188     dayPlaceholder : '',
31189     monthPlaceholder : '',
31190     yearPlaceholder : '',
31191     dayFormat : 'd',
31192     monthFormat : 'm',
31193     yearFormat : 'Y',
31194     isFormField : true,
31195     labellg : 0,
31196     labelmd : 0,
31197     labelsm : 0,
31198     labelxs : 0,
31199     
31200     getAutoCreate : function()
31201     {
31202         var cfg = {
31203             tag : 'div',
31204             cls : 'row roo-date-split-field-group',
31205             cn : [
31206                 {
31207                     tag : 'input',
31208                     type : 'hidden',
31209                     cls : 'form-hidden-field roo-date-split-field-group-value',
31210                     name : this.name
31211                 }
31212             ]
31213         };
31214         
31215         var labelCls = 'col-md-12';
31216         var contentCls = 'col-md-4';
31217         
31218         if(this.fieldLabel){
31219             
31220             var label = {
31221                 tag : 'div',
31222                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31223                 cn : [
31224                     {
31225                         tag : 'label',
31226                         html : this.fieldLabel
31227                     }
31228                 ]
31229             };
31230             
31231             if(this.labelAlign == 'left'){
31232             
31233                 if(this.labelWidth > 12){
31234                     label.style = "width: " + this.labelWidth + 'px';
31235                 }
31236
31237                 if(this.labelWidth < 13 && this.labelmd == 0){
31238                     this.labelmd = this.labelWidth;
31239                 }
31240
31241                 if(this.labellg > 0){
31242                     labelCls = ' col-lg-' + this.labellg;
31243                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31244                 }
31245
31246                 if(this.labelmd > 0){
31247                     labelCls = ' col-md-' + this.labelmd;
31248                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31249                 }
31250
31251                 if(this.labelsm > 0){
31252                     labelCls = ' col-sm-' + this.labelsm;
31253                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31254                 }
31255
31256                 if(this.labelxs > 0){
31257                     labelCls = ' col-xs-' + this.labelxs;
31258                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31259                 }
31260             }
31261             
31262             label.cls += ' ' + labelCls;
31263             
31264             cfg.cn.push(label);
31265         }
31266         
31267         Roo.each(['day', 'month', 'year'], function(t){
31268             cfg.cn.push({
31269                 tag : 'div',
31270                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31271             });
31272         }, this);
31273         
31274         return cfg;
31275     },
31276     
31277     inputEl: function ()
31278     {
31279         return this.el.select('.roo-date-split-field-group-value', true).first();
31280     },
31281     
31282     onRender : function(ct, position) 
31283     {
31284         var _this = this;
31285         
31286         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31287         
31288         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31289         
31290         this.dayField = new Roo.bootstrap.ComboBox({
31291             allowBlank : this.dayAllowBlank,
31292             alwaysQuery : true,
31293             displayField : 'value',
31294             editable : false,
31295             fieldLabel : '',
31296             forceSelection : true,
31297             mode : 'local',
31298             placeholder : this.dayPlaceholder,
31299             selectOnFocus : true,
31300             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31301             triggerAction : 'all',
31302             typeAhead : true,
31303             valueField : 'value',
31304             store : new Roo.data.SimpleStore({
31305                 data : (function() {    
31306                     var days = [];
31307                     _this.fireEvent('days', _this, days);
31308                     return days;
31309                 })(),
31310                 fields : [ 'value' ]
31311             }),
31312             listeners : {
31313                 select : function (_self, record, index)
31314                 {
31315                     _this.setValue(_this.getValue());
31316                 }
31317             }
31318         });
31319
31320         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31321         
31322         this.monthField = new Roo.bootstrap.MonthField({
31323             after : '<i class=\"fa fa-calendar\"></i>',
31324             allowBlank : this.monthAllowBlank,
31325             placeholder : this.monthPlaceholder,
31326             readOnly : true,
31327             listeners : {
31328                 render : function (_self)
31329                 {
31330                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31331                         e.preventDefault();
31332                         _self.focus();
31333                     });
31334                 },
31335                 select : function (_self, oldvalue, newvalue)
31336                 {
31337                     _this.setValue(_this.getValue());
31338                 }
31339             }
31340         });
31341         
31342         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31343         
31344         this.yearField = new Roo.bootstrap.ComboBox({
31345             allowBlank : this.yearAllowBlank,
31346             alwaysQuery : true,
31347             displayField : 'value',
31348             editable : false,
31349             fieldLabel : '',
31350             forceSelection : true,
31351             mode : 'local',
31352             placeholder : this.yearPlaceholder,
31353             selectOnFocus : true,
31354             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31355             triggerAction : 'all',
31356             typeAhead : true,
31357             valueField : 'value',
31358             store : new Roo.data.SimpleStore({
31359                 data : (function() {
31360                     var years = [];
31361                     _this.fireEvent('years', _this, years);
31362                     return years;
31363                 })(),
31364                 fields : [ 'value' ]
31365             }),
31366             listeners : {
31367                 select : function (_self, record, index)
31368                 {
31369                     _this.setValue(_this.getValue());
31370                 }
31371             }
31372         });
31373
31374         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31375     },
31376     
31377     setValue : function(v, format)
31378     {
31379         this.inputEl.dom.value = v;
31380         
31381         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31382         
31383         var d = Date.parseDate(v, f);
31384         
31385         if(!d){
31386             this.validate();
31387             return;
31388         }
31389         
31390         this.setDay(d.format(this.dayFormat));
31391         this.setMonth(d.format(this.monthFormat));
31392         this.setYear(d.format(this.yearFormat));
31393         
31394         this.validate();
31395         
31396         return;
31397     },
31398     
31399     setDay : function(v)
31400     {
31401         this.dayField.setValue(v);
31402         this.inputEl.dom.value = this.getValue();
31403         this.validate();
31404         return;
31405     },
31406     
31407     setMonth : function(v)
31408     {
31409         this.monthField.setValue(v, true);
31410         this.inputEl.dom.value = this.getValue();
31411         this.validate();
31412         return;
31413     },
31414     
31415     setYear : function(v)
31416     {
31417         this.yearField.setValue(v);
31418         this.inputEl.dom.value = this.getValue();
31419         this.validate();
31420         return;
31421     },
31422     
31423     getDay : function()
31424     {
31425         return this.dayField.getValue();
31426     },
31427     
31428     getMonth : function()
31429     {
31430         return this.monthField.getValue();
31431     },
31432     
31433     getYear : function()
31434     {
31435         return this.yearField.getValue();
31436     },
31437     
31438     getValue : function()
31439     {
31440         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31441         
31442         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31443         
31444         return date;
31445     },
31446     
31447     reset : function()
31448     {
31449         this.setDay('');
31450         this.setMonth('');
31451         this.setYear('');
31452         this.inputEl.dom.value = '';
31453         this.validate();
31454         return;
31455     },
31456     
31457     validate : function()
31458     {
31459         var d = this.dayField.validate();
31460         var m = this.monthField.validate();
31461         var y = this.yearField.validate();
31462         
31463         var valid = true;
31464         
31465         if(
31466                 (!this.dayAllowBlank && !d) ||
31467                 (!this.monthAllowBlank && !m) ||
31468                 (!this.yearAllowBlank && !y)
31469         ){
31470             valid = false;
31471         }
31472         
31473         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31474             return valid;
31475         }
31476         
31477         if(valid){
31478             this.markValid();
31479             return valid;
31480         }
31481         
31482         this.markInvalid();
31483         
31484         return valid;
31485     },
31486     
31487     markValid : function()
31488     {
31489         
31490         var label = this.el.select('label', true).first();
31491         var icon = this.el.select('i.fa-star', true).first();
31492
31493         if(label && icon){
31494             icon.remove();
31495         }
31496         
31497         this.fireEvent('valid', this);
31498     },
31499     
31500      /**
31501      * Mark this field as invalid
31502      * @param {String} msg The validation message
31503      */
31504     markInvalid : function(msg)
31505     {
31506         
31507         var label = this.el.select('label', true).first();
31508         var icon = this.el.select('i.fa-star', true).first();
31509
31510         if(label && !icon){
31511             this.el.select('.roo-date-split-field-label', true).createChild({
31512                 tag : 'i',
31513                 cls : 'text-danger fa fa-lg fa-star',
31514                 tooltip : 'This field is required',
31515                 style : 'margin-right:5px;'
31516             }, label, true);
31517         }
31518         
31519         this.fireEvent('invalid', this, msg);
31520     },
31521     
31522     clearInvalid : function()
31523     {
31524         var label = this.el.select('label', true).first();
31525         var icon = this.el.select('i.fa-star', true).first();
31526
31527         if(label && icon){
31528             icon.remove();
31529         }
31530         
31531         this.fireEvent('valid', this);
31532     },
31533     
31534     getName: function()
31535     {
31536         return this.name;
31537     }
31538     
31539 });
31540
31541  /**
31542  *
31543  * This is based on 
31544  * http://masonry.desandro.com
31545  *
31546  * The idea is to render all the bricks based on vertical width...
31547  *
31548  * The original code extends 'outlayer' - we might need to use that....
31549  * 
31550  */
31551
31552
31553 /**
31554  * @class Roo.bootstrap.LayoutMasonry
31555  * @extends Roo.bootstrap.Component
31556  * Bootstrap Layout Masonry class
31557  * 
31558  * @constructor
31559  * Create a new Element
31560  * @param {Object} config The config object
31561  */
31562
31563 Roo.bootstrap.LayoutMasonry = function(config){
31564     
31565     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31566     
31567     this.bricks = [];
31568     
31569     Roo.bootstrap.LayoutMasonry.register(this);
31570     
31571     this.addEvents({
31572         // raw events
31573         /**
31574          * @event layout
31575          * Fire after layout the items
31576          * @param {Roo.bootstrap.LayoutMasonry} this
31577          * @param {Roo.EventObject} e
31578          */
31579         "layout" : true
31580     });
31581     
31582 };
31583
31584 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31585     
31586     /**
31587      * @cfg {Boolean} isLayoutInstant = no animation?
31588      */   
31589     isLayoutInstant : false, // needed?
31590    
31591     /**
31592      * @cfg {Number} boxWidth  width of the columns
31593      */   
31594     boxWidth : 450,
31595     
31596       /**
31597      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31598      */   
31599     boxHeight : 0,
31600     
31601     /**
31602      * @cfg {Number} padWidth padding below box..
31603      */   
31604     padWidth : 10, 
31605     
31606     /**
31607      * @cfg {Number} gutter gutter width..
31608      */   
31609     gutter : 10,
31610     
31611      /**
31612      * @cfg {Number} maxCols maximum number of columns
31613      */   
31614     
31615     maxCols: 0,
31616     
31617     /**
31618      * @cfg {Boolean} isAutoInitial defalut true
31619      */   
31620     isAutoInitial : true, 
31621     
31622     containerWidth: 0,
31623     
31624     /**
31625      * @cfg {Boolean} isHorizontal defalut false
31626      */   
31627     isHorizontal : false, 
31628
31629     currentSize : null,
31630     
31631     tag: 'div',
31632     
31633     cls: '',
31634     
31635     bricks: null, //CompositeElement
31636     
31637     cols : 1,
31638     
31639     _isLayoutInited : false,
31640     
31641 //    isAlternative : false, // only use for vertical layout...
31642     
31643     /**
31644      * @cfg {Number} alternativePadWidth padding below box..
31645      */   
31646     alternativePadWidth : 50,
31647     
31648     selectedBrick : [],
31649     
31650     getAutoCreate : function(){
31651         
31652         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31653         
31654         var cfg = {
31655             tag: this.tag,
31656             cls: 'blog-masonary-wrapper ' + this.cls,
31657             cn : {
31658                 cls : 'mas-boxes masonary'
31659             }
31660         };
31661         
31662         return cfg;
31663     },
31664     
31665     getChildContainer: function( )
31666     {
31667         if (this.boxesEl) {
31668             return this.boxesEl;
31669         }
31670         
31671         this.boxesEl = this.el.select('.mas-boxes').first();
31672         
31673         return this.boxesEl;
31674     },
31675     
31676     
31677     initEvents : function()
31678     {
31679         var _this = this;
31680         
31681         if(this.isAutoInitial){
31682             Roo.log('hook children rendered');
31683             this.on('childrenrendered', function() {
31684                 Roo.log('children rendered');
31685                 _this.initial();
31686             } ,this);
31687         }
31688     },
31689     
31690     initial : function()
31691     {
31692         this.selectedBrick = [];
31693         
31694         this.currentSize = this.el.getBox(true);
31695         
31696         Roo.EventManager.onWindowResize(this.resize, this); 
31697
31698         if(!this.isAutoInitial){
31699             this.layout();
31700             return;
31701         }
31702         
31703         this.layout();
31704         
31705         return;
31706         //this.layout.defer(500,this);
31707         
31708     },
31709     
31710     resize : function()
31711     {
31712         var cs = this.el.getBox(true);
31713         
31714         if (
31715                 this.currentSize.width == cs.width && 
31716                 this.currentSize.x == cs.x && 
31717                 this.currentSize.height == cs.height && 
31718                 this.currentSize.y == cs.y 
31719         ) {
31720             Roo.log("no change in with or X or Y");
31721             return;
31722         }
31723         
31724         this.currentSize = cs;
31725         
31726         this.layout();
31727         
31728     },
31729     
31730     layout : function()
31731     {   
31732         this._resetLayout();
31733         
31734         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31735         
31736         this.layoutItems( isInstant );
31737       
31738         this._isLayoutInited = true;
31739         
31740         this.fireEvent('layout', this);
31741         
31742     },
31743     
31744     _resetLayout : function()
31745     {
31746         if(this.isHorizontal){
31747             this.horizontalMeasureColumns();
31748             return;
31749         }
31750         
31751         this.verticalMeasureColumns();
31752         
31753     },
31754     
31755     verticalMeasureColumns : function()
31756     {
31757         this.getContainerWidth();
31758         
31759 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31760 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31761 //            return;
31762 //        }
31763         
31764         var boxWidth = this.boxWidth + this.padWidth;
31765         
31766         if(this.containerWidth < this.boxWidth){
31767             boxWidth = this.containerWidth
31768         }
31769         
31770         var containerWidth = this.containerWidth;
31771         
31772         var cols = Math.floor(containerWidth / boxWidth);
31773         
31774         this.cols = Math.max( cols, 1 );
31775         
31776         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31777         
31778         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31779         
31780         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31781         
31782         this.colWidth = boxWidth + avail - this.padWidth;
31783         
31784         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31785         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31786     },
31787     
31788     horizontalMeasureColumns : function()
31789     {
31790         this.getContainerWidth();
31791         
31792         var boxWidth = this.boxWidth;
31793         
31794         if(this.containerWidth < boxWidth){
31795             boxWidth = this.containerWidth;
31796         }
31797         
31798         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31799         
31800         this.el.setHeight(boxWidth);
31801         
31802     },
31803     
31804     getContainerWidth : function()
31805     {
31806         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31807     },
31808     
31809     layoutItems : function( isInstant )
31810     {
31811         Roo.log(this.bricks);
31812         
31813         var items = Roo.apply([], this.bricks);
31814         
31815         if(this.isHorizontal){
31816             this._horizontalLayoutItems( items , isInstant );
31817             return;
31818         }
31819         
31820 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31821 //            this._verticalAlternativeLayoutItems( items , isInstant );
31822 //            return;
31823 //        }
31824         
31825         this._verticalLayoutItems( items , isInstant );
31826         
31827     },
31828     
31829     _verticalLayoutItems : function ( items , isInstant)
31830     {
31831         if ( !items || !items.length ) {
31832             return;
31833         }
31834         
31835         var standard = [
31836             ['xs', 'xs', 'xs', 'tall'],
31837             ['xs', 'xs', 'tall'],
31838             ['xs', 'xs', 'sm'],
31839             ['xs', 'xs', 'xs'],
31840             ['xs', 'tall'],
31841             ['xs', 'sm'],
31842             ['xs', 'xs'],
31843             ['xs'],
31844             
31845             ['sm', 'xs', 'xs'],
31846             ['sm', 'xs'],
31847             ['sm'],
31848             
31849             ['tall', 'xs', 'xs', 'xs'],
31850             ['tall', 'xs', 'xs'],
31851             ['tall', 'xs'],
31852             ['tall']
31853             
31854         ];
31855         
31856         var queue = [];
31857         
31858         var boxes = [];
31859         
31860         var box = [];
31861         
31862         Roo.each(items, function(item, k){
31863             
31864             switch (item.size) {
31865                 // these layouts take up a full box,
31866                 case 'md' :
31867                 case 'md-left' :
31868                 case 'md-right' :
31869                 case 'wide' :
31870                     
31871                     if(box.length){
31872                         boxes.push(box);
31873                         box = [];
31874                     }
31875                     
31876                     boxes.push([item]);
31877                     
31878                     break;
31879                     
31880                 case 'xs' :
31881                 case 'sm' :
31882                 case 'tall' :
31883                     
31884                     box.push(item);
31885                     
31886                     break;
31887                 default :
31888                     break;
31889                     
31890             }
31891             
31892         }, this);
31893         
31894         if(box.length){
31895             boxes.push(box);
31896             box = [];
31897         }
31898         
31899         var filterPattern = function(box, length)
31900         {
31901             if(!box.length){
31902                 return;
31903             }
31904             
31905             var match = false;
31906             
31907             var pattern = box.slice(0, length);
31908             
31909             var format = [];
31910             
31911             Roo.each(pattern, function(i){
31912                 format.push(i.size);
31913             }, this);
31914             
31915             Roo.each(standard, function(s){
31916                 
31917                 if(String(s) != String(format)){
31918                     return;
31919                 }
31920                 
31921                 match = true;
31922                 return false;
31923                 
31924             }, this);
31925             
31926             if(!match && length == 1){
31927                 return;
31928             }
31929             
31930             if(!match){
31931                 filterPattern(box, length - 1);
31932                 return;
31933             }
31934                 
31935             queue.push(pattern);
31936
31937             box = box.slice(length, box.length);
31938
31939             filterPattern(box, 4);
31940
31941             return;
31942             
31943         }
31944         
31945         Roo.each(boxes, function(box, k){
31946             
31947             if(!box.length){
31948                 return;
31949             }
31950             
31951             if(box.length == 1){
31952                 queue.push(box);
31953                 return;
31954             }
31955             
31956             filterPattern(box, 4);
31957             
31958         }, this);
31959         
31960         this._processVerticalLayoutQueue( queue, isInstant );
31961         
31962     },
31963     
31964 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31965 //    {
31966 //        if ( !items || !items.length ) {
31967 //            return;
31968 //        }
31969 //
31970 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31971 //        
31972 //    },
31973     
31974     _horizontalLayoutItems : function ( items , isInstant)
31975     {
31976         if ( !items || !items.length || items.length < 3) {
31977             return;
31978         }
31979         
31980         items.reverse();
31981         
31982         var eItems = items.slice(0, 3);
31983         
31984         items = items.slice(3, items.length);
31985         
31986         var standard = [
31987             ['xs', 'xs', 'xs', 'wide'],
31988             ['xs', 'xs', 'wide'],
31989             ['xs', 'xs', 'sm'],
31990             ['xs', 'xs', 'xs'],
31991             ['xs', 'wide'],
31992             ['xs', 'sm'],
31993             ['xs', 'xs'],
31994             ['xs'],
31995             
31996             ['sm', 'xs', 'xs'],
31997             ['sm', 'xs'],
31998             ['sm'],
31999             
32000             ['wide', 'xs', 'xs', 'xs'],
32001             ['wide', 'xs', 'xs'],
32002             ['wide', 'xs'],
32003             ['wide'],
32004             
32005             ['wide-thin']
32006         ];
32007         
32008         var queue = [];
32009         
32010         var boxes = [];
32011         
32012         var box = [];
32013         
32014         Roo.each(items, function(item, k){
32015             
32016             switch (item.size) {
32017                 case 'md' :
32018                 case 'md-left' :
32019                 case 'md-right' :
32020                 case 'tall' :
32021                     
32022                     if(box.length){
32023                         boxes.push(box);
32024                         box = [];
32025                     }
32026                     
32027                     boxes.push([item]);
32028                     
32029                     break;
32030                     
32031                 case 'xs' :
32032                 case 'sm' :
32033                 case 'wide' :
32034                 case 'wide-thin' :
32035                     
32036                     box.push(item);
32037                     
32038                     break;
32039                 default :
32040                     break;
32041                     
32042             }
32043             
32044         }, this);
32045         
32046         if(box.length){
32047             boxes.push(box);
32048             box = [];
32049         }
32050         
32051         var filterPattern = function(box, length)
32052         {
32053             if(!box.length){
32054                 return;
32055             }
32056             
32057             var match = false;
32058             
32059             var pattern = box.slice(0, length);
32060             
32061             var format = [];
32062             
32063             Roo.each(pattern, function(i){
32064                 format.push(i.size);
32065             }, this);
32066             
32067             Roo.each(standard, function(s){
32068                 
32069                 if(String(s) != String(format)){
32070                     return;
32071                 }
32072                 
32073                 match = true;
32074                 return false;
32075                 
32076             }, this);
32077             
32078             if(!match && length == 1){
32079                 return;
32080             }
32081             
32082             if(!match){
32083                 filterPattern(box, length - 1);
32084                 return;
32085             }
32086                 
32087             queue.push(pattern);
32088
32089             box = box.slice(length, box.length);
32090
32091             filterPattern(box, 4);
32092
32093             return;
32094             
32095         }
32096         
32097         Roo.each(boxes, function(box, k){
32098             
32099             if(!box.length){
32100                 return;
32101             }
32102             
32103             if(box.length == 1){
32104                 queue.push(box);
32105                 return;
32106             }
32107             
32108             filterPattern(box, 4);
32109             
32110         }, this);
32111         
32112         
32113         var prune = [];
32114         
32115         var pos = this.el.getBox(true);
32116         
32117         var minX = pos.x;
32118         
32119         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32120         
32121         var hit_end = false;
32122         
32123         Roo.each(queue, function(box){
32124             
32125             if(hit_end){
32126                 
32127                 Roo.each(box, function(b){
32128                 
32129                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32130                     b.el.hide();
32131
32132                 }, this);
32133
32134                 return;
32135             }
32136             
32137             var mx = 0;
32138             
32139             Roo.each(box, function(b){
32140                 
32141                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32142                 b.el.show();
32143
32144                 mx = Math.max(mx, b.x);
32145                 
32146             }, this);
32147             
32148             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32149             
32150             if(maxX < minX){
32151                 
32152                 Roo.each(box, function(b){
32153                 
32154                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32155                     b.el.hide();
32156                     
32157                 }, this);
32158                 
32159                 hit_end = true;
32160                 
32161                 return;
32162             }
32163             
32164             prune.push(box);
32165             
32166         }, this);
32167         
32168         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32169     },
32170     
32171     /** Sets position of item in DOM
32172     * @param {Element} item
32173     * @param {Number} x - horizontal position
32174     * @param {Number} y - vertical position
32175     * @param {Boolean} isInstant - disables transitions
32176     */
32177     _processVerticalLayoutQueue : function( queue, isInstant )
32178     {
32179         var pos = this.el.getBox(true);
32180         var x = pos.x;
32181         var y = pos.y;
32182         var maxY = [];
32183         
32184         for (var i = 0; i < this.cols; i++){
32185             maxY[i] = pos.y;
32186         }
32187         
32188         Roo.each(queue, function(box, k){
32189             
32190             var col = k % this.cols;
32191             
32192             Roo.each(box, function(b,kk){
32193                 
32194                 b.el.position('absolute');
32195                 
32196                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32197                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32198                 
32199                 if(b.size == 'md-left' || b.size == 'md-right'){
32200                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32201                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32202                 }
32203                 
32204                 b.el.setWidth(width);
32205                 b.el.setHeight(height);
32206                 // iframe?
32207                 b.el.select('iframe',true).setSize(width,height);
32208                 
32209             }, this);
32210             
32211             for (var i = 0; i < this.cols; i++){
32212                 
32213                 if(maxY[i] < maxY[col]){
32214                     col = i;
32215                     continue;
32216                 }
32217                 
32218                 col = Math.min(col, i);
32219                 
32220             }
32221             
32222             x = pos.x + col * (this.colWidth + this.padWidth);
32223             
32224             y = maxY[col];
32225             
32226             var positions = [];
32227             
32228             switch (box.length){
32229                 case 1 :
32230                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32231                     break;
32232                 case 2 :
32233                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32234                     break;
32235                 case 3 :
32236                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32237                     break;
32238                 case 4 :
32239                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32240                     break;
32241                 default :
32242                     break;
32243             }
32244             
32245             Roo.each(box, function(b,kk){
32246                 
32247                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32248                 
32249                 var sz = b.el.getSize();
32250                 
32251                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32252                 
32253             }, this);
32254             
32255         }, this);
32256         
32257         var mY = 0;
32258         
32259         for (var i = 0; i < this.cols; i++){
32260             mY = Math.max(mY, maxY[i]);
32261         }
32262         
32263         this.el.setHeight(mY - pos.y);
32264         
32265     },
32266     
32267 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32268 //    {
32269 //        var pos = this.el.getBox(true);
32270 //        var x = pos.x;
32271 //        var y = pos.y;
32272 //        var maxX = pos.right;
32273 //        
32274 //        var maxHeight = 0;
32275 //        
32276 //        Roo.each(items, function(item, k){
32277 //            
32278 //            var c = k % 2;
32279 //            
32280 //            item.el.position('absolute');
32281 //                
32282 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32283 //
32284 //            item.el.setWidth(width);
32285 //
32286 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32287 //
32288 //            item.el.setHeight(height);
32289 //            
32290 //            if(c == 0){
32291 //                item.el.setXY([x, y], isInstant ? false : true);
32292 //            } else {
32293 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32294 //            }
32295 //            
32296 //            y = y + height + this.alternativePadWidth;
32297 //            
32298 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32299 //            
32300 //        }, this);
32301 //        
32302 //        this.el.setHeight(maxHeight);
32303 //        
32304 //    },
32305     
32306     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32307     {
32308         var pos = this.el.getBox(true);
32309         
32310         var minX = pos.x;
32311         var minY = pos.y;
32312         
32313         var maxX = pos.right;
32314         
32315         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32316         
32317         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32318         
32319         Roo.each(queue, function(box, k){
32320             
32321             Roo.each(box, function(b, kk){
32322                 
32323                 b.el.position('absolute');
32324                 
32325                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32326                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32327                 
32328                 if(b.size == 'md-left' || b.size == 'md-right'){
32329                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32330                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32331                 }
32332                 
32333                 b.el.setWidth(width);
32334                 b.el.setHeight(height);
32335                 
32336             }, this);
32337             
32338             if(!box.length){
32339                 return;
32340             }
32341             
32342             var positions = [];
32343             
32344             switch (box.length){
32345                 case 1 :
32346                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32347                     break;
32348                 case 2 :
32349                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32350                     break;
32351                 case 3 :
32352                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32353                     break;
32354                 case 4 :
32355                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32356                     break;
32357                 default :
32358                     break;
32359             }
32360             
32361             Roo.each(box, function(b,kk){
32362                 
32363                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32364                 
32365                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32366                 
32367             }, this);
32368             
32369         }, this);
32370         
32371     },
32372     
32373     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32374     {
32375         Roo.each(eItems, function(b,k){
32376             
32377             b.size = (k == 0) ? 'sm' : 'xs';
32378             b.x = (k == 0) ? 2 : 1;
32379             b.y = (k == 0) ? 2 : 1;
32380             
32381             b.el.position('absolute');
32382             
32383             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32384                 
32385             b.el.setWidth(width);
32386             
32387             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32388             
32389             b.el.setHeight(height);
32390             
32391         }, this);
32392
32393         var positions = [];
32394         
32395         positions.push({
32396             x : maxX - this.unitWidth * 2 - this.gutter,
32397             y : minY
32398         });
32399         
32400         positions.push({
32401             x : maxX - this.unitWidth,
32402             y : minY + (this.unitWidth + this.gutter) * 2
32403         });
32404         
32405         positions.push({
32406             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32407             y : minY
32408         });
32409         
32410         Roo.each(eItems, function(b,k){
32411             
32412             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32413
32414         }, this);
32415         
32416     },
32417     
32418     getVerticalOneBoxColPositions : function(x, y, box)
32419     {
32420         var pos = [];
32421         
32422         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32423         
32424         if(box[0].size == 'md-left'){
32425             rand = 0;
32426         }
32427         
32428         if(box[0].size == 'md-right'){
32429             rand = 1;
32430         }
32431         
32432         pos.push({
32433             x : x + (this.unitWidth + this.gutter) * rand,
32434             y : y
32435         });
32436         
32437         return pos;
32438     },
32439     
32440     getVerticalTwoBoxColPositions : function(x, y, box)
32441     {
32442         var pos = [];
32443         
32444         if(box[0].size == 'xs'){
32445             
32446             pos.push({
32447                 x : x,
32448                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32449             });
32450
32451             pos.push({
32452                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32453                 y : y
32454             });
32455             
32456             return pos;
32457             
32458         }
32459         
32460         pos.push({
32461             x : x,
32462             y : y
32463         });
32464
32465         pos.push({
32466             x : x + (this.unitWidth + this.gutter) * 2,
32467             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32468         });
32469         
32470         return pos;
32471         
32472     },
32473     
32474     getVerticalThreeBoxColPositions : function(x, y, box)
32475     {
32476         var pos = [];
32477         
32478         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32479             
32480             pos.push({
32481                 x : x,
32482                 y : y
32483             });
32484
32485             pos.push({
32486                 x : x + (this.unitWidth + this.gutter) * 1,
32487                 y : y
32488             });
32489             
32490             pos.push({
32491                 x : x + (this.unitWidth + this.gutter) * 2,
32492                 y : y
32493             });
32494             
32495             return pos;
32496             
32497         }
32498         
32499         if(box[0].size == 'xs' && box[1].size == 'xs'){
32500             
32501             pos.push({
32502                 x : x,
32503                 y : y
32504             });
32505
32506             pos.push({
32507                 x : x,
32508                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32509             });
32510             
32511             pos.push({
32512                 x : x + (this.unitWidth + this.gutter) * 1,
32513                 y : y
32514             });
32515             
32516             return pos;
32517             
32518         }
32519         
32520         pos.push({
32521             x : x,
32522             y : y
32523         });
32524
32525         pos.push({
32526             x : x + (this.unitWidth + this.gutter) * 2,
32527             y : y
32528         });
32529
32530         pos.push({
32531             x : x + (this.unitWidth + this.gutter) * 2,
32532             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32533         });
32534             
32535         return pos;
32536         
32537     },
32538     
32539     getVerticalFourBoxColPositions : function(x, y, box)
32540     {
32541         var pos = [];
32542         
32543         if(box[0].size == 'xs'){
32544             
32545             pos.push({
32546                 x : x,
32547                 y : y
32548             });
32549
32550             pos.push({
32551                 x : x,
32552                 y : y + (this.unitHeight + this.gutter) * 1
32553             });
32554             
32555             pos.push({
32556                 x : x,
32557                 y : y + (this.unitHeight + this.gutter) * 2
32558             });
32559             
32560             pos.push({
32561                 x : x + (this.unitWidth + this.gutter) * 1,
32562                 y : y
32563             });
32564             
32565             return pos;
32566             
32567         }
32568         
32569         pos.push({
32570             x : x,
32571             y : y
32572         });
32573
32574         pos.push({
32575             x : x + (this.unitWidth + this.gutter) * 2,
32576             y : y
32577         });
32578
32579         pos.push({
32580             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32581             y : y + (this.unitHeight + this.gutter) * 1
32582         });
32583
32584         pos.push({
32585             x : x + (this.unitWidth + this.gutter) * 2,
32586             y : y + (this.unitWidth + this.gutter) * 2
32587         });
32588
32589         return pos;
32590         
32591     },
32592     
32593     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32594     {
32595         var pos = [];
32596         
32597         if(box[0].size == 'md-left'){
32598             pos.push({
32599                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32600                 y : minY
32601             });
32602             
32603             return pos;
32604         }
32605         
32606         if(box[0].size == 'md-right'){
32607             pos.push({
32608                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32609                 y : minY + (this.unitWidth + this.gutter) * 1
32610             });
32611             
32612             return pos;
32613         }
32614         
32615         var rand = Math.floor(Math.random() * (4 - box[0].y));
32616         
32617         pos.push({
32618             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32619             y : minY + (this.unitWidth + this.gutter) * rand
32620         });
32621         
32622         return pos;
32623         
32624     },
32625     
32626     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32627     {
32628         var pos = [];
32629         
32630         if(box[0].size == 'xs'){
32631             
32632             pos.push({
32633                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32634                 y : minY
32635             });
32636
32637             pos.push({
32638                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32639                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32640             });
32641             
32642             return pos;
32643             
32644         }
32645         
32646         pos.push({
32647             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32648             y : minY
32649         });
32650
32651         pos.push({
32652             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32653             y : minY + (this.unitWidth + this.gutter) * 2
32654         });
32655         
32656         return pos;
32657         
32658     },
32659     
32660     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32661     {
32662         var pos = [];
32663         
32664         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32665             
32666             pos.push({
32667                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32668                 y : minY
32669             });
32670
32671             pos.push({
32672                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32673                 y : minY + (this.unitWidth + this.gutter) * 1
32674             });
32675             
32676             pos.push({
32677                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32678                 y : minY + (this.unitWidth + this.gutter) * 2
32679             });
32680             
32681             return pos;
32682             
32683         }
32684         
32685         if(box[0].size == 'xs' && box[1].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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32694                 y : minY
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) * 1
32700             });
32701             
32702             return pos;
32703             
32704         }
32705         
32706         pos.push({
32707             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32708             y : minY
32709         });
32710
32711         pos.push({
32712             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32713             y : minY + (this.unitWidth + this.gutter) * 2
32714         });
32715
32716         pos.push({
32717             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32718             y : minY + (this.unitWidth + this.gutter) * 2
32719         });
32720             
32721         return pos;
32722         
32723     },
32724     
32725     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32726     {
32727         var pos = [];
32728         
32729         if(box[0].size == 'xs'){
32730             
32731             pos.push({
32732                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32733                 y : minY
32734             });
32735
32736             pos.push({
32737                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32738                 y : minY
32739             });
32740             
32741             pos.push({
32742                 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),
32743                 y : minY
32744             });
32745             
32746             pos.push({
32747                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32748                 y : minY + (this.unitWidth + this.gutter) * 1
32749             });
32750             
32751             return pos;
32752             
32753         }
32754         
32755         pos.push({
32756             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32757             y : minY
32758         });
32759         
32760         pos.push({
32761             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32762             y : minY + (this.unitWidth + this.gutter) * 2
32763         });
32764         
32765         pos.push({
32766             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32767             y : minY + (this.unitWidth + this.gutter) * 2
32768         });
32769         
32770         pos.push({
32771             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),
32772             y : minY + (this.unitWidth + this.gutter) * 2
32773         });
32774
32775         return pos;
32776         
32777     },
32778     
32779     /**
32780     * remove a Masonry Brick
32781     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32782     */
32783     removeBrick : function(brick_id)
32784     {
32785         if (!brick_id) {
32786             return;
32787         }
32788         
32789         for (var i = 0; i<this.bricks.length; i++) {
32790             if (this.bricks[i].id == brick_id) {
32791                 this.bricks.splice(i,1);
32792                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32793                 this.initial();
32794             }
32795         }
32796     },
32797     
32798     /**
32799     * adds a Masonry Brick
32800     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32801     */
32802     addBrick : function(cfg)
32803     {
32804         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32805         //this.register(cn);
32806         cn.parentId = this.id;
32807         cn.render(this.el);
32808         return cn;
32809     },
32810     
32811     /**
32812     * register a Masonry Brick
32813     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32814     */
32815     
32816     register : function(brick)
32817     {
32818         this.bricks.push(brick);
32819         brick.masonryId = this.id;
32820     },
32821     
32822     /**
32823     * clear all the Masonry Brick
32824     */
32825     clearAll : function()
32826     {
32827         this.bricks = [];
32828         //this.getChildContainer().dom.innerHTML = "";
32829         this.el.dom.innerHTML = '';
32830     },
32831     
32832     getSelected : function()
32833     {
32834         if (!this.selectedBrick) {
32835             return false;
32836         }
32837         
32838         return this.selectedBrick;
32839     }
32840 });
32841
32842 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32843     
32844     groups: {},
32845      /**
32846     * register a Masonry Layout
32847     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32848     */
32849     
32850     register : function(layout)
32851     {
32852         this.groups[layout.id] = layout;
32853     },
32854     /**
32855     * fetch a  Masonry Layout based on the masonry layout ID
32856     * @param {string} the masonry layout to add
32857     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32858     */
32859     
32860     get: function(layout_id) {
32861         if (typeof(this.groups[layout_id]) == 'undefined') {
32862             return false;
32863         }
32864         return this.groups[layout_id] ;
32865     }
32866     
32867     
32868     
32869 });
32870
32871  
32872
32873  /**
32874  *
32875  * This is based on 
32876  * http://masonry.desandro.com
32877  *
32878  * The idea is to render all the bricks based on vertical width...
32879  *
32880  * The original code extends 'outlayer' - we might need to use that....
32881  * 
32882  */
32883
32884
32885 /**
32886  * @class Roo.bootstrap.LayoutMasonryAuto
32887  * @extends Roo.bootstrap.Component
32888  * Bootstrap Layout Masonry class
32889  * 
32890  * @constructor
32891  * Create a new Element
32892  * @param {Object} config The config object
32893  */
32894
32895 Roo.bootstrap.LayoutMasonryAuto = function(config){
32896     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32897 };
32898
32899 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32900     
32901       /**
32902      * @cfg {Boolean} isFitWidth  - resize the width..
32903      */   
32904     isFitWidth : false,  // options..
32905     /**
32906      * @cfg {Boolean} isOriginLeft = left align?
32907      */   
32908     isOriginLeft : true,
32909     /**
32910      * @cfg {Boolean} isOriginTop = top align?
32911      */   
32912     isOriginTop : false,
32913     /**
32914      * @cfg {Boolean} isLayoutInstant = no animation?
32915      */   
32916     isLayoutInstant : false, // needed?
32917     /**
32918      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32919      */   
32920     isResizingContainer : true,
32921     /**
32922      * @cfg {Number} columnWidth  width of the columns 
32923      */   
32924     
32925     columnWidth : 0,
32926     
32927     /**
32928      * @cfg {Number} maxCols maximum number of columns
32929      */   
32930     
32931     maxCols: 0,
32932     /**
32933      * @cfg {Number} padHeight padding below box..
32934      */   
32935     
32936     padHeight : 10, 
32937     
32938     /**
32939      * @cfg {Boolean} isAutoInitial defalut true
32940      */   
32941     
32942     isAutoInitial : true, 
32943     
32944     // private?
32945     gutter : 0,
32946     
32947     containerWidth: 0,
32948     initialColumnWidth : 0,
32949     currentSize : null,
32950     
32951     colYs : null, // array.
32952     maxY : 0,
32953     padWidth: 10,
32954     
32955     
32956     tag: 'div',
32957     cls: '',
32958     bricks: null, //CompositeElement
32959     cols : 0, // array?
32960     // element : null, // wrapped now this.el
32961     _isLayoutInited : null, 
32962     
32963     
32964     getAutoCreate : function(){
32965         
32966         var cfg = {
32967             tag: this.tag,
32968             cls: 'blog-masonary-wrapper ' + this.cls,
32969             cn : {
32970                 cls : 'mas-boxes masonary'
32971             }
32972         };
32973         
32974         return cfg;
32975     },
32976     
32977     getChildContainer: function( )
32978     {
32979         if (this.boxesEl) {
32980             return this.boxesEl;
32981         }
32982         
32983         this.boxesEl = this.el.select('.mas-boxes').first();
32984         
32985         return this.boxesEl;
32986     },
32987     
32988     
32989     initEvents : function()
32990     {
32991         var _this = this;
32992         
32993         if(this.isAutoInitial){
32994             Roo.log('hook children rendered');
32995             this.on('childrenrendered', function() {
32996                 Roo.log('children rendered');
32997                 _this.initial();
32998             } ,this);
32999         }
33000         
33001     },
33002     
33003     initial : function()
33004     {
33005         this.reloadItems();
33006
33007         this.currentSize = this.el.getBox(true);
33008
33009         /// was window resize... - let's see if this works..
33010         Roo.EventManager.onWindowResize(this.resize, this); 
33011
33012         if(!this.isAutoInitial){
33013             this.layout();
33014             return;
33015         }
33016         
33017         this.layout.defer(500,this);
33018     },
33019     
33020     reloadItems: function()
33021     {
33022         this.bricks = this.el.select('.masonry-brick', true);
33023         
33024         this.bricks.each(function(b) {
33025             //Roo.log(b.getSize());
33026             if (!b.attr('originalwidth')) {
33027                 b.attr('originalwidth',  b.getSize().width);
33028             }
33029             
33030         });
33031         
33032         Roo.log(this.bricks.elements.length);
33033     },
33034     
33035     resize : function()
33036     {
33037         Roo.log('resize');
33038         var cs = this.el.getBox(true);
33039         
33040         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33041             Roo.log("no change in with or X");
33042             return;
33043         }
33044         this.currentSize = cs;
33045         this.layout();
33046     },
33047     
33048     layout : function()
33049     {
33050          Roo.log('layout');
33051         this._resetLayout();
33052         //this._manageStamps();
33053       
33054         // don't animate first layout
33055         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33056         this.layoutItems( isInstant );
33057       
33058         // flag for initalized
33059         this._isLayoutInited = true;
33060     },
33061     
33062     layoutItems : function( isInstant )
33063     {
33064         //var items = this._getItemsForLayout( this.items );
33065         // original code supports filtering layout items.. we just ignore it..
33066         
33067         this._layoutItems( this.bricks , isInstant );
33068       
33069         this._postLayout();
33070     },
33071     _layoutItems : function ( items , isInstant)
33072     {
33073        //this.fireEvent( 'layout', this, items );
33074     
33075
33076         if ( !items || !items.elements.length ) {
33077           // no items, emit event with empty array
33078             return;
33079         }
33080
33081         var queue = [];
33082         items.each(function(item) {
33083             Roo.log("layout item");
33084             Roo.log(item);
33085             // get x/y object from method
33086             var position = this._getItemLayoutPosition( item );
33087             // enqueue
33088             position.item = item;
33089             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33090             queue.push( position );
33091         }, this);
33092       
33093         this._processLayoutQueue( queue );
33094     },
33095     /** Sets position of item in DOM
33096     * @param {Element} item
33097     * @param {Number} x - horizontal position
33098     * @param {Number} y - vertical position
33099     * @param {Boolean} isInstant - disables transitions
33100     */
33101     _processLayoutQueue : function( queue )
33102     {
33103         for ( var i=0, len = queue.length; i < len; i++ ) {
33104             var obj = queue[i];
33105             obj.item.position('absolute');
33106             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33107         }
33108     },
33109       
33110     
33111     /**
33112     * Any logic you want to do after each layout,
33113     * i.e. size the container
33114     */
33115     _postLayout : function()
33116     {
33117         this.resizeContainer();
33118     },
33119     
33120     resizeContainer : function()
33121     {
33122         if ( !this.isResizingContainer ) {
33123             return;
33124         }
33125         var size = this._getContainerSize();
33126         if ( size ) {
33127             this.el.setSize(size.width,size.height);
33128             this.boxesEl.setSize(size.width,size.height);
33129         }
33130     },
33131     
33132     
33133     
33134     _resetLayout : function()
33135     {
33136         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33137         this.colWidth = this.el.getWidth();
33138         //this.gutter = this.el.getWidth(); 
33139         
33140         this.measureColumns();
33141
33142         // reset column Y
33143         var i = this.cols;
33144         this.colYs = [];
33145         while (i--) {
33146             this.colYs.push( 0 );
33147         }
33148     
33149         this.maxY = 0;
33150     },
33151
33152     measureColumns : function()
33153     {
33154         this.getContainerWidth();
33155       // if columnWidth is 0, default to outerWidth of first item
33156         if ( !this.columnWidth ) {
33157             var firstItem = this.bricks.first();
33158             Roo.log(firstItem);
33159             this.columnWidth  = this.containerWidth;
33160             if (firstItem && firstItem.attr('originalwidth') ) {
33161                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33162             }
33163             // columnWidth fall back to item of first element
33164             Roo.log("set column width?");
33165                         this.initialColumnWidth = this.columnWidth  ;
33166
33167             // if first elem has no width, default to size of container
33168             
33169         }
33170         
33171         
33172         if (this.initialColumnWidth) {
33173             this.columnWidth = this.initialColumnWidth;
33174         }
33175         
33176         
33177             
33178         // column width is fixed at the top - however if container width get's smaller we should
33179         // reduce it...
33180         
33181         // this bit calcs how man columns..
33182             
33183         var columnWidth = this.columnWidth += this.gutter;
33184       
33185         // calculate columns
33186         var containerWidth = this.containerWidth + this.gutter;
33187         
33188         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33189         // fix rounding errors, typically with gutters
33190         var excess = columnWidth - containerWidth % columnWidth;
33191         
33192         
33193         // if overshoot is less than a pixel, round up, otherwise floor it
33194         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33195         cols = Math[ mathMethod ]( cols );
33196         this.cols = Math.max( cols, 1 );
33197         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33198         
33199          // padding positioning..
33200         var totalColWidth = this.cols * this.columnWidth;
33201         var padavail = this.containerWidth - totalColWidth;
33202         // so for 2 columns - we need 3 'pads'
33203         
33204         var padNeeded = (1+this.cols) * this.padWidth;
33205         
33206         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33207         
33208         this.columnWidth += padExtra
33209         //this.padWidth = Math.floor(padavail /  ( this.cols));
33210         
33211         // adjust colum width so that padding is fixed??
33212         
33213         // we have 3 columns ... total = width * 3
33214         // we have X left over... that should be used by 
33215         
33216         //if (this.expandC) {
33217             
33218         //}
33219         
33220         
33221         
33222     },
33223     
33224     getContainerWidth : function()
33225     {
33226        /* // container is parent if fit width
33227         var container = this.isFitWidth ? this.element.parentNode : this.element;
33228         // check that this.size and size are there
33229         // IE8 triggers resize on body size change, so they might not be
33230         
33231         var size = getSize( container );  //FIXME
33232         this.containerWidth = size && size.innerWidth; //FIXME
33233         */
33234          
33235         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33236         
33237     },
33238     
33239     _getItemLayoutPosition : function( item )  // what is item?
33240     {
33241         // we resize the item to our columnWidth..
33242       
33243         item.setWidth(this.columnWidth);
33244         item.autoBoxAdjust  = false;
33245         
33246         var sz = item.getSize();
33247  
33248         // how many columns does this brick span
33249         var remainder = this.containerWidth % this.columnWidth;
33250         
33251         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33252         // round if off by 1 pixel, otherwise use ceil
33253         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33254         colSpan = Math.min( colSpan, this.cols );
33255         
33256         // normally this should be '1' as we dont' currently allow multi width columns..
33257         
33258         var colGroup = this._getColGroup( colSpan );
33259         // get the minimum Y value from the columns
33260         var minimumY = Math.min.apply( Math, colGroup );
33261         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33262         
33263         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33264          
33265         // position the brick
33266         var position = {
33267             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33268             y: this.currentSize.y + minimumY + this.padHeight
33269         };
33270         
33271         Roo.log(position);
33272         // apply setHeight to necessary columns
33273         var setHeight = minimumY + sz.height + this.padHeight;
33274         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33275         
33276         var setSpan = this.cols + 1 - colGroup.length;
33277         for ( var i = 0; i < setSpan; i++ ) {
33278           this.colYs[ shortColIndex + i ] = setHeight ;
33279         }
33280       
33281         return position;
33282     },
33283     
33284     /**
33285      * @param {Number} colSpan - number of columns the element spans
33286      * @returns {Array} colGroup
33287      */
33288     _getColGroup : function( colSpan )
33289     {
33290         if ( colSpan < 2 ) {
33291           // if brick spans only one column, use all the column Ys
33292           return this.colYs;
33293         }
33294       
33295         var colGroup = [];
33296         // how many different places could this brick fit horizontally
33297         var groupCount = this.cols + 1 - colSpan;
33298         // for each group potential horizontal position
33299         for ( var i = 0; i < groupCount; i++ ) {
33300           // make an array of colY values for that one group
33301           var groupColYs = this.colYs.slice( i, i + colSpan );
33302           // and get the max value of the array
33303           colGroup[i] = Math.max.apply( Math, groupColYs );
33304         }
33305         return colGroup;
33306     },
33307     /*
33308     _manageStamp : function( stamp )
33309     {
33310         var stampSize =  stamp.getSize();
33311         var offset = stamp.getBox();
33312         // get the columns that this stamp affects
33313         var firstX = this.isOriginLeft ? offset.x : offset.right;
33314         var lastX = firstX + stampSize.width;
33315         var firstCol = Math.floor( firstX / this.columnWidth );
33316         firstCol = Math.max( 0, firstCol );
33317         
33318         var lastCol = Math.floor( lastX / this.columnWidth );
33319         // lastCol should not go over if multiple of columnWidth #425
33320         lastCol -= lastX % this.columnWidth ? 0 : 1;
33321         lastCol = Math.min( this.cols - 1, lastCol );
33322         
33323         // set colYs to bottom of the stamp
33324         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33325             stampSize.height;
33326             
33327         for ( var i = firstCol; i <= lastCol; i++ ) {
33328           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33329         }
33330     },
33331     */
33332     
33333     _getContainerSize : function()
33334     {
33335         this.maxY = Math.max.apply( Math, this.colYs );
33336         var size = {
33337             height: this.maxY
33338         };
33339       
33340         if ( this.isFitWidth ) {
33341             size.width = this._getContainerFitWidth();
33342         }
33343       
33344         return size;
33345     },
33346     
33347     _getContainerFitWidth : function()
33348     {
33349         var unusedCols = 0;
33350         // count unused columns
33351         var i = this.cols;
33352         while ( --i ) {
33353           if ( this.colYs[i] !== 0 ) {
33354             break;
33355           }
33356           unusedCols++;
33357         }
33358         // fit container to columns that have been used
33359         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33360     },
33361     
33362     needsResizeLayout : function()
33363     {
33364         var previousWidth = this.containerWidth;
33365         this.getContainerWidth();
33366         return previousWidth !== this.containerWidth;
33367     }
33368  
33369 });
33370
33371  
33372
33373  /*
33374  * - LGPL
33375  *
33376  * element
33377  * 
33378  */
33379
33380 /**
33381  * @class Roo.bootstrap.MasonryBrick
33382  * @extends Roo.bootstrap.Component
33383  * Bootstrap MasonryBrick class
33384  * 
33385  * @constructor
33386  * Create a new MasonryBrick
33387  * @param {Object} config The config object
33388  */
33389
33390 Roo.bootstrap.MasonryBrick = function(config){
33391     
33392     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33393     
33394     Roo.bootstrap.MasonryBrick.register(this);
33395     
33396     this.addEvents({
33397         // raw events
33398         /**
33399          * @event click
33400          * When a MasonryBrick is clcik
33401          * @param {Roo.bootstrap.MasonryBrick} this
33402          * @param {Roo.EventObject} e
33403          */
33404         "click" : true
33405     });
33406 };
33407
33408 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33409     
33410     /**
33411      * @cfg {String} title
33412      */   
33413     title : '',
33414     /**
33415      * @cfg {String} html
33416      */   
33417     html : '',
33418     /**
33419      * @cfg {String} bgimage
33420      */   
33421     bgimage : '',
33422     /**
33423      * @cfg {String} videourl
33424      */   
33425     videourl : '',
33426     /**
33427      * @cfg {String} cls
33428      */   
33429     cls : '',
33430     /**
33431      * @cfg {String} href
33432      */   
33433     href : '',
33434     /**
33435      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33436      */   
33437     size : 'xs',
33438     
33439     /**
33440      * @cfg {String} placetitle (center|bottom)
33441      */   
33442     placetitle : '',
33443     
33444     /**
33445      * @cfg {Boolean} isFitContainer defalut true
33446      */   
33447     isFitContainer : true, 
33448     
33449     /**
33450      * @cfg {Boolean} preventDefault defalut false
33451      */   
33452     preventDefault : false, 
33453     
33454     /**
33455      * @cfg {Boolean} inverse defalut false
33456      */   
33457     maskInverse : false, 
33458     
33459     getAutoCreate : function()
33460     {
33461         if(!this.isFitContainer){
33462             return this.getSplitAutoCreate();
33463         }
33464         
33465         var cls = 'masonry-brick masonry-brick-full';
33466         
33467         if(this.href.length){
33468             cls += ' masonry-brick-link';
33469         }
33470         
33471         if(this.bgimage.length){
33472             cls += ' masonry-brick-image';
33473         }
33474         
33475         if(this.maskInverse){
33476             cls += ' mask-inverse';
33477         }
33478         
33479         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33480             cls += ' enable-mask';
33481         }
33482         
33483         if(this.size){
33484             cls += ' masonry-' + this.size + '-brick';
33485         }
33486         
33487         if(this.placetitle.length){
33488             
33489             switch (this.placetitle) {
33490                 case 'center' :
33491                     cls += ' masonry-center-title';
33492                     break;
33493                 case 'bottom' :
33494                     cls += ' masonry-bottom-title';
33495                     break;
33496                 default:
33497                     break;
33498             }
33499             
33500         } else {
33501             if(!this.html.length && !this.bgimage.length){
33502                 cls += ' masonry-center-title';
33503             }
33504
33505             if(!this.html.length && this.bgimage.length){
33506                 cls += ' masonry-bottom-title';
33507             }
33508         }
33509         
33510         if(this.cls){
33511             cls += ' ' + this.cls;
33512         }
33513         
33514         var cfg = {
33515             tag: (this.href.length) ? 'a' : 'div',
33516             cls: cls,
33517             cn: [
33518                 {
33519                     tag: 'div',
33520                     cls: 'masonry-brick-mask'
33521                 },
33522                 {
33523                     tag: 'div',
33524                     cls: 'masonry-brick-paragraph',
33525                     cn: []
33526                 }
33527             ]
33528         };
33529         
33530         if(this.href.length){
33531             cfg.href = this.href;
33532         }
33533         
33534         var cn = cfg.cn[1].cn;
33535         
33536         if(this.title.length){
33537             cn.push({
33538                 tag: 'h4',
33539                 cls: 'masonry-brick-title',
33540                 html: this.title
33541             });
33542         }
33543         
33544         if(this.html.length){
33545             cn.push({
33546                 tag: 'p',
33547                 cls: 'masonry-brick-text',
33548                 html: this.html
33549             });
33550         }
33551         
33552         if (!this.title.length && !this.html.length) {
33553             cfg.cn[1].cls += ' hide';
33554         }
33555         
33556         if(this.bgimage.length){
33557             cfg.cn.push({
33558                 tag: 'img',
33559                 cls: 'masonry-brick-image-view',
33560                 src: this.bgimage
33561             });
33562         }
33563         
33564         if(this.videourl.length){
33565             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33566             // youtube support only?
33567             cfg.cn.push({
33568                 tag: 'iframe',
33569                 cls: 'masonry-brick-image-view',
33570                 src: vurl,
33571                 frameborder : 0,
33572                 allowfullscreen : true
33573             });
33574         }
33575         
33576         return cfg;
33577         
33578     },
33579     
33580     getSplitAutoCreate : function()
33581     {
33582         var cls = 'masonry-brick masonry-brick-split';
33583         
33584         if(this.href.length){
33585             cls += ' masonry-brick-link';
33586         }
33587         
33588         if(this.bgimage.length){
33589             cls += ' masonry-brick-image';
33590         }
33591         
33592         if(this.size){
33593             cls += ' masonry-' + this.size + '-brick';
33594         }
33595         
33596         switch (this.placetitle) {
33597             case 'center' :
33598                 cls += ' masonry-center-title';
33599                 break;
33600             case 'bottom' :
33601                 cls += ' masonry-bottom-title';
33602                 break;
33603             default:
33604                 if(!this.bgimage.length){
33605                     cls += ' masonry-center-title';
33606                 }
33607
33608                 if(this.bgimage.length){
33609                     cls += ' masonry-bottom-title';
33610                 }
33611                 break;
33612         }
33613         
33614         if(this.cls){
33615             cls += ' ' + this.cls;
33616         }
33617         
33618         var cfg = {
33619             tag: (this.href.length) ? 'a' : 'div',
33620             cls: cls,
33621             cn: [
33622                 {
33623                     tag: 'div',
33624                     cls: 'masonry-brick-split-head',
33625                     cn: [
33626                         {
33627                             tag: 'div',
33628                             cls: 'masonry-brick-paragraph',
33629                             cn: []
33630                         }
33631                     ]
33632                 },
33633                 {
33634                     tag: 'div',
33635                     cls: 'masonry-brick-split-body',
33636                     cn: []
33637                 }
33638             ]
33639         };
33640         
33641         if(this.href.length){
33642             cfg.href = this.href;
33643         }
33644         
33645         if(this.title.length){
33646             cfg.cn[0].cn[0].cn.push({
33647                 tag: 'h4',
33648                 cls: 'masonry-brick-title',
33649                 html: this.title
33650             });
33651         }
33652         
33653         if(this.html.length){
33654             cfg.cn[1].cn.push({
33655                 tag: 'p',
33656                 cls: 'masonry-brick-text',
33657                 html: this.html
33658             });
33659         }
33660
33661         if(this.bgimage.length){
33662             cfg.cn[0].cn.push({
33663                 tag: 'img',
33664                 cls: 'masonry-brick-image-view',
33665                 src: this.bgimage
33666             });
33667         }
33668         
33669         if(this.videourl.length){
33670             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33671             // youtube support only?
33672             cfg.cn[0].cn.cn.push({
33673                 tag: 'iframe',
33674                 cls: 'masonry-brick-image-view',
33675                 src: vurl,
33676                 frameborder : 0,
33677                 allowfullscreen : true
33678             });
33679         }
33680         
33681         return cfg;
33682     },
33683     
33684     initEvents: function() 
33685     {
33686         switch (this.size) {
33687             case 'xs' :
33688                 this.x = 1;
33689                 this.y = 1;
33690                 break;
33691             case 'sm' :
33692                 this.x = 2;
33693                 this.y = 2;
33694                 break;
33695             case 'md' :
33696             case 'md-left' :
33697             case 'md-right' :
33698                 this.x = 3;
33699                 this.y = 3;
33700                 break;
33701             case 'tall' :
33702                 this.x = 2;
33703                 this.y = 3;
33704                 break;
33705             case 'wide' :
33706                 this.x = 3;
33707                 this.y = 2;
33708                 break;
33709             case 'wide-thin' :
33710                 this.x = 3;
33711                 this.y = 1;
33712                 break;
33713                         
33714             default :
33715                 break;
33716         }
33717         
33718         if(Roo.isTouch){
33719             this.el.on('touchstart', this.onTouchStart, this);
33720             this.el.on('touchmove', this.onTouchMove, this);
33721             this.el.on('touchend', this.onTouchEnd, this);
33722             this.el.on('contextmenu', this.onContextMenu, this);
33723         } else {
33724             this.el.on('mouseenter'  ,this.enter, this);
33725             this.el.on('mouseleave', this.leave, this);
33726             this.el.on('click', this.onClick, this);
33727         }
33728         
33729         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33730             this.parent().bricks.push(this);   
33731         }
33732         
33733     },
33734     
33735     onClick: function(e, el)
33736     {
33737         var time = this.endTimer - this.startTimer;
33738         // Roo.log(e.preventDefault());
33739         if(Roo.isTouch){
33740             if(time > 1000){
33741                 e.preventDefault();
33742                 return;
33743             }
33744         }
33745         
33746         if(!this.preventDefault){
33747             return;
33748         }
33749         
33750         e.preventDefault();
33751         
33752         if (this.activeClass != '') {
33753             this.selectBrick();
33754         }
33755         
33756         this.fireEvent('click', this, e);
33757     },
33758     
33759     enter: function(e, el)
33760     {
33761         e.preventDefault();
33762         
33763         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33764             return;
33765         }
33766         
33767         if(this.bgimage.length && this.html.length){
33768             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33769         }
33770     },
33771     
33772     leave: function(e, el)
33773     {
33774         e.preventDefault();
33775         
33776         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33777             return;
33778         }
33779         
33780         if(this.bgimage.length && this.html.length){
33781             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33782         }
33783     },
33784     
33785     onTouchStart: function(e, el)
33786     {
33787 //        e.preventDefault();
33788         
33789         this.touchmoved = false;
33790         
33791         if(!this.isFitContainer){
33792             return;
33793         }
33794         
33795         if(!this.bgimage.length || !this.html.length){
33796             return;
33797         }
33798         
33799         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33800         
33801         this.timer = new Date().getTime();
33802         
33803     },
33804     
33805     onTouchMove: function(e, el)
33806     {
33807         this.touchmoved = true;
33808     },
33809     
33810     onContextMenu : function(e,el)
33811     {
33812         e.preventDefault();
33813         e.stopPropagation();
33814         return false;
33815     },
33816     
33817     onTouchEnd: function(e, el)
33818     {
33819 //        e.preventDefault();
33820         
33821         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33822         
33823             this.leave(e,el);
33824             
33825             return;
33826         }
33827         
33828         if(!this.bgimage.length || !this.html.length){
33829             
33830             if(this.href.length){
33831                 window.location.href = this.href;
33832             }
33833             
33834             return;
33835         }
33836         
33837         if(!this.isFitContainer){
33838             return;
33839         }
33840         
33841         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33842         
33843         window.location.href = this.href;
33844     },
33845     
33846     //selection on single brick only
33847     selectBrick : function() {
33848         
33849         if (!this.parentId) {
33850             return;
33851         }
33852         
33853         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33854         var index = m.selectedBrick.indexOf(this.id);
33855         
33856         if ( index > -1) {
33857             m.selectedBrick.splice(index,1);
33858             this.el.removeClass(this.activeClass);
33859             return;
33860         }
33861         
33862         for(var i = 0; i < m.selectedBrick.length; i++) {
33863             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33864             b.el.removeClass(b.activeClass);
33865         }
33866         
33867         m.selectedBrick = [];
33868         
33869         m.selectedBrick.push(this.id);
33870         this.el.addClass(this.activeClass);
33871         return;
33872     },
33873     
33874     isSelected : function(){
33875         return this.el.hasClass(this.activeClass);
33876         
33877     }
33878 });
33879
33880 Roo.apply(Roo.bootstrap.MasonryBrick, {
33881     
33882     //groups: {},
33883     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33884      /**
33885     * register a Masonry Brick
33886     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33887     */
33888     
33889     register : function(brick)
33890     {
33891         //this.groups[brick.id] = brick;
33892         this.groups.add(brick.id, brick);
33893     },
33894     /**
33895     * fetch a  masonry brick based on the masonry brick ID
33896     * @param {string} the masonry brick to add
33897     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33898     */
33899     
33900     get: function(brick_id) 
33901     {
33902         // if (typeof(this.groups[brick_id]) == 'undefined') {
33903         //     return false;
33904         // }
33905         // return this.groups[brick_id] ;
33906         
33907         if(this.groups.key(brick_id)) {
33908             return this.groups.key(brick_id);
33909         }
33910         
33911         return false;
33912     }
33913     
33914     
33915     
33916 });
33917
33918  /*
33919  * - LGPL
33920  *
33921  * element
33922  * 
33923  */
33924
33925 /**
33926  * @class Roo.bootstrap.Brick
33927  * @extends Roo.bootstrap.Component
33928  * Bootstrap Brick class
33929  * 
33930  * @constructor
33931  * Create a new Brick
33932  * @param {Object} config The config object
33933  */
33934
33935 Roo.bootstrap.Brick = function(config){
33936     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33937     
33938     this.addEvents({
33939         // raw events
33940         /**
33941          * @event click
33942          * When a Brick is click
33943          * @param {Roo.bootstrap.Brick} this
33944          * @param {Roo.EventObject} e
33945          */
33946         "click" : true
33947     });
33948 };
33949
33950 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33951     
33952     /**
33953      * @cfg {String} title
33954      */   
33955     title : '',
33956     /**
33957      * @cfg {String} html
33958      */   
33959     html : '',
33960     /**
33961      * @cfg {String} bgimage
33962      */   
33963     bgimage : '',
33964     /**
33965      * @cfg {String} cls
33966      */   
33967     cls : '',
33968     /**
33969      * @cfg {String} href
33970      */   
33971     href : '',
33972     /**
33973      * @cfg {String} video
33974      */   
33975     video : '',
33976     /**
33977      * @cfg {Boolean} square
33978      */   
33979     square : true,
33980     
33981     getAutoCreate : function()
33982     {
33983         var cls = 'roo-brick';
33984         
33985         if(this.href.length){
33986             cls += ' roo-brick-link';
33987         }
33988         
33989         if(this.bgimage.length){
33990             cls += ' roo-brick-image';
33991         }
33992         
33993         if(!this.html.length && !this.bgimage.length){
33994             cls += ' roo-brick-center-title';
33995         }
33996         
33997         if(!this.html.length && this.bgimage.length){
33998             cls += ' roo-brick-bottom-title';
33999         }
34000         
34001         if(this.cls){
34002             cls += ' ' + this.cls;
34003         }
34004         
34005         var cfg = {
34006             tag: (this.href.length) ? 'a' : 'div',
34007             cls: cls,
34008             cn: [
34009                 {
34010                     tag: 'div',
34011                     cls: 'roo-brick-paragraph',
34012                     cn: []
34013                 }
34014             ]
34015         };
34016         
34017         if(this.href.length){
34018             cfg.href = this.href;
34019         }
34020         
34021         var cn = cfg.cn[0].cn;
34022         
34023         if(this.title.length){
34024             cn.push({
34025                 tag: 'h4',
34026                 cls: 'roo-brick-title',
34027                 html: this.title
34028             });
34029         }
34030         
34031         if(this.html.length){
34032             cn.push({
34033                 tag: 'p',
34034                 cls: 'roo-brick-text',
34035                 html: this.html
34036             });
34037         } else {
34038             cn.cls += ' hide';
34039         }
34040         
34041         if(this.bgimage.length){
34042             cfg.cn.push({
34043                 tag: 'img',
34044                 cls: 'roo-brick-image-view',
34045                 src: this.bgimage
34046             });
34047         }
34048         
34049         return cfg;
34050     },
34051     
34052     initEvents: function() 
34053     {
34054         if(this.title.length || this.html.length){
34055             this.el.on('mouseenter'  ,this.enter, this);
34056             this.el.on('mouseleave', this.leave, this);
34057         }
34058         
34059         Roo.EventManager.onWindowResize(this.resize, this); 
34060         
34061         if(this.bgimage.length){
34062             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34063             this.imageEl.on('load', this.onImageLoad, this);
34064             return;
34065         }
34066         
34067         this.resize();
34068     },
34069     
34070     onImageLoad : function()
34071     {
34072         this.resize();
34073     },
34074     
34075     resize : function()
34076     {
34077         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34078         
34079         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34080         
34081         if(this.bgimage.length){
34082             var image = this.el.select('.roo-brick-image-view', true).first();
34083             
34084             image.setWidth(paragraph.getWidth());
34085             
34086             if(this.square){
34087                 image.setHeight(paragraph.getWidth());
34088             }
34089             
34090             this.el.setHeight(image.getHeight());
34091             paragraph.setHeight(image.getHeight());
34092             
34093         }
34094         
34095     },
34096     
34097     enter: function(e, el)
34098     {
34099         e.preventDefault();
34100         
34101         if(this.bgimage.length){
34102             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34103             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34104         }
34105     },
34106     
34107     leave: function(e, el)
34108     {
34109         e.preventDefault();
34110         
34111         if(this.bgimage.length){
34112             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34113             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34114         }
34115     }
34116     
34117 });
34118
34119  
34120
34121  /*
34122  * - LGPL
34123  *
34124  * Number field 
34125  */
34126
34127 /**
34128  * @class Roo.bootstrap.NumberField
34129  * @extends Roo.bootstrap.Input
34130  * Bootstrap NumberField class
34131  * 
34132  * 
34133  * 
34134  * 
34135  * @constructor
34136  * Create a new NumberField
34137  * @param {Object} config The config object
34138  */
34139
34140 Roo.bootstrap.NumberField = function(config){
34141     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34142 };
34143
34144 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34145     
34146     /**
34147      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34148      */
34149     allowDecimals : true,
34150     /**
34151      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34152      */
34153     decimalSeparator : ".",
34154     /**
34155      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34156      */
34157     decimalPrecision : 2,
34158     /**
34159      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34160      */
34161     allowNegative : true,
34162     
34163     /**
34164      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34165      */
34166     allowZero: true,
34167     /**
34168      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34169      */
34170     minValue : Number.NEGATIVE_INFINITY,
34171     /**
34172      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34173      */
34174     maxValue : Number.MAX_VALUE,
34175     /**
34176      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34177      */
34178     minText : "The minimum value for this field is {0}",
34179     /**
34180      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34181      */
34182     maxText : "The maximum value for this field is {0}",
34183     /**
34184      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34185      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34186      */
34187     nanText : "{0} is not a valid number",
34188     /**
34189      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34190      */
34191     thousandsDelimiter : false,
34192     /**
34193      * @cfg {String} valueAlign alignment of value
34194      */
34195     valueAlign : "left",
34196
34197     getAutoCreate : function()
34198     {
34199         var hiddenInput = {
34200             tag: 'input',
34201             type: 'hidden',
34202             id: Roo.id(),
34203             cls: 'hidden-number-input'
34204         };
34205         
34206         if (this.name) {
34207             hiddenInput.name = this.name;
34208         }
34209         
34210         this.name = '';
34211         
34212         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34213         
34214         this.name = hiddenInput.name;
34215         
34216         if(cfg.cn.length > 0) {
34217             cfg.cn.push(hiddenInput);
34218         }
34219         
34220         return cfg;
34221     },
34222
34223     // private
34224     initEvents : function()
34225     {   
34226         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34227         
34228         var allowed = "0123456789";
34229         
34230         if(this.allowDecimals){
34231             allowed += this.decimalSeparator;
34232         }
34233         
34234         if(this.allowNegative){
34235             allowed += "-";
34236         }
34237         
34238         if(this.thousandsDelimiter) {
34239             allowed += ",";
34240         }
34241         
34242         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34243         
34244         var keyPress = function(e){
34245             
34246             var k = e.getKey();
34247             
34248             var c = e.getCharCode();
34249             
34250             if(
34251                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34252                     allowed.indexOf(String.fromCharCode(c)) === -1
34253             ){
34254                 e.stopEvent();
34255                 return;
34256             }
34257             
34258             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34259                 return;
34260             }
34261             
34262             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34263                 e.stopEvent();
34264             }
34265         };
34266         
34267         this.el.on("keypress", keyPress, this);
34268     },
34269     
34270     validateValue : function(value)
34271     {
34272         
34273         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34274             return false;
34275         }
34276         
34277         var num = this.parseValue(value);
34278         
34279         if(isNaN(num)){
34280             this.markInvalid(String.format(this.nanText, value));
34281             return false;
34282         }
34283         
34284         if(num < this.minValue){
34285             this.markInvalid(String.format(this.minText, this.minValue));
34286             return false;
34287         }
34288         
34289         if(num > this.maxValue){
34290             this.markInvalid(String.format(this.maxText, this.maxValue));
34291             return false;
34292         }
34293         
34294         return true;
34295     },
34296
34297     getValue : function()
34298     {
34299         var v = this.hiddenEl().getValue();
34300         
34301         return this.fixPrecision(this.parseValue(v));
34302     },
34303
34304     parseValue : function(value)
34305     {
34306         if(this.thousandsDelimiter) {
34307             value += "";
34308             r = new RegExp(",", "g");
34309             value = value.replace(r, "");
34310         }
34311         
34312         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34313         return isNaN(value) ? '' : value;
34314     },
34315
34316     fixPrecision : function(value)
34317     {
34318         if(this.thousandsDelimiter) {
34319             value += "";
34320             r = new RegExp(",", "g");
34321             value = value.replace(r, "");
34322         }
34323         
34324         var nan = isNaN(value);
34325         
34326         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34327             return nan ? '' : value;
34328         }
34329         return parseFloat(value).toFixed(this.decimalPrecision);
34330     },
34331
34332     setValue : function(v)
34333     {
34334         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34335         
34336         this.value = v;
34337         
34338         if(this.rendered){
34339             
34340             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34341             
34342             this.inputEl().dom.value = (v == '') ? '' :
34343                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34344             
34345             if(!this.allowZero && v === '0') {
34346                 this.hiddenEl().dom.value = '';
34347                 this.inputEl().dom.value = '';
34348             }
34349             
34350             this.validate();
34351         }
34352     },
34353
34354     decimalPrecisionFcn : function(v)
34355     {
34356         return Math.floor(v);
34357     },
34358
34359     beforeBlur : function()
34360     {
34361         var v = this.parseValue(this.getRawValue());
34362         
34363         if(v || v === 0 || v === ''){
34364             this.setValue(v);
34365         }
34366     },
34367     
34368     hiddenEl : function()
34369     {
34370         return this.el.select('input.hidden-number-input',true).first();
34371     }
34372     
34373 });
34374
34375  
34376
34377 /*
34378 * Licence: LGPL
34379 */
34380
34381 /**
34382  * @class Roo.bootstrap.DocumentSlider
34383  * @extends Roo.bootstrap.Component
34384  * Bootstrap DocumentSlider class
34385  * 
34386  * @constructor
34387  * Create a new DocumentViewer
34388  * @param {Object} config The config object
34389  */
34390
34391 Roo.bootstrap.DocumentSlider = function(config){
34392     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34393     
34394     this.files = [];
34395     
34396     this.addEvents({
34397         /**
34398          * @event initial
34399          * Fire after initEvent
34400          * @param {Roo.bootstrap.DocumentSlider} this
34401          */
34402         "initial" : true,
34403         /**
34404          * @event update
34405          * Fire after update
34406          * @param {Roo.bootstrap.DocumentSlider} this
34407          */
34408         "update" : true,
34409         /**
34410          * @event click
34411          * Fire after click
34412          * @param {Roo.bootstrap.DocumentSlider} this
34413          */
34414         "click" : true
34415     });
34416 };
34417
34418 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34419     
34420     files : false,
34421     
34422     indicator : 0,
34423     
34424     getAutoCreate : function()
34425     {
34426         var cfg = {
34427             tag : 'div',
34428             cls : 'roo-document-slider',
34429             cn : [
34430                 {
34431                     tag : 'div',
34432                     cls : 'roo-document-slider-header',
34433                     cn : [
34434                         {
34435                             tag : 'div',
34436                             cls : 'roo-document-slider-header-title'
34437                         }
34438                     ]
34439                 },
34440                 {
34441                     tag : 'div',
34442                     cls : 'roo-document-slider-body',
34443                     cn : [
34444                         {
34445                             tag : 'div',
34446                             cls : 'roo-document-slider-prev',
34447                             cn : [
34448                                 {
34449                                     tag : 'i',
34450                                     cls : 'fa fa-chevron-left'
34451                                 }
34452                             ]
34453                         },
34454                         {
34455                             tag : 'div',
34456                             cls : 'roo-document-slider-thumb',
34457                             cn : [
34458                                 {
34459                                     tag : 'img',
34460                                     cls : 'roo-document-slider-image'
34461                                 }
34462                             ]
34463                         },
34464                         {
34465                             tag : 'div',
34466                             cls : 'roo-document-slider-next',
34467                             cn : [
34468                                 {
34469                                     tag : 'i',
34470                                     cls : 'fa fa-chevron-right'
34471                                 }
34472                             ]
34473                         }
34474                     ]
34475                 }
34476             ]
34477         };
34478         
34479         return cfg;
34480     },
34481     
34482     initEvents : function()
34483     {
34484         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34485         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34486         
34487         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34488         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34489         
34490         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34491         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34492         
34493         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34494         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34495         
34496         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34497         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34498         
34499         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34500         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34501         
34502         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34503         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34504         
34505         this.thumbEl.on('click', this.onClick, this);
34506         
34507         this.prevIndicator.on('click', this.prev, this);
34508         
34509         this.nextIndicator.on('click', this.next, this);
34510         
34511     },
34512     
34513     initial : function()
34514     {
34515         if(this.files.length){
34516             this.indicator = 1;
34517             this.update()
34518         }
34519         
34520         this.fireEvent('initial', this);
34521     },
34522     
34523     update : function()
34524     {
34525         this.imageEl.attr('src', this.files[this.indicator - 1]);
34526         
34527         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34528         
34529         this.prevIndicator.show();
34530         
34531         if(this.indicator == 1){
34532             this.prevIndicator.hide();
34533         }
34534         
34535         this.nextIndicator.show();
34536         
34537         if(this.indicator == this.files.length){
34538             this.nextIndicator.hide();
34539         }
34540         
34541         this.thumbEl.scrollTo('top');
34542         
34543         this.fireEvent('update', this);
34544     },
34545     
34546     onClick : function(e)
34547     {
34548         e.preventDefault();
34549         
34550         this.fireEvent('click', this);
34551     },
34552     
34553     prev : function(e)
34554     {
34555         e.preventDefault();
34556         
34557         this.indicator = Math.max(1, this.indicator - 1);
34558         
34559         this.update();
34560     },
34561     
34562     next : function(e)
34563     {
34564         e.preventDefault();
34565         
34566         this.indicator = Math.min(this.files.length, this.indicator + 1);
34567         
34568         this.update();
34569     }
34570 });
34571 /*
34572  * - LGPL
34573  *
34574  * RadioSet
34575  *
34576  *
34577  */
34578
34579 /**
34580  * @class Roo.bootstrap.RadioSet
34581  * @extends Roo.bootstrap.Input
34582  * Bootstrap RadioSet class
34583  * @cfg {String} indicatorpos (left|right) default left
34584  * @cfg {Boolean} inline (true|false) inline the element (default true)
34585  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34586  * @constructor
34587  * Create a new RadioSet
34588  * @param {Object} config The config object
34589  */
34590
34591 Roo.bootstrap.RadioSet = function(config){
34592     
34593     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34594     
34595     this.radioes = [];
34596     
34597     Roo.bootstrap.RadioSet.register(this);
34598     
34599     this.addEvents({
34600         /**
34601         * @event check
34602         * Fires when the element is checked or unchecked.
34603         * @param {Roo.bootstrap.RadioSet} this This radio
34604         * @param {Roo.bootstrap.Radio} item The checked item
34605         */
34606        check : true,
34607        /**
34608         * @event click
34609         * Fires when the element is click.
34610         * @param {Roo.bootstrap.RadioSet} this This radio set
34611         * @param {Roo.bootstrap.Radio} item The checked item
34612         * @param {Roo.EventObject} e The event object
34613         */
34614        click : true
34615     });
34616     
34617 };
34618
34619 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34620
34621     radioes : false,
34622     
34623     inline : true,
34624     
34625     weight : '',
34626     
34627     indicatorpos : 'left',
34628     
34629     getAutoCreate : function()
34630     {
34631         var label = {
34632             tag : 'label',
34633             cls : 'roo-radio-set-label',
34634             cn : [
34635                 {
34636                     tag : 'span',
34637                     html : this.fieldLabel
34638                 }
34639             ]
34640         };
34641         if (Roo.bootstrap.version == 3) {
34642             
34643             
34644             if(this.indicatorpos == 'left'){
34645                 label.cn.unshift({
34646                     tag : 'i',
34647                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34648                     tooltip : 'This field is required'
34649                 });
34650             } else {
34651                 label.cn.push({
34652                     tag : 'i',
34653                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34654                     tooltip : 'This field is required'
34655                 });
34656             }
34657         }
34658         var items = {
34659             tag : 'div',
34660             cls : 'roo-radio-set-items'
34661         };
34662         
34663         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34664         
34665         if (align === 'left' && this.fieldLabel.length) {
34666             
34667             items = {
34668                 cls : "roo-radio-set-right", 
34669                 cn: [
34670                     items
34671                 ]
34672             };
34673             
34674             if(this.labelWidth > 12){
34675                 label.style = "width: " + this.labelWidth + 'px';
34676             }
34677             
34678             if(this.labelWidth < 13 && this.labelmd == 0){
34679                 this.labelmd = this.labelWidth;
34680             }
34681             
34682             if(this.labellg > 0){
34683                 label.cls += ' col-lg-' + this.labellg;
34684                 items.cls += ' col-lg-' + (12 - this.labellg);
34685             }
34686             
34687             if(this.labelmd > 0){
34688                 label.cls += ' col-md-' + this.labelmd;
34689                 items.cls += ' col-md-' + (12 - this.labelmd);
34690             }
34691             
34692             if(this.labelsm > 0){
34693                 label.cls += ' col-sm-' + this.labelsm;
34694                 items.cls += ' col-sm-' + (12 - this.labelsm);
34695             }
34696             
34697             if(this.labelxs > 0){
34698                 label.cls += ' col-xs-' + this.labelxs;
34699                 items.cls += ' col-xs-' + (12 - this.labelxs);
34700             }
34701         }
34702         
34703         var cfg = {
34704             tag : 'div',
34705             cls : 'roo-radio-set',
34706             cn : [
34707                 {
34708                     tag : 'input',
34709                     cls : 'roo-radio-set-input',
34710                     type : 'hidden',
34711                     name : this.name,
34712                     value : this.value ? this.value :  ''
34713                 },
34714                 label,
34715                 items
34716             ]
34717         };
34718         
34719         if(this.weight.length){
34720             cfg.cls += ' roo-radio-' + this.weight;
34721         }
34722         
34723         if(this.inline) {
34724             cfg.cls += ' roo-radio-set-inline';
34725         }
34726         
34727         var settings=this;
34728         ['xs','sm','md','lg'].map(function(size){
34729             if (settings[size]) {
34730                 cfg.cls += ' col-' + size + '-' + settings[size];
34731             }
34732         });
34733         
34734         return cfg;
34735         
34736     },
34737
34738     initEvents : function()
34739     {
34740         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34741         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34742         
34743         if(!this.fieldLabel.length){
34744             this.labelEl.hide();
34745         }
34746         
34747         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34748         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34749         
34750         this.indicator = this.indicatorEl();
34751         
34752         if(this.indicator){
34753             this.indicator.addClass('invisible');
34754         }
34755         
34756         this.originalValue = this.getValue();
34757         
34758     },
34759     
34760     inputEl: function ()
34761     {
34762         return this.el.select('.roo-radio-set-input', true).first();
34763     },
34764     
34765     getChildContainer : function()
34766     {
34767         return this.itemsEl;
34768     },
34769     
34770     register : function(item)
34771     {
34772         this.radioes.push(item);
34773         
34774     },
34775     
34776     validate : function()
34777     {   
34778         if(this.getVisibilityEl().hasClass('hidden')){
34779             return true;
34780         }
34781         
34782         var valid = false;
34783         
34784         Roo.each(this.radioes, function(i){
34785             if(!i.checked){
34786                 return;
34787             }
34788             
34789             valid = true;
34790             return false;
34791         });
34792         
34793         if(this.allowBlank) {
34794             return true;
34795         }
34796         
34797         if(this.disabled || valid){
34798             this.markValid();
34799             return true;
34800         }
34801         
34802         this.markInvalid();
34803         return false;
34804         
34805     },
34806     
34807     markValid : function()
34808     {
34809         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34810             this.indicatorEl().removeClass('visible');
34811             this.indicatorEl().addClass('invisible');
34812         }
34813         
34814         
34815         if (Roo.bootstrap.version == 3) {
34816             this.el.removeClass([this.invalidClass, this.validClass]);
34817             this.el.addClass(this.validClass);
34818         } else {
34819             this.el.removeClass(['is-invalid','is-valid']);
34820             this.el.addClass(['is-valid']);
34821         }
34822         this.fireEvent('valid', this);
34823     },
34824     
34825     markInvalid : function(msg)
34826     {
34827         if(this.allowBlank || this.disabled){
34828             return;
34829         }
34830         
34831         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34832             this.indicatorEl().removeClass('invisible');
34833             this.indicatorEl().addClass('visible');
34834         }
34835         if (Roo.bootstrap.version == 3) {
34836             this.el.removeClass([this.invalidClass, this.validClass]);
34837             this.el.addClass(this.invalidClass);
34838         } else {
34839             this.el.removeClass(['is-invalid','is-valid']);
34840             this.el.addClass(['is-invalid']);
34841         }
34842         
34843         this.fireEvent('invalid', this, msg);
34844         
34845     },
34846     
34847     setValue : function(v, suppressEvent)
34848     {   
34849         if(this.value === v){
34850             return;
34851         }
34852         
34853         this.value = v;
34854         
34855         if(this.rendered){
34856             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34857         }
34858         
34859         Roo.each(this.radioes, function(i){
34860             i.checked = false;
34861             i.el.removeClass('checked');
34862         });
34863         
34864         Roo.each(this.radioes, function(i){
34865             
34866             if(i.value === v || i.value.toString() === v.toString()){
34867                 i.checked = true;
34868                 i.el.addClass('checked');
34869                 
34870                 if(suppressEvent !== true){
34871                     this.fireEvent('check', this, i);
34872                 }
34873                 
34874                 return false;
34875             }
34876             
34877         }, this);
34878         
34879         this.validate();
34880     },
34881     
34882     clearInvalid : function(){
34883         
34884         if(!this.el || this.preventMark){
34885             return;
34886         }
34887         
34888         this.el.removeClass([this.invalidClass]);
34889         
34890         this.fireEvent('valid', this);
34891     }
34892     
34893 });
34894
34895 Roo.apply(Roo.bootstrap.RadioSet, {
34896     
34897     groups: {},
34898     
34899     register : function(set)
34900     {
34901         this.groups[set.name] = set;
34902     },
34903     
34904     get: function(name) 
34905     {
34906         if (typeof(this.groups[name]) == 'undefined') {
34907             return false;
34908         }
34909         
34910         return this.groups[name] ;
34911     }
34912     
34913 });
34914 /*
34915  * Based on:
34916  * Ext JS Library 1.1.1
34917  * Copyright(c) 2006-2007, Ext JS, LLC.
34918  *
34919  * Originally Released Under LGPL - original licence link has changed is not relivant.
34920  *
34921  * Fork - LGPL
34922  * <script type="text/javascript">
34923  */
34924
34925
34926 /**
34927  * @class Roo.bootstrap.SplitBar
34928  * @extends Roo.util.Observable
34929  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34930  * <br><br>
34931  * Usage:
34932  * <pre><code>
34933 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34934                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34935 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34936 split.minSize = 100;
34937 split.maxSize = 600;
34938 split.animate = true;
34939 split.on('moved', splitterMoved);
34940 </code></pre>
34941  * @constructor
34942  * Create a new SplitBar
34943  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34944  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34945  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34946  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34947                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34948                         position of the SplitBar).
34949  */
34950 Roo.bootstrap.SplitBar = function(cfg){
34951     
34952     /** @private */
34953     
34954     //{
34955     //  dragElement : elm
34956     //  resizingElement: el,
34957         // optional..
34958     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34959     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34960         // existingProxy ???
34961     //}
34962     
34963     this.el = Roo.get(cfg.dragElement, true);
34964     this.el.dom.unselectable = "on";
34965     /** @private */
34966     this.resizingEl = Roo.get(cfg.resizingElement, true);
34967
34968     /**
34969      * @private
34970      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34971      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34972      * @type Number
34973      */
34974     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34975     
34976     /**
34977      * The minimum size of the resizing element. (Defaults to 0)
34978      * @type Number
34979      */
34980     this.minSize = 0;
34981     
34982     /**
34983      * The maximum size of the resizing element. (Defaults to 2000)
34984      * @type Number
34985      */
34986     this.maxSize = 2000;
34987     
34988     /**
34989      * Whether to animate the transition to the new size
34990      * @type Boolean
34991      */
34992     this.animate = false;
34993     
34994     /**
34995      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34996      * @type Boolean
34997      */
34998     this.useShim = false;
34999     
35000     /** @private */
35001     this.shim = null;
35002     
35003     if(!cfg.existingProxy){
35004         /** @private */
35005         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35006     }else{
35007         this.proxy = Roo.get(cfg.existingProxy).dom;
35008     }
35009     /** @private */
35010     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35011     
35012     /** @private */
35013     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35014     
35015     /** @private */
35016     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35017     
35018     /** @private */
35019     this.dragSpecs = {};
35020     
35021     /**
35022      * @private The adapter to use to positon and resize elements
35023      */
35024     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35025     this.adapter.init(this);
35026     
35027     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35028         /** @private */
35029         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35030         this.el.addClass("roo-splitbar-h");
35031     }else{
35032         /** @private */
35033         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35034         this.el.addClass("roo-splitbar-v");
35035     }
35036     
35037     this.addEvents({
35038         /**
35039          * @event resize
35040          * Fires when the splitter is moved (alias for {@link #event-moved})
35041          * @param {Roo.bootstrap.SplitBar} this
35042          * @param {Number} newSize the new width or height
35043          */
35044         "resize" : true,
35045         /**
35046          * @event moved
35047          * Fires when the splitter is moved
35048          * @param {Roo.bootstrap.SplitBar} this
35049          * @param {Number} newSize the new width or height
35050          */
35051         "moved" : true,
35052         /**
35053          * @event beforeresize
35054          * Fires before the splitter is dragged
35055          * @param {Roo.bootstrap.SplitBar} this
35056          */
35057         "beforeresize" : true,
35058
35059         "beforeapply" : true
35060     });
35061
35062     Roo.util.Observable.call(this);
35063 };
35064
35065 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35066     onStartProxyDrag : function(x, y){
35067         this.fireEvent("beforeresize", this);
35068         if(!this.overlay){
35069             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35070             o.unselectable();
35071             o.enableDisplayMode("block");
35072             // all splitbars share the same overlay
35073             Roo.bootstrap.SplitBar.prototype.overlay = o;
35074         }
35075         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35076         this.overlay.show();
35077         Roo.get(this.proxy).setDisplayed("block");
35078         var size = this.adapter.getElementSize(this);
35079         this.activeMinSize = this.getMinimumSize();;
35080         this.activeMaxSize = this.getMaximumSize();;
35081         var c1 = size - this.activeMinSize;
35082         var c2 = Math.max(this.activeMaxSize - size, 0);
35083         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35084             this.dd.resetConstraints();
35085             this.dd.setXConstraint(
35086                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35087                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35088             );
35089             this.dd.setYConstraint(0, 0);
35090         }else{
35091             this.dd.resetConstraints();
35092             this.dd.setXConstraint(0, 0);
35093             this.dd.setYConstraint(
35094                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35095                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35096             );
35097          }
35098         this.dragSpecs.startSize = size;
35099         this.dragSpecs.startPoint = [x, y];
35100         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35101     },
35102     
35103     /** 
35104      * @private Called after the drag operation by the DDProxy
35105      */
35106     onEndProxyDrag : function(e){
35107         Roo.get(this.proxy).setDisplayed(false);
35108         var endPoint = Roo.lib.Event.getXY(e);
35109         if(this.overlay){
35110             this.overlay.hide();
35111         }
35112         var newSize;
35113         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35114             newSize = this.dragSpecs.startSize + 
35115                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35116                     endPoint[0] - this.dragSpecs.startPoint[0] :
35117                     this.dragSpecs.startPoint[0] - endPoint[0]
35118                 );
35119         }else{
35120             newSize = this.dragSpecs.startSize + 
35121                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35122                     endPoint[1] - this.dragSpecs.startPoint[1] :
35123                     this.dragSpecs.startPoint[1] - endPoint[1]
35124                 );
35125         }
35126         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35127         if(newSize != this.dragSpecs.startSize){
35128             if(this.fireEvent('beforeapply', this, newSize) !== false){
35129                 this.adapter.setElementSize(this, newSize);
35130                 this.fireEvent("moved", this, newSize);
35131                 this.fireEvent("resize", this, newSize);
35132             }
35133         }
35134     },
35135     
35136     /**
35137      * Get the adapter this SplitBar uses
35138      * @return The adapter object
35139      */
35140     getAdapter : function(){
35141         return this.adapter;
35142     },
35143     
35144     /**
35145      * Set the adapter this SplitBar uses
35146      * @param {Object} adapter A SplitBar adapter object
35147      */
35148     setAdapter : function(adapter){
35149         this.adapter = adapter;
35150         this.adapter.init(this);
35151     },
35152     
35153     /**
35154      * Gets the minimum size for the resizing element
35155      * @return {Number} The minimum size
35156      */
35157     getMinimumSize : function(){
35158         return this.minSize;
35159     },
35160     
35161     /**
35162      * Sets the minimum size for the resizing element
35163      * @param {Number} minSize The minimum size
35164      */
35165     setMinimumSize : function(minSize){
35166         this.minSize = minSize;
35167     },
35168     
35169     /**
35170      * Gets the maximum size for the resizing element
35171      * @return {Number} The maximum size
35172      */
35173     getMaximumSize : function(){
35174         return this.maxSize;
35175     },
35176     
35177     /**
35178      * Sets the maximum size for the resizing element
35179      * @param {Number} maxSize The maximum size
35180      */
35181     setMaximumSize : function(maxSize){
35182         this.maxSize = maxSize;
35183     },
35184     
35185     /**
35186      * Sets the initialize size for the resizing element
35187      * @param {Number} size The initial size
35188      */
35189     setCurrentSize : function(size){
35190         var oldAnimate = this.animate;
35191         this.animate = false;
35192         this.adapter.setElementSize(this, size);
35193         this.animate = oldAnimate;
35194     },
35195     
35196     /**
35197      * Destroy this splitbar. 
35198      * @param {Boolean} removeEl True to remove the element
35199      */
35200     destroy : function(removeEl){
35201         if(this.shim){
35202             this.shim.remove();
35203         }
35204         this.dd.unreg();
35205         this.proxy.parentNode.removeChild(this.proxy);
35206         if(removeEl){
35207             this.el.remove();
35208         }
35209     }
35210 });
35211
35212 /**
35213  * @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.
35214  */
35215 Roo.bootstrap.SplitBar.createProxy = function(dir){
35216     var proxy = new Roo.Element(document.createElement("div"));
35217     proxy.unselectable();
35218     var cls = 'roo-splitbar-proxy';
35219     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35220     document.body.appendChild(proxy.dom);
35221     return proxy.dom;
35222 };
35223
35224 /** 
35225  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35226  * Default Adapter. It assumes the splitter and resizing element are not positioned
35227  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35228  */
35229 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35230 };
35231
35232 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35233     // do nothing for now
35234     init : function(s){
35235     
35236     },
35237     /**
35238      * Called before drag operations to get the current size of the resizing element. 
35239      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35240      */
35241      getElementSize : function(s){
35242         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35243             return s.resizingEl.getWidth();
35244         }else{
35245             return s.resizingEl.getHeight();
35246         }
35247     },
35248     
35249     /**
35250      * Called after drag operations to set the size of the resizing element.
35251      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35252      * @param {Number} newSize The new size to set
35253      * @param {Function} onComplete A function to be invoked when resizing is complete
35254      */
35255     setElementSize : function(s, newSize, onComplete){
35256         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35257             if(!s.animate){
35258                 s.resizingEl.setWidth(newSize);
35259                 if(onComplete){
35260                     onComplete(s, newSize);
35261                 }
35262             }else{
35263                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35264             }
35265         }else{
35266             
35267             if(!s.animate){
35268                 s.resizingEl.setHeight(newSize);
35269                 if(onComplete){
35270                     onComplete(s, newSize);
35271                 }
35272             }else{
35273                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35274             }
35275         }
35276     }
35277 };
35278
35279 /** 
35280  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35281  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35282  * Adapter that  moves the splitter element to align with the resized sizing element. 
35283  * Used with an absolute positioned SplitBar.
35284  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35285  * document.body, make sure you assign an id to the body element.
35286  */
35287 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35288     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35289     this.container = Roo.get(container);
35290 };
35291
35292 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35293     init : function(s){
35294         this.basic.init(s);
35295     },
35296     
35297     getElementSize : function(s){
35298         return this.basic.getElementSize(s);
35299     },
35300     
35301     setElementSize : function(s, newSize, onComplete){
35302         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35303     },
35304     
35305     moveSplitter : function(s){
35306         var yes = Roo.bootstrap.SplitBar;
35307         switch(s.placement){
35308             case yes.LEFT:
35309                 s.el.setX(s.resizingEl.getRight());
35310                 break;
35311             case yes.RIGHT:
35312                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35313                 break;
35314             case yes.TOP:
35315                 s.el.setY(s.resizingEl.getBottom());
35316                 break;
35317             case yes.BOTTOM:
35318                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35319                 break;
35320         }
35321     }
35322 };
35323
35324 /**
35325  * Orientation constant - Create a vertical SplitBar
35326  * @static
35327  * @type Number
35328  */
35329 Roo.bootstrap.SplitBar.VERTICAL = 1;
35330
35331 /**
35332  * Orientation constant - Create a horizontal SplitBar
35333  * @static
35334  * @type Number
35335  */
35336 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35337
35338 /**
35339  * Placement constant - The resizing element is to the left of the splitter element
35340  * @static
35341  * @type Number
35342  */
35343 Roo.bootstrap.SplitBar.LEFT = 1;
35344
35345 /**
35346  * Placement constant - The resizing element is to the right of the splitter element
35347  * @static
35348  * @type Number
35349  */
35350 Roo.bootstrap.SplitBar.RIGHT = 2;
35351
35352 /**
35353  * Placement constant - The resizing element is positioned above the splitter element
35354  * @static
35355  * @type Number
35356  */
35357 Roo.bootstrap.SplitBar.TOP = 3;
35358
35359 /**
35360  * Placement constant - The resizing element is positioned under splitter element
35361  * @static
35362  * @type Number
35363  */
35364 Roo.bootstrap.SplitBar.BOTTOM = 4;
35365 Roo.namespace("Roo.bootstrap.layout");/*
35366  * Based on:
35367  * Ext JS Library 1.1.1
35368  * Copyright(c) 2006-2007, Ext JS, LLC.
35369  *
35370  * Originally Released Under LGPL - original licence link has changed is not relivant.
35371  *
35372  * Fork - LGPL
35373  * <script type="text/javascript">
35374  */
35375
35376 /**
35377  * @class Roo.bootstrap.layout.Manager
35378  * @extends Roo.bootstrap.Component
35379  * Base class for layout managers.
35380  */
35381 Roo.bootstrap.layout.Manager = function(config)
35382 {
35383     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35384
35385
35386
35387
35388
35389     /** false to disable window resize monitoring @type Boolean */
35390     this.monitorWindowResize = true;
35391     this.regions = {};
35392     this.addEvents({
35393         /**
35394          * @event layout
35395          * Fires when a layout is performed.
35396          * @param {Roo.LayoutManager} this
35397          */
35398         "layout" : true,
35399         /**
35400          * @event regionresized
35401          * Fires when the user resizes a region.
35402          * @param {Roo.LayoutRegion} region The resized region
35403          * @param {Number} newSize The new size (width for east/west, height for north/south)
35404          */
35405         "regionresized" : true,
35406         /**
35407          * @event regioncollapsed
35408          * Fires when a region is collapsed.
35409          * @param {Roo.LayoutRegion} region The collapsed region
35410          */
35411         "regioncollapsed" : true,
35412         /**
35413          * @event regionexpanded
35414          * Fires when a region is expanded.
35415          * @param {Roo.LayoutRegion} region The expanded region
35416          */
35417         "regionexpanded" : true
35418     });
35419     this.updating = false;
35420
35421     if (config.el) {
35422         this.el = Roo.get(config.el);
35423         this.initEvents();
35424     }
35425
35426 };
35427
35428 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35429
35430
35431     regions : null,
35432
35433     monitorWindowResize : true,
35434
35435
35436     updating : false,
35437
35438
35439     onRender : function(ct, position)
35440     {
35441         if(!this.el){
35442             this.el = Roo.get(ct);
35443             this.initEvents();
35444         }
35445         //this.fireEvent('render',this);
35446     },
35447
35448
35449     initEvents: function()
35450     {
35451
35452
35453         // ie scrollbar fix
35454         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35455             document.body.scroll = "no";
35456         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35457             this.el.position('relative');
35458         }
35459         this.id = this.el.id;
35460         this.el.addClass("roo-layout-container");
35461         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35462         if(this.el.dom != document.body ) {
35463             this.el.on('resize', this.layout,this);
35464             this.el.on('show', this.layout,this);
35465         }
35466
35467     },
35468
35469     /**
35470      * Returns true if this layout is currently being updated
35471      * @return {Boolean}
35472      */
35473     isUpdating : function(){
35474         return this.updating;
35475     },
35476
35477     /**
35478      * Suspend the LayoutManager from doing auto-layouts while
35479      * making multiple add or remove calls
35480      */
35481     beginUpdate : function(){
35482         this.updating = true;
35483     },
35484
35485     /**
35486      * Restore auto-layouts and optionally disable the manager from performing a layout
35487      * @param {Boolean} noLayout true to disable a layout update
35488      */
35489     endUpdate : function(noLayout){
35490         this.updating = false;
35491         if(!noLayout){
35492             this.layout();
35493         }
35494     },
35495
35496     layout: function(){
35497         // abstract...
35498     },
35499
35500     onRegionResized : function(region, newSize){
35501         this.fireEvent("regionresized", region, newSize);
35502         this.layout();
35503     },
35504
35505     onRegionCollapsed : function(region){
35506         this.fireEvent("regioncollapsed", region);
35507     },
35508
35509     onRegionExpanded : function(region){
35510         this.fireEvent("regionexpanded", region);
35511     },
35512
35513     /**
35514      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35515      * performs box-model adjustments.
35516      * @return {Object} The size as an object {width: (the width), height: (the height)}
35517      */
35518     getViewSize : function()
35519     {
35520         var size;
35521         if(this.el.dom != document.body){
35522             size = this.el.getSize();
35523         }else{
35524             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35525         }
35526         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35527         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35528         return size;
35529     },
35530
35531     /**
35532      * Returns the Element this layout is bound to.
35533      * @return {Roo.Element}
35534      */
35535     getEl : function(){
35536         return this.el;
35537     },
35538
35539     /**
35540      * Returns the specified region.
35541      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35542      * @return {Roo.LayoutRegion}
35543      */
35544     getRegion : function(target){
35545         return this.regions[target.toLowerCase()];
35546     },
35547
35548     onWindowResize : function(){
35549         if(this.monitorWindowResize){
35550             this.layout();
35551         }
35552     }
35553 });
35554 /*
35555  * Based on:
35556  * Ext JS Library 1.1.1
35557  * Copyright(c) 2006-2007, Ext JS, LLC.
35558  *
35559  * Originally Released Under LGPL - original licence link has changed is not relivant.
35560  *
35561  * Fork - LGPL
35562  * <script type="text/javascript">
35563  */
35564 /**
35565  * @class Roo.bootstrap.layout.Border
35566  * @extends Roo.bootstrap.layout.Manager
35567  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35568  * please see: examples/bootstrap/nested.html<br><br>
35569  
35570 <b>The container the layout is rendered into can be either the body element or any other element.
35571 If it is not the body element, the container needs to either be an absolute positioned element,
35572 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35573 the container size if it is not the body element.</b>
35574
35575 * @constructor
35576 * Create a new Border
35577 * @param {Object} config Configuration options
35578  */
35579 Roo.bootstrap.layout.Border = function(config){
35580     config = config || {};
35581     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35582     
35583     
35584     
35585     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35586         if(config[region]){
35587             config[region].region = region;
35588             this.addRegion(config[region]);
35589         }
35590     },this);
35591     
35592 };
35593
35594 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35595
35596 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35597     
35598     parent : false, // this might point to a 'nest' or a ???
35599     
35600     /**
35601      * Creates and adds a new region if it doesn't already exist.
35602      * @param {String} target The target region key (north, south, east, west or center).
35603      * @param {Object} config The regions config object
35604      * @return {BorderLayoutRegion} The new region
35605      */
35606     addRegion : function(config)
35607     {
35608         if(!this.regions[config.region]){
35609             var r = this.factory(config);
35610             this.bindRegion(r);
35611         }
35612         return this.regions[config.region];
35613     },
35614
35615     // private (kinda)
35616     bindRegion : function(r){
35617         this.regions[r.config.region] = r;
35618         
35619         r.on("visibilitychange",    this.layout, this);
35620         r.on("paneladded",          this.layout, this);
35621         r.on("panelremoved",        this.layout, this);
35622         r.on("invalidated",         this.layout, this);
35623         r.on("resized",             this.onRegionResized, this);
35624         r.on("collapsed",           this.onRegionCollapsed, this);
35625         r.on("expanded",            this.onRegionExpanded, this);
35626     },
35627
35628     /**
35629      * Performs a layout update.
35630      */
35631     layout : function()
35632     {
35633         if(this.updating) {
35634             return;
35635         }
35636         
35637         // render all the rebions if they have not been done alreayd?
35638         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35639             if(this.regions[region] && !this.regions[region].bodyEl){
35640                 this.regions[region].onRender(this.el)
35641             }
35642         },this);
35643         
35644         var size = this.getViewSize();
35645         var w = size.width;
35646         var h = size.height;
35647         var centerW = w;
35648         var centerH = h;
35649         var centerY = 0;
35650         var centerX = 0;
35651         //var x = 0, y = 0;
35652
35653         var rs = this.regions;
35654         var north = rs["north"];
35655         var south = rs["south"]; 
35656         var west = rs["west"];
35657         var east = rs["east"];
35658         var center = rs["center"];
35659         //if(this.hideOnLayout){ // not supported anymore
35660             //c.el.setStyle("display", "none");
35661         //}
35662         if(north && north.isVisible()){
35663             var b = north.getBox();
35664             var m = north.getMargins();
35665             b.width = w - (m.left+m.right);
35666             b.x = m.left;
35667             b.y = m.top;
35668             centerY = b.height + b.y + m.bottom;
35669             centerH -= centerY;
35670             north.updateBox(this.safeBox(b));
35671         }
35672         if(south && south.isVisible()){
35673             var b = south.getBox();
35674             var m = south.getMargins();
35675             b.width = w - (m.left+m.right);
35676             b.x = m.left;
35677             var totalHeight = (b.height + m.top + m.bottom);
35678             b.y = h - totalHeight + m.top;
35679             centerH -= totalHeight;
35680             south.updateBox(this.safeBox(b));
35681         }
35682         if(west && west.isVisible()){
35683             var b = west.getBox();
35684             var m = west.getMargins();
35685             b.height = centerH - (m.top+m.bottom);
35686             b.x = m.left;
35687             b.y = centerY + m.top;
35688             var totalWidth = (b.width + m.left + m.right);
35689             centerX += totalWidth;
35690             centerW -= totalWidth;
35691             west.updateBox(this.safeBox(b));
35692         }
35693         if(east && east.isVisible()){
35694             var b = east.getBox();
35695             var m = east.getMargins();
35696             b.height = centerH - (m.top+m.bottom);
35697             var totalWidth = (b.width + m.left + m.right);
35698             b.x = w - totalWidth + m.left;
35699             b.y = centerY + m.top;
35700             centerW -= totalWidth;
35701             east.updateBox(this.safeBox(b));
35702         }
35703         if(center){
35704             var m = center.getMargins();
35705             var centerBox = {
35706                 x: centerX + m.left,
35707                 y: centerY + m.top,
35708                 width: centerW - (m.left+m.right),
35709                 height: centerH - (m.top+m.bottom)
35710             };
35711             //if(this.hideOnLayout){
35712                 //center.el.setStyle("display", "block");
35713             //}
35714             center.updateBox(this.safeBox(centerBox));
35715         }
35716         this.el.repaint();
35717         this.fireEvent("layout", this);
35718     },
35719
35720     // private
35721     safeBox : function(box){
35722         box.width = Math.max(0, box.width);
35723         box.height = Math.max(0, box.height);
35724         return box;
35725     },
35726
35727     /**
35728      * Adds a ContentPanel (or subclass) to this layout.
35729      * @param {String} target The target region key (north, south, east, west or center).
35730      * @param {Roo.ContentPanel} panel The panel to add
35731      * @return {Roo.ContentPanel} The added panel
35732      */
35733     add : function(target, panel){
35734          
35735         target = target.toLowerCase();
35736         return this.regions[target].add(panel);
35737     },
35738
35739     /**
35740      * Remove a ContentPanel (or subclass) to this layout.
35741      * @param {String} target The target region key (north, south, east, west or center).
35742      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35743      * @return {Roo.ContentPanel} The removed panel
35744      */
35745     remove : function(target, panel){
35746         target = target.toLowerCase();
35747         return this.regions[target].remove(panel);
35748     },
35749
35750     /**
35751      * Searches all regions for a panel with the specified id
35752      * @param {String} panelId
35753      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35754      */
35755     findPanel : function(panelId){
35756         var rs = this.regions;
35757         for(var target in rs){
35758             if(typeof rs[target] != "function"){
35759                 var p = rs[target].getPanel(panelId);
35760                 if(p){
35761                     return p;
35762                 }
35763             }
35764         }
35765         return null;
35766     },
35767
35768     /**
35769      * Searches all regions for a panel with the specified id and activates (shows) it.
35770      * @param {String/ContentPanel} panelId The panels id or the panel itself
35771      * @return {Roo.ContentPanel} The shown panel or null
35772      */
35773     showPanel : function(panelId) {
35774       var rs = this.regions;
35775       for(var target in rs){
35776          var r = rs[target];
35777          if(typeof r != "function"){
35778             if(r.hasPanel(panelId)){
35779                return r.showPanel(panelId);
35780             }
35781          }
35782       }
35783       return null;
35784    },
35785
35786    /**
35787      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35788      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35789      */
35790    /*
35791     restoreState : function(provider){
35792         if(!provider){
35793             provider = Roo.state.Manager;
35794         }
35795         var sm = new Roo.LayoutStateManager();
35796         sm.init(this, provider);
35797     },
35798 */
35799  
35800  
35801     /**
35802      * Adds a xtype elements to the layout.
35803      * <pre><code>
35804
35805 layout.addxtype({
35806        xtype : 'ContentPanel',
35807        region: 'west',
35808        items: [ .... ]
35809    }
35810 );
35811
35812 layout.addxtype({
35813         xtype : 'NestedLayoutPanel',
35814         region: 'west',
35815         layout: {
35816            center: { },
35817            west: { }   
35818         },
35819         items : [ ... list of content panels or nested layout panels.. ]
35820    }
35821 );
35822 </code></pre>
35823      * @param {Object} cfg Xtype definition of item to add.
35824      */
35825     addxtype : function(cfg)
35826     {
35827         // basically accepts a pannel...
35828         // can accept a layout region..!?!?
35829         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35830         
35831         
35832         // theory?  children can only be panels??
35833         
35834         //if (!cfg.xtype.match(/Panel$/)) {
35835         //    return false;
35836         //}
35837         var ret = false;
35838         
35839         if (typeof(cfg.region) == 'undefined') {
35840             Roo.log("Failed to add Panel, region was not set");
35841             Roo.log(cfg);
35842             return false;
35843         }
35844         var region = cfg.region;
35845         delete cfg.region;
35846         
35847           
35848         var xitems = [];
35849         if (cfg.items) {
35850             xitems = cfg.items;
35851             delete cfg.items;
35852         }
35853         var nb = false;
35854         
35855         if ( region == 'center') {
35856             Roo.log("Center: " + cfg.title);
35857         }
35858         
35859         
35860         switch(cfg.xtype) 
35861         {
35862             case 'Content':  // ContentPanel (el, cfg)
35863             case 'Scroll':  // ContentPanel (el, cfg)
35864             case 'View': 
35865                 cfg.autoCreate = cfg.autoCreate || true;
35866                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35867                 //} else {
35868                 //    var el = this.el.createChild();
35869                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35870                 //}
35871                 
35872                 this.add(region, ret);
35873                 break;
35874             
35875             /*
35876             case 'TreePanel': // our new panel!
35877                 cfg.el = this.el.createChild();
35878                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35879                 this.add(region, ret);
35880                 break;
35881             */
35882             
35883             case 'Nest': 
35884                 // create a new Layout (which is  a Border Layout...
35885                 
35886                 var clayout = cfg.layout;
35887                 clayout.el  = this.el.createChild();
35888                 clayout.items   = clayout.items  || [];
35889                 
35890                 delete cfg.layout;
35891                 
35892                 // replace this exitems with the clayout ones..
35893                 xitems = clayout.items;
35894                  
35895                 // force background off if it's in center...
35896                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35897                     cfg.background = false;
35898                 }
35899                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35900                 
35901                 
35902                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35903                 //console.log('adding nested layout panel '  + cfg.toSource());
35904                 this.add(region, ret);
35905                 nb = {}; /// find first...
35906                 break;
35907             
35908             case 'Grid':
35909                 
35910                 // needs grid and region
35911                 
35912                 //var el = this.getRegion(region).el.createChild();
35913                 /*
35914                  *var el = this.el.createChild();
35915                 // create the grid first...
35916                 cfg.grid.container = el;
35917                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35918                 */
35919                 
35920                 if (region == 'center' && this.active ) {
35921                     cfg.background = false;
35922                 }
35923                 
35924                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35925                 
35926                 this.add(region, ret);
35927                 /*
35928                 if (cfg.background) {
35929                     // render grid on panel activation (if panel background)
35930                     ret.on('activate', function(gp) {
35931                         if (!gp.grid.rendered) {
35932                     //        gp.grid.render(el);
35933                         }
35934                     });
35935                 } else {
35936                   //  cfg.grid.render(el);
35937                 }
35938                 */
35939                 break;
35940            
35941            
35942             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35943                 // it was the old xcomponent building that caused this before.
35944                 // espeically if border is the top element in the tree.
35945                 ret = this;
35946                 break; 
35947                 
35948                     
35949                 
35950                 
35951                 
35952             default:
35953                 /*
35954                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35955                     
35956                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35957                     this.add(region, ret);
35958                 } else {
35959                 */
35960                     Roo.log(cfg);
35961                     throw "Can not add '" + cfg.xtype + "' to Border";
35962                     return null;
35963              
35964                                 
35965              
35966         }
35967         this.beginUpdate();
35968         // add children..
35969         var region = '';
35970         var abn = {};
35971         Roo.each(xitems, function(i)  {
35972             region = nb && i.region ? i.region : false;
35973             
35974             var add = ret.addxtype(i);
35975            
35976             if (region) {
35977                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35978                 if (!i.background) {
35979                     abn[region] = nb[region] ;
35980                 }
35981             }
35982             
35983         });
35984         this.endUpdate();
35985
35986         // make the last non-background panel active..
35987         //if (nb) { Roo.log(abn); }
35988         if (nb) {
35989             
35990             for(var r in abn) {
35991                 region = this.getRegion(r);
35992                 if (region) {
35993                     // tried using nb[r], but it does not work..
35994                      
35995                     region.showPanel(abn[r]);
35996                    
35997                 }
35998             }
35999         }
36000         return ret;
36001         
36002     },
36003     
36004     
36005 // private
36006     factory : function(cfg)
36007     {
36008         
36009         var validRegions = Roo.bootstrap.layout.Border.regions;
36010
36011         var target = cfg.region;
36012         cfg.mgr = this;
36013         
36014         var r = Roo.bootstrap.layout;
36015         Roo.log(target);
36016         switch(target){
36017             case "north":
36018                 return new r.North(cfg);
36019             case "south":
36020                 return new r.South(cfg);
36021             case "east":
36022                 return new r.East(cfg);
36023             case "west":
36024                 return new r.West(cfg);
36025             case "center":
36026                 return new r.Center(cfg);
36027         }
36028         throw 'Layout region "'+target+'" not supported.';
36029     }
36030     
36031     
36032 });
36033  /*
36034  * Based on:
36035  * Ext JS Library 1.1.1
36036  * Copyright(c) 2006-2007, Ext JS, LLC.
36037  *
36038  * Originally Released Under LGPL - original licence link has changed is not relivant.
36039  *
36040  * Fork - LGPL
36041  * <script type="text/javascript">
36042  */
36043  
36044 /**
36045  * @class Roo.bootstrap.layout.Basic
36046  * @extends Roo.util.Observable
36047  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36048  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36049  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36050  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36051  * @cfg {string}   region  the region that it inhabits..
36052  * @cfg {bool}   skipConfig skip config?
36053  * 
36054
36055  */
36056 Roo.bootstrap.layout.Basic = function(config){
36057     
36058     this.mgr = config.mgr;
36059     
36060     this.position = config.region;
36061     
36062     var skipConfig = config.skipConfig;
36063     
36064     this.events = {
36065         /**
36066          * @scope Roo.BasicLayoutRegion
36067          */
36068         
36069         /**
36070          * @event beforeremove
36071          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36072          * @param {Roo.LayoutRegion} this
36073          * @param {Roo.ContentPanel} panel The panel
36074          * @param {Object} e The cancel event object
36075          */
36076         "beforeremove" : true,
36077         /**
36078          * @event invalidated
36079          * Fires when the layout for this region is changed.
36080          * @param {Roo.LayoutRegion} this
36081          */
36082         "invalidated" : true,
36083         /**
36084          * @event visibilitychange
36085          * Fires when this region is shown or hidden 
36086          * @param {Roo.LayoutRegion} this
36087          * @param {Boolean} visibility true or false
36088          */
36089         "visibilitychange" : true,
36090         /**
36091          * @event paneladded
36092          * Fires when a panel is added. 
36093          * @param {Roo.LayoutRegion} this
36094          * @param {Roo.ContentPanel} panel The panel
36095          */
36096         "paneladded" : true,
36097         /**
36098          * @event panelremoved
36099          * Fires when a panel is removed. 
36100          * @param {Roo.LayoutRegion} this
36101          * @param {Roo.ContentPanel} panel The panel
36102          */
36103         "panelremoved" : true,
36104         /**
36105          * @event beforecollapse
36106          * Fires when this region before collapse.
36107          * @param {Roo.LayoutRegion} this
36108          */
36109         "beforecollapse" : true,
36110         /**
36111          * @event collapsed
36112          * Fires when this region is collapsed.
36113          * @param {Roo.LayoutRegion} this
36114          */
36115         "collapsed" : true,
36116         /**
36117          * @event expanded
36118          * Fires when this region is expanded.
36119          * @param {Roo.LayoutRegion} this
36120          */
36121         "expanded" : true,
36122         /**
36123          * @event slideshow
36124          * Fires when this region is slid into view.
36125          * @param {Roo.LayoutRegion} this
36126          */
36127         "slideshow" : true,
36128         /**
36129          * @event slidehide
36130          * Fires when this region slides out of view. 
36131          * @param {Roo.LayoutRegion} this
36132          */
36133         "slidehide" : true,
36134         /**
36135          * @event panelactivated
36136          * Fires when a panel is activated. 
36137          * @param {Roo.LayoutRegion} this
36138          * @param {Roo.ContentPanel} panel The activated panel
36139          */
36140         "panelactivated" : true,
36141         /**
36142          * @event resized
36143          * Fires when the user resizes this region. 
36144          * @param {Roo.LayoutRegion} this
36145          * @param {Number} newSize The new size (width for east/west, height for north/south)
36146          */
36147         "resized" : true
36148     };
36149     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36150     this.panels = new Roo.util.MixedCollection();
36151     this.panels.getKey = this.getPanelId.createDelegate(this);
36152     this.box = null;
36153     this.activePanel = null;
36154     // ensure listeners are added...
36155     
36156     if (config.listeners || config.events) {
36157         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36158             listeners : config.listeners || {},
36159             events : config.events || {}
36160         });
36161     }
36162     
36163     if(skipConfig !== true){
36164         this.applyConfig(config);
36165     }
36166 };
36167
36168 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36169 {
36170     getPanelId : function(p){
36171         return p.getId();
36172     },
36173     
36174     applyConfig : function(config){
36175         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36176         this.config = config;
36177         
36178     },
36179     
36180     /**
36181      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36182      * the width, for horizontal (north, south) the height.
36183      * @param {Number} newSize The new width or height
36184      */
36185     resizeTo : function(newSize){
36186         var el = this.el ? this.el :
36187                  (this.activePanel ? this.activePanel.getEl() : null);
36188         if(el){
36189             switch(this.position){
36190                 case "east":
36191                 case "west":
36192                     el.setWidth(newSize);
36193                     this.fireEvent("resized", this, newSize);
36194                 break;
36195                 case "north":
36196                 case "south":
36197                     el.setHeight(newSize);
36198                     this.fireEvent("resized", this, newSize);
36199                 break;                
36200             }
36201         }
36202     },
36203     
36204     getBox : function(){
36205         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36206     },
36207     
36208     getMargins : function(){
36209         return this.margins;
36210     },
36211     
36212     updateBox : function(box){
36213         this.box = box;
36214         var el = this.activePanel.getEl();
36215         el.dom.style.left = box.x + "px";
36216         el.dom.style.top = box.y + "px";
36217         this.activePanel.setSize(box.width, box.height);
36218     },
36219     
36220     /**
36221      * Returns the container element for this region.
36222      * @return {Roo.Element}
36223      */
36224     getEl : function(){
36225         return this.activePanel;
36226     },
36227     
36228     /**
36229      * Returns true if this region is currently visible.
36230      * @return {Boolean}
36231      */
36232     isVisible : function(){
36233         return this.activePanel ? true : false;
36234     },
36235     
36236     setActivePanel : function(panel){
36237         panel = this.getPanel(panel);
36238         if(this.activePanel && this.activePanel != panel){
36239             this.activePanel.setActiveState(false);
36240             this.activePanel.getEl().setLeftTop(-10000,-10000);
36241         }
36242         this.activePanel = panel;
36243         panel.setActiveState(true);
36244         if(this.box){
36245             panel.setSize(this.box.width, this.box.height);
36246         }
36247         this.fireEvent("panelactivated", this, panel);
36248         this.fireEvent("invalidated");
36249     },
36250     
36251     /**
36252      * Show the specified panel.
36253      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36254      * @return {Roo.ContentPanel} The shown panel or null
36255      */
36256     showPanel : function(panel){
36257         panel = this.getPanel(panel);
36258         if(panel){
36259             this.setActivePanel(panel);
36260         }
36261         return panel;
36262     },
36263     
36264     /**
36265      * Get the active panel for this region.
36266      * @return {Roo.ContentPanel} The active panel or null
36267      */
36268     getActivePanel : function(){
36269         return this.activePanel;
36270     },
36271     
36272     /**
36273      * Add the passed ContentPanel(s)
36274      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36275      * @return {Roo.ContentPanel} The panel added (if only one was added)
36276      */
36277     add : function(panel){
36278         if(arguments.length > 1){
36279             for(var i = 0, len = arguments.length; i < len; i++) {
36280                 this.add(arguments[i]);
36281             }
36282             return null;
36283         }
36284         if(this.hasPanel(panel)){
36285             this.showPanel(panel);
36286             return panel;
36287         }
36288         var el = panel.getEl();
36289         if(el.dom.parentNode != this.mgr.el.dom){
36290             this.mgr.el.dom.appendChild(el.dom);
36291         }
36292         if(panel.setRegion){
36293             panel.setRegion(this);
36294         }
36295         this.panels.add(panel);
36296         el.setStyle("position", "absolute");
36297         if(!panel.background){
36298             this.setActivePanel(panel);
36299             if(this.config.initialSize && this.panels.getCount()==1){
36300                 this.resizeTo(this.config.initialSize);
36301             }
36302         }
36303         this.fireEvent("paneladded", this, panel);
36304         return panel;
36305     },
36306     
36307     /**
36308      * Returns true if the panel is in this region.
36309      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36310      * @return {Boolean}
36311      */
36312     hasPanel : function(panel){
36313         if(typeof panel == "object"){ // must be panel obj
36314             panel = panel.getId();
36315         }
36316         return this.getPanel(panel) ? true : false;
36317     },
36318     
36319     /**
36320      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36321      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36322      * @param {Boolean} preservePanel Overrides the config preservePanel option
36323      * @return {Roo.ContentPanel} The panel that was removed
36324      */
36325     remove : function(panel, preservePanel){
36326         panel = this.getPanel(panel);
36327         if(!panel){
36328             return null;
36329         }
36330         var e = {};
36331         this.fireEvent("beforeremove", this, panel, e);
36332         if(e.cancel === true){
36333             return null;
36334         }
36335         var panelId = panel.getId();
36336         this.panels.removeKey(panelId);
36337         return panel;
36338     },
36339     
36340     /**
36341      * Returns the panel specified or null if it's not in this region.
36342      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36343      * @return {Roo.ContentPanel}
36344      */
36345     getPanel : function(id){
36346         if(typeof id == "object"){ // must be panel obj
36347             return id;
36348         }
36349         return this.panels.get(id);
36350     },
36351     
36352     /**
36353      * Returns this regions position (north/south/east/west/center).
36354      * @return {String} 
36355      */
36356     getPosition: function(){
36357         return this.position;    
36358     }
36359 });/*
36360  * Based on:
36361  * Ext JS Library 1.1.1
36362  * Copyright(c) 2006-2007, Ext JS, LLC.
36363  *
36364  * Originally Released Under LGPL - original licence link has changed is not relivant.
36365  *
36366  * Fork - LGPL
36367  * <script type="text/javascript">
36368  */
36369  
36370 /**
36371  * @class Roo.bootstrap.layout.Region
36372  * @extends Roo.bootstrap.layout.Basic
36373  * This class represents a region in a layout manager.
36374  
36375  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36376  * @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})
36377  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36378  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36379  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36380  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36381  * @cfg {String}    title           The title for the region (overrides panel titles)
36382  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36383  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36384  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36385  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36386  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36387  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36388  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36389  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36390  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36391  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36392
36393  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36394  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36395  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36396  * @cfg {Number}    width           For East/West panels
36397  * @cfg {Number}    height          For North/South panels
36398  * @cfg {Boolean}   split           To show the splitter
36399  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36400  * 
36401  * @cfg {string}   cls             Extra CSS classes to add to region
36402  * 
36403  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36404  * @cfg {string}   region  the region that it inhabits..
36405  *
36406
36407  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36408  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36409
36410  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36411  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36412  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36413  */
36414 Roo.bootstrap.layout.Region = function(config)
36415 {
36416     this.applyConfig(config);
36417
36418     var mgr = config.mgr;
36419     var pos = config.region;
36420     config.skipConfig = true;
36421     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36422     
36423     if (mgr.el) {
36424         this.onRender(mgr.el);   
36425     }
36426      
36427     this.visible = true;
36428     this.collapsed = false;
36429     this.unrendered_panels = [];
36430 };
36431
36432 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36433
36434     position: '', // set by wrapper (eg. north/south etc..)
36435     unrendered_panels : null,  // unrendered panels.
36436     
36437     tabPosition : false,
36438     
36439     mgr: false, // points to 'Border'
36440     
36441     
36442     createBody : function(){
36443         /** This region's body element 
36444         * @type Roo.Element */
36445         this.bodyEl = this.el.createChild({
36446                 tag: "div",
36447                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36448         });
36449     },
36450
36451     onRender: function(ctr, pos)
36452     {
36453         var dh = Roo.DomHelper;
36454         /** This region's container element 
36455         * @type Roo.Element */
36456         this.el = dh.append(ctr.dom, {
36457                 tag: "div",
36458                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36459             }, true);
36460         /** This region's title element 
36461         * @type Roo.Element */
36462     
36463         this.titleEl = dh.append(this.el.dom,  {
36464                 tag: "div",
36465                 unselectable: "on",
36466                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36467                 children:[
36468                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36469                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36470                 ]
36471             }, true);
36472         
36473         this.titleEl.enableDisplayMode();
36474         /** This region's title text element 
36475         * @type HTMLElement */
36476         this.titleTextEl = this.titleEl.dom.firstChild;
36477         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36478         /*
36479         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36480         this.closeBtn.enableDisplayMode();
36481         this.closeBtn.on("click", this.closeClicked, this);
36482         this.closeBtn.hide();
36483     */
36484         this.createBody(this.config);
36485         if(this.config.hideWhenEmpty){
36486             this.hide();
36487             this.on("paneladded", this.validateVisibility, this);
36488             this.on("panelremoved", this.validateVisibility, this);
36489         }
36490         if(this.autoScroll){
36491             this.bodyEl.setStyle("overflow", "auto");
36492         }else{
36493             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36494         }
36495         //if(c.titlebar !== false){
36496             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36497                 this.titleEl.hide();
36498             }else{
36499                 this.titleEl.show();
36500                 if(this.config.title){
36501                     this.titleTextEl.innerHTML = this.config.title;
36502                 }
36503             }
36504         //}
36505         if(this.config.collapsed){
36506             this.collapse(true);
36507         }
36508         if(this.config.hidden){
36509             this.hide();
36510         }
36511         
36512         if (this.unrendered_panels && this.unrendered_panels.length) {
36513             for (var i =0;i< this.unrendered_panels.length; i++) {
36514                 this.add(this.unrendered_panels[i]);
36515             }
36516             this.unrendered_panels = null;
36517             
36518         }
36519         
36520     },
36521     
36522     applyConfig : function(c)
36523     {
36524         /*
36525          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36526             var dh = Roo.DomHelper;
36527             if(c.titlebar !== false){
36528                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36529                 this.collapseBtn.on("click", this.collapse, this);
36530                 this.collapseBtn.enableDisplayMode();
36531                 /*
36532                 if(c.showPin === true || this.showPin){
36533                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36534                     this.stickBtn.enableDisplayMode();
36535                     this.stickBtn.on("click", this.expand, this);
36536                     this.stickBtn.hide();
36537                 }
36538                 
36539             }
36540             */
36541             /** This region's collapsed element
36542             * @type Roo.Element */
36543             /*
36544              *
36545             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36546                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36547             ]}, true);
36548             
36549             if(c.floatable !== false){
36550                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36551                this.collapsedEl.on("click", this.collapseClick, this);
36552             }
36553
36554             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36555                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36556                    id: "message", unselectable: "on", style:{"float":"left"}});
36557                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36558              }
36559             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36560             this.expandBtn.on("click", this.expand, this);
36561             
36562         }
36563         
36564         if(this.collapseBtn){
36565             this.collapseBtn.setVisible(c.collapsible == true);
36566         }
36567         
36568         this.cmargins = c.cmargins || this.cmargins ||
36569                          (this.position == "west" || this.position == "east" ?
36570                              {top: 0, left: 2, right:2, bottom: 0} :
36571                              {top: 2, left: 0, right:0, bottom: 2});
36572         */
36573         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36574         
36575         
36576         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36577         
36578         this.autoScroll = c.autoScroll || false;
36579         
36580         
36581        
36582         
36583         this.duration = c.duration || .30;
36584         this.slideDuration = c.slideDuration || .45;
36585         this.config = c;
36586        
36587     },
36588     /**
36589      * Returns true if this region is currently visible.
36590      * @return {Boolean}
36591      */
36592     isVisible : function(){
36593         return this.visible;
36594     },
36595
36596     /**
36597      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36598      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36599      */
36600     //setCollapsedTitle : function(title){
36601     //    title = title || "&#160;";
36602      //   if(this.collapsedTitleTextEl){
36603       //      this.collapsedTitleTextEl.innerHTML = title;
36604        // }
36605     //},
36606
36607     getBox : function(){
36608         var b;
36609       //  if(!this.collapsed){
36610             b = this.el.getBox(false, true);
36611        // }else{
36612           //  b = this.collapsedEl.getBox(false, true);
36613         //}
36614         return b;
36615     },
36616
36617     getMargins : function(){
36618         return this.margins;
36619         //return this.collapsed ? this.cmargins : this.margins;
36620     },
36621 /*
36622     highlight : function(){
36623         this.el.addClass("x-layout-panel-dragover");
36624     },
36625
36626     unhighlight : function(){
36627         this.el.removeClass("x-layout-panel-dragover");
36628     },
36629 */
36630     updateBox : function(box)
36631     {
36632         if (!this.bodyEl) {
36633             return; // not rendered yet..
36634         }
36635         
36636         this.box = box;
36637         if(!this.collapsed){
36638             this.el.dom.style.left = box.x + "px";
36639             this.el.dom.style.top = box.y + "px";
36640             this.updateBody(box.width, box.height);
36641         }else{
36642             this.collapsedEl.dom.style.left = box.x + "px";
36643             this.collapsedEl.dom.style.top = box.y + "px";
36644             this.collapsedEl.setSize(box.width, box.height);
36645         }
36646         if(this.tabs){
36647             this.tabs.autoSizeTabs();
36648         }
36649     },
36650
36651     updateBody : function(w, h)
36652     {
36653         if(w !== null){
36654             this.el.setWidth(w);
36655             w -= this.el.getBorderWidth("rl");
36656             if(this.config.adjustments){
36657                 w += this.config.adjustments[0];
36658             }
36659         }
36660         if(h !== null && h > 0){
36661             this.el.setHeight(h);
36662             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36663             h -= this.el.getBorderWidth("tb");
36664             if(this.config.adjustments){
36665                 h += this.config.adjustments[1];
36666             }
36667             this.bodyEl.setHeight(h);
36668             if(this.tabs){
36669                 h = this.tabs.syncHeight(h);
36670             }
36671         }
36672         if(this.panelSize){
36673             w = w !== null ? w : this.panelSize.width;
36674             h = h !== null ? h : this.panelSize.height;
36675         }
36676         if(this.activePanel){
36677             var el = this.activePanel.getEl();
36678             w = w !== null ? w : el.getWidth();
36679             h = h !== null ? h : el.getHeight();
36680             this.panelSize = {width: w, height: h};
36681             this.activePanel.setSize(w, h);
36682         }
36683         if(Roo.isIE && this.tabs){
36684             this.tabs.el.repaint();
36685         }
36686     },
36687
36688     /**
36689      * Returns the container element for this region.
36690      * @return {Roo.Element}
36691      */
36692     getEl : function(){
36693         return this.el;
36694     },
36695
36696     /**
36697      * Hides this region.
36698      */
36699     hide : function(){
36700         //if(!this.collapsed){
36701             this.el.dom.style.left = "-2000px";
36702             this.el.hide();
36703         //}else{
36704          //   this.collapsedEl.dom.style.left = "-2000px";
36705          //   this.collapsedEl.hide();
36706        // }
36707         this.visible = false;
36708         this.fireEvent("visibilitychange", this, false);
36709     },
36710
36711     /**
36712      * Shows this region if it was previously hidden.
36713      */
36714     show : function(){
36715         //if(!this.collapsed){
36716             this.el.show();
36717         //}else{
36718         //    this.collapsedEl.show();
36719        // }
36720         this.visible = true;
36721         this.fireEvent("visibilitychange", this, true);
36722     },
36723 /*
36724     closeClicked : function(){
36725         if(this.activePanel){
36726             this.remove(this.activePanel);
36727         }
36728     },
36729
36730     collapseClick : function(e){
36731         if(this.isSlid){
36732            e.stopPropagation();
36733            this.slideIn();
36734         }else{
36735            e.stopPropagation();
36736            this.slideOut();
36737         }
36738     },
36739 */
36740     /**
36741      * Collapses this region.
36742      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36743      */
36744     /*
36745     collapse : function(skipAnim, skipCheck = false){
36746         if(this.collapsed) {
36747             return;
36748         }
36749         
36750         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36751             
36752             this.collapsed = true;
36753             if(this.split){
36754                 this.split.el.hide();
36755             }
36756             if(this.config.animate && skipAnim !== true){
36757                 this.fireEvent("invalidated", this);
36758                 this.animateCollapse();
36759             }else{
36760                 this.el.setLocation(-20000,-20000);
36761                 this.el.hide();
36762                 this.collapsedEl.show();
36763                 this.fireEvent("collapsed", this);
36764                 this.fireEvent("invalidated", this);
36765             }
36766         }
36767         
36768     },
36769 */
36770     animateCollapse : function(){
36771         // overridden
36772     },
36773
36774     /**
36775      * Expands this region if it was previously collapsed.
36776      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36777      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36778      */
36779     /*
36780     expand : function(e, skipAnim){
36781         if(e) {
36782             e.stopPropagation();
36783         }
36784         if(!this.collapsed || this.el.hasActiveFx()) {
36785             return;
36786         }
36787         if(this.isSlid){
36788             this.afterSlideIn();
36789             skipAnim = true;
36790         }
36791         this.collapsed = false;
36792         if(this.config.animate && skipAnim !== true){
36793             this.animateExpand();
36794         }else{
36795             this.el.show();
36796             if(this.split){
36797                 this.split.el.show();
36798             }
36799             this.collapsedEl.setLocation(-2000,-2000);
36800             this.collapsedEl.hide();
36801             this.fireEvent("invalidated", this);
36802             this.fireEvent("expanded", this);
36803         }
36804     },
36805 */
36806     animateExpand : function(){
36807         // overridden
36808     },
36809
36810     initTabs : function()
36811     {
36812         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36813         
36814         var ts = new Roo.bootstrap.panel.Tabs({
36815             el: this.bodyEl.dom,
36816             region : this,
36817             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36818             disableTooltips: this.config.disableTabTips,
36819             toolbar : this.config.toolbar
36820         });
36821         
36822         if(this.config.hideTabs){
36823             ts.stripWrap.setDisplayed(false);
36824         }
36825         this.tabs = ts;
36826         ts.resizeTabs = this.config.resizeTabs === true;
36827         ts.minTabWidth = this.config.minTabWidth || 40;
36828         ts.maxTabWidth = this.config.maxTabWidth || 250;
36829         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36830         ts.monitorResize = false;
36831         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36832         ts.bodyEl.addClass('roo-layout-tabs-body');
36833         this.panels.each(this.initPanelAsTab, this);
36834     },
36835
36836     initPanelAsTab : function(panel){
36837         var ti = this.tabs.addTab(
36838             panel.getEl().id,
36839             panel.getTitle(),
36840             null,
36841             this.config.closeOnTab && panel.isClosable(),
36842             panel.tpl
36843         );
36844         if(panel.tabTip !== undefined){
36845             ti.setTooltip(panel.tabTip);
36846         }
36847         ti.on("activate", function(){
36848               this.setActivePanel(panel);
36849         }, this);
36850         
36851         if(this.config.closeOnTab){
36852             ti.on("beforeclose", function(t, e){
36853                 e.cancel = true;
36854                 this.remove(panel);
36855             }, this);
36856         }
36857         
36858         panel.tabItem = ti;
36859         
36860         return ti;
36861     },
36862
36863     updatePanelTitle : function(panel, title)
36864     {
36865         if(this.activePanel == panel){
36866             this.updateTitle(title);
36867         }
36868         if(this.tabs){
36869             var ti = this.tabs.getTab(panel.getEl().id);
36870             ti.setText(title);
36871             if(panel.tabTip !== undefined){
36872                 ti.setTooltip(panel.tabTip);
36873             }
36874         }
36875     },
36876
36877     updateTitle : function(title){
36878         if(this.titleTextEl && !this.config.title){
36879             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36880         }
36881     },
36882
36883     setActivePanel : function(panel)
36884     {
36885         panel = this.getPanel(panel);
36886         if(this.activePanel && this.activePanel != panel){
36887             if(this.activePanel.setActiveState(false) === false){
36888                 return;
36889             }
36890         }
36891         this.activePanel = panel;
36892         panel.setActiveState(true);
36893         if(this.panelSize){
36894             panel.setSize(this.panelSize.width, this.panelSize.height);
36895         }
36896         if(this.closeBtn){
36897             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36898         }
36899         this.updateTitle(panel.getTitle());
36900         if(this.tabs){
36901             this.fireEvent("invalidated", this);
36902         }
36903         this.fireEvent("panelactivated", this, panel);
36904     },
36905
36906     /**
36907      * Shows the specified panel.
36908      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36909      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36910      */
36911     showPanel : function(panel)
36912     {
36913         panel = this.getPanel(panel);
36914         if(panel){
36915             if(this.tabs){
36916                 var tab = this.tabs.getTab(panel.getEl().id);
36917                 if(tab.isHidden()){
36918                     this.tabs.unhideTab(tab.id);
36919                 }
36920                 tab.activate();
36921             }else{
36922                 this.setActivePanel(panel);
36923             }
36924         }
36925         return panel;
36926     },
36927
36928     /**
36929      * Get the active panel for this region.
36930      * @return {Roo.ContentPanel} The active panel or null
36931      */
36932     getActivePanel : function(){
36933         return this.activePanel;
36934     },
36935
36936     validateVisibility : function(){
36937         if(this.panels.getCount() < 1){
36938             this.updateTitle("&#160;");
36939             this.closeBtn.hide();
36940             this.hide();
36941         }else{
36942             if(!this.isVisible()){
36943                 this.show();
36944             }
36945         }
36946     },
36947
36948     /**
36949      * Adds the passed ContentPanel(s) to this region.
36950      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36951      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36952      */
36953     add : function(panel)
36954     {
36955         if(arguments.length > 1){
36956             for(var i = 0, len = arguments.length; i < len; i++) {
36957                 this.add(arguments[i]);
36958             }
36959             return null;
36960         }
36961         
36962         // if we have not been rendered yet, then we can not really do much of this..
36963         if (!this.bodyEl) {
36964             this.unrendered_panels.push(panel);
36965             return panel;
36966         }
36967         
36968         
36969         
36970         
36971         if(this.hasPanel(panel)){
36972             this.showPanel(panel);
36973             return panel;
36974         }
36975         panel.setRegion(this);
36976         this.panels.add(panel);
36977        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36978             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36979             // and hide them... ???
36980             this.bodyEl.dom.appendChild(panel.getEl().dom);
36981             if(panel.background !== true){
36982                 this.setActivePanel(panel);
36983             }
36984             this.fireEvent("paneladded", this, panel);
36985             return panel;
36986         }
36987         */
36988         if(!this.tabs){
36989             this.initTabs();
36990         }else{
36991             this.initPanelAsTab(panel);
36992         }
36993         
36994         
36995         if(panel.background !== true){
36996             this.tabs.activate(panel.getEl().id);
36997         }
36998         this.fireEvent("paneladded", this, panel);
36999         return panel;
37000     },
37001
37002     /**
37003      * Hides the tab for the specified panel.
37004      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37005      */
37006     hidePanel : function(panel){
37007         if(this.tabs && (panel = this.getPanel(panel))){
37008             this.tabs.hideTab(panel.getEl().id);
37009         }
37010     },
37011
37012     /**
37013      * Unhides the tab for a previously hidden panel.
37014      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37015      */
37016     unhidePanel : function(panel){
37017         if(this.tabs && (panel = this.getPanel(panel))){
37018             this.tabs.unhideTab(panel.getEl().id);
37019         }
37020     },
37021
37022     clearPanels : function(){
37023         while(this.panels.getCount() > 0){
37024              this.remove(this.panels.first());
37025         }
37026     },
37027
37028     /**
37029      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37030      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37031      * @param {Boolean} preservePanel Overrides the config preservePanel option
37032      * @return {Roo.ContentPanel} The panel that was removed
37033      */
37034     remove : function(panel, preservePanel)
37035     {
37036         panel = this.getPanel(panel);
37037         if(!panel){
37038             return null;
37039         }
37040         var e = {};
37041         this.fireEvent("beforeremove", this, panel, e);
37042         if(e.cancel === true){
37043             return null;
37044         }
37045         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37046         var panelId = panel.getId();
37047         this.panels.removeKey(panelId);
37048         if(preservePanel){
37049             document.body.appendChild(panel.getEl().dom);
37050         }
37051         if(this.tabs){
37052             this.tabs.removeTab(panel.getEl().id);
37053         }else if (!preservePanel){
37054             this.bodyEl.dom.removeChild(panel.getEl().dom);
37055         }
37056         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37057             var p = this.panels.first();
37058             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37059             tempEl.appendChild(p.getEl().dom);
37060             this.bodyEl.update("");
37061             this.bodyEl.dom.appendChild(p.getEl().dom);
37062             tempEl = null;
37063             this.updateTitle(p.getTitle());
37064             this.tabs = null;
37065             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37066             this.setActivePanel(p);
37067         }
37068         panel.setRegion(null);
37069         if(this.activePanel == panel){
37070             this.activePanel = null;
37071         }
37072         if(this.config.autoDestroy !== false && preservePanel !== true){
37073             try{panel.destroy();}catch(e){}
37074         }
37075         this.fireEvent("panelremoved", this, panel);
37076         return panel;
37077     },
37078
37079     /**
37080      * Returns the TabPanel component used by this region
37081      * @return {Roo.TabPanel}
37082      */
37083     getTabs : function(){
37084         return this.tabs;
37085     },
37086
37087     createTool : function(parentEl, className){
37088         var btn = Roo.DomHelper.append(parentEl, {
37089             tag: "div",
37090             cls: "x-layout-tools-button",
37091             children: [ {
37092                 tag: "div",
37093                 cls: "roo-layout-tools-button-inner " + className,
37094                 html: "&#160;"
37095             }]
37096         }, true);
37097         btn.addClassOnOver("roo-layout-tools-button-over");
37098         return btn;
37099     }
37100 });/*
37101  * Based on:
37102  * Ext JS Library 1.1.1
37103  * Copyright(c) 2006-2007, Ext JS, LLC.
37104  *
37105  * Originally Released Under LGPL - original licence link has changed is not relivant.
37106  *
37107  * Fork - LGPL
37108  * <script type="text/javascript">
37109  */
37110  
37111
37112
37113 /**
37114  * @class Roo.SplitLayoutRegion
37115  * @extends Roo.LayoutRegion
37116  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37117  */
37118 Roo.bootstrap.layout.Split = function(config){
37119     this.cursor = config.cursor;
37120     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37121 };
37122
37123 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37124 {
37125     splitTip : "Drag to resize.",
37126     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37127     useSplitTips : false,
37128
37129     applyConfig : function(config){
37130         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37131     },
37132     
37133     onRender : function(ctr,pos) {
37134         
37135         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37136         if(!this.config.split){
37137             return;
37138         }
37139         if(!this.split){
37140             
37141             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37142                             tag: "div",
37143                             id: this.el.id + "-split",
37144                             cls: "roo-layout-split roo-layout-split-"+this.position,
37145                             html: "&#160;"
37146             });
37147             /** The SplitBar for this region 
37148             * @type Roo.SplitBar */
37149             // does not exist yet...
37150             Roo.log([this.position, this.orientation]);
37151             
37152             this.split = new Roo.bootstrap.SplitBar({
37153                 dragElement : splitEl,
37154                 resizingElement: this.el,
37155                 orientation : this.orientation
37156             });
37157             
37158             this.split.on("moved", this.onSplitMove, this);
37159             this.split.useShim = this.config.useShim === true;
37160             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37161             if(this.useSplitTips){
37162                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37163             }
37164             //if(config.collapsible){
37165             //    this.split.el.on("dblclick", this.collapse,  this);
37166             //}
37167         }
37168         if(typeof this.config.minSize != "undefined"){
37169             this.split.minSize = this.config.minSize;
37170         }
37171         if(typeof this.config.maxSize != "undefined"){
37172             this.split.maxSize = this.config.maxSize;
37173         }
37174         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37175             this.hideSplitter();
37176         }
37177         
37178     },
37179
37180     getHMaxSize : function(){
37181          var cmax = this.config.maxSize || 10000;
37182          var center = this.mgr.getRegion("center");
37183          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37184     },
37185
37186     getVMaxSize : function(){
37187          var cmax = this.config.maxSize || 10000;
37188          var center = this.mgr.getRegion("center");
37189          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37190     },
37191
37192     onSplitMove : function(split, newSize){
37193         this.fireEvent("resized", this, newSize);
37194     },
37195     
37196     /** 
37197      * Returns the {@link Roo.SplitBar} for this region.
37198      * @return {Roo.SplitBar}
37199      */
37200     getSplitBar : function(){
37201         return this.split;
37202     },
37203     
37204     hide : function(){
37205         this.hideSplitter();
37206         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37207     },
37208
37209     hideSplitter : function(){
37210         if(this.split){
37211             this.split.el.setLocation(-2000,-2000);
37212             this.split.el.hide();
37213         }
37214     },
37215
37216     show : function(){
37217         if(this.split){
37218             this.split.el.show();
37219         }
37220         Roo.bootstrap.layout.Split.superclass.show.call(this);
37221     },
37222     
37223     beforeSlide: function(){
37224         if(Roo.isGecko){// firefox overflow auto bug workaround
37225             this.bodyEl.clip();
37226             if(this.tabs) {
37227                 this.tabs.bodyEl.clip();
37228             }
37229             if(this.activePanel){
37230                 this.activePanel.getEl().clip();
37231                 
37232                 if(this.activePanel.beforeSlide){
37233                     this.activePanel.beforeSlide();
37234                 }
37235             }
37236         }
37237     },
37238     
37239     afterSlide : function(){
37240         if(Roo.isGecko){// firefox overflow auto bug workaround
37241             this.bodyEl.unclip();
37242             if(this.tabs) {
37243                 this.tabs.bodyEl.unclip();
37244             }
37245             if(this.activePanel){
37246                 this.activePanel.getEl().unclip();
37247                 if(this.activePanel.afterSlide){
37248                     this.activePanel.afterSlide();
37249                 }
37250             }
37251         }
37252     },
37253
37254     initAutoHide : function(){
37255         if(this.autoHide !== false){
37256             if(!this.autoHideHd){
37257                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37258                 this.autoHideHd = {
37259                     "mouseout": function(e){
37260                         if(!e.within(this.el, true)){
37261                             st.delay(500);
37262                         }
37263                     },
37264                     "mouseover" : function(e){
37265                         st.cancel();
37266                     },
37267                     scope : this
37268                 };
37269             }
37270             this.el.on(this.autoHideHd);
37271         }
37272     },
37273
37274     clearAutoHide : function(){
37275         if(this.autoHide !== false){
37276             this.el.un("mouseout", this.autoHideHd.mouseout);
37277             this.el.un("mouseover", this.autoHideHd.mouseover);
37278         }
37279     },
37280
37281     clearMonitor : function(){
37282         Roo.get(document).un("click", this.slideInIf, this);
37283     },
37284
37285     // these names are backwards but not changed for compat
37286     slideOut : function(){
37287         if(this.isSlid || this.el.hasActiveFx()){
37288             return;
37289         }
37290         this.isSlid = true;
37291         if(this.collapseBtn){
37292             this.collapseBtn.hide();
37293         }
37294         this.closeBtnState = this.closeBtn.getStyle('display');
37295         this.closeBtn.hide();
37296         if(this.stickBtn){
37297             this.stickBtn.show();
37298         }
37299         this.el.show();
37300         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37301         this.beforeSlide();
37302         this.el.setStyle("z-index", 10001);
37303         this.el.slideIn(this.getSlideAnchor(), {
37304             callback: function(){
37305                 this.afterSlide();
37306                 this.initAutoHide();
37307                 Roo.get(document).on("click", this.slideInIf, this);
37308                 this.fireEvent("slideshow", this);
37309             },
37310             scope: this,
37311             block: true
37312         });
37313     },
37314
37315     afterSlideIn : function(){
37316         this.clearAutoHide();
37317         this.isSlid = false;
37318         this.clearMonitor();
37319         this.el.setStyle("z-index", "");
37320         if(this.collapseBtn){
37321             this.collapseBtn.show();
37322         }
37323         this.closeBtn.setStyle('display', this.closeBtnState);
37324         if(this.stickBtn){
37325             this.stickBtn.hide();
37326         }
37327         this.fireEvent("slidehide", this);
37328     },
37329
37330     slideIn : function(cb){
37331         if(!this.isSlid || this.el.hasActiveFx()){
37332             Roo.callback(cb);
37333             return;
37334         }
37335         this.isSlid = false;
37336         this.beforeSlide();
37337         this.el.slideOut(this.getSlideAnchor(), {
37338             callback: function(){
37339                 this.el.setLeftTop(-10000, -10000);
37340                 this.afterSlide();
37341                 this.afterSlideIn();
37342                 Roo.callback(cb);
37343             },
37344             scope: this,
37345             block: true
37346         });
37347     },
37348     
37349     slideInIf : function(e){
37350         if(!e.within(this.el)){
37351             this.slideIn();
37352         }
37353     },
37354
37355     animateCollapse : function(){
37356         this.beforeSlide();
37357         this.el.setStyle("z-index", 20000);
37358         var anchor = this.getSlideAnchor();
37359         this.el.slideOut(anchor, {
37360             callback : function(){
37361                 this.el.setStyle("z-index", "");
37362                 this.collapsedEl.slideIn(anchor, {duration:.3});
37363                 this.afterSlide();
37364                 this.el.setLocation(-10000,-10000);
37365                 this.el.hide();
37366                 this.fireEvent("collapsed", this);
37367             },
37368             scope: this,
37369             block: true
37370         });
37371     },
37372
37373     animateExpand : function(){
37374         this.beforeSlide();
37375         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37376         this.el.setStyle("z-index", 20000);
37377         this.collapsedEl.hide({
37378             duration:.1
37379         });
37380         this.el.slideIn(this.getSlideAnchor(), {
37381             callback : function(){
37382                 this.el.setStyle("z-index", "");
37383                 this.afterSlide();
37384                 if(this.split){
37385                     this.split.el.show();
37386                 }
37387                 this.fireEvent("invalidated", this);
37388                 this.fireEvent("expanded", this);
37389             },
37390             scope: this,
37391             block: true
37392         });
37393     },
37394
37395     anchors : {
37396         "west" : "left",
37397         "east" : "right",
37398         "north" : "top",
37399         "south" : "bottom"
37400     },
37401
37402     sanchors : {
37403         "west" : "l",
37404         "east" : "r",
37405         "north" : "t",
37406         "south" : "b"
37407     },
37408
37409     canchors : {
37410         "west" : "tl-tr",
37411         "east" : "tr-tl",
37412         "north" : "tl-bl",
37413         "south" : "bl-tl"
37414     },
37415
37416     getAnchor : function(){
37417         return this.anchors[this.position];
37418     },
37419
37420     getCollapseAnchor : function(){
37421         return this.canchors[this.position];
37422     },
37423
37424     getSlideAnchor : function(){
37425         return this.sanchors[this.position];
37426     },
37427
37428     getAlignAdj : function(){
37429         var cm = this.cmargins;
37430         switch(this.position){
37431             case "west":
37432                 return [0, 0];
37433             break;
37434             case "east":
37435                 return [0, 0];
37436             break;
37437             case "north":
37438                 return [0, 0];
37439             break;
37440             case "south":
37441                 return [0, 0];
37442             break;
37443         }
37444     },
37445
37446     getExpandAdj : function(){
37447         var c = this.collapsedEl, cm = this.cmargins;
37448         switch(this.position){
37449             case "west":
37450                 return [-(cm.right+c.getWidth()+cm.left), 0];
37451             break;
37452             case "east":
37453                 return [cm.right+c.getWidth()+cm.left, 0];
37454             break;
37455             case "north":
37456                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37457             break;
37458             case "south":
37459                 return [0, cm.top+cm.bottom+c.getHeight()];
37460             break;
37461         }
37462     }
37463 });/*
37464  * Based on:
37465  * Ext JS Library 1.1.1
37466  * Copyright(c) 2006-2007, Ext JS, LLC.
37467  *
37468  * Originally Released Under LGPL - original licence link has changed is not relivant.
37469  *
37470  * Fork - LGPL
37471  * <script type="text/javascript">
37472  */
37473 /*
37474  * These classes are private internal classes
37475  */
37476 Roo.bootstrap.layout.Center = function(config){
37477     config.region = "center";
37478     Roo.bootstrap.layout.Region.call(this, config);
37479     this.visible = true;
37480     this.minWidth = config.minWidth || 20;
37481     this.minHeight = config.minHeight || 20;
37482 };
37483
37484 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37485     hide : function(){
37486         // center panel can't be hidden
37487     },
37488     
37489     show : function(){
37490         // center panel can't be hidden
37491     },
37492     
37493     getMinWidth: function(){
37494         return this.minWidth;
37495     },
37496     
37497     getMinHeight: function(){
37498         return this.minHeight;
37499     }
37500 });
37501
37502
37503
37504
37505  
37506
37507
37508
37509
37510
37511
37512 Roo.bootstrap.layout.North = function(config)
37513 {
37514     config.region = 'north';
37515     config.cursor = 'n-resize';
37516     
37517     Roo.bootstrap.layout.Split.call(this, config);
37518     
37519     
37520     if(this.split){
37521         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37522         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37523         this.split.el.addClass("roo-layout-split-v");
37524     }
37525     var size = config.initialSize || config.height;
37526     if(typeof size != "undefined"){
37527         this.el.setHeight(size);
37528     }
37529 };
37530 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37531 {
37532     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37533     
37534     
37535     
37536     getBox : function(){
37537         if(this.collapsed){
37538             return this.collapsedEl.getBox();
37539         }
37540         var box = this.el.getBox();
37541         if(this.split){
37542             box.height += this.split.el.getHeight();
37543         }
37544         return box;
37545     },
37546     
37547     updateBox : function(box){
37548         if(this.split && !this.collapsed){
37549             box.height -= this.split.el.getHeight();
37550             this.split.el.setLeft(box.x);
37551             this.split.el.setTop(box.y+box.height);
37552             this.split.el.setWidth(box.width);
37553         }
37554         if(this.collapsed){
37555             this.updateBody(box.width, null);
37556         }
37557         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37558     }
37559 });
37560
37561
37562
37563
37564
37565 Roo.bootstrap.layout.South = function(config){
37566     config.region = 'south';
37567     config.cursor = 's-resize';
37568     Roo.bootstrap.layout.Split.call(this, config);
37569     if(this.split){
37570         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37571         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37572         this.split.el.addClass("roo-layout-split-v");
37573     }
37574     var size = config.initialSize || config.height;
37575     if(typeof size != "undefined"){
37576         this.el.setHeight(size);
37577     }
37578 };
37579
37580 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37581     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37582     getBox : function(){
37583         if(this.collapsed){
37584             return this.collapsedEl.getBox();
37585         }
37586         var box = this.el.getBox();
37587         if(this.split){
37588             var sh = this.split.el.getHeight();
37589             box.height += sh;
37590             box.y -= sh;
37591         }
37592         return box;
37593     },
37594     
37595     updateBox : function(box){
37596         if(this.split && !this.collapsed){
37597             var sh = this.split.el.getHeight();
37598             box.height -= sh;
37599             box.y += sh;
37600             this.split.el.setLeft(box.x);
37601             this.split.el.setTop(box.y-sh);
37602             this.split.el.setWidth(box.width);
37603         }
37604         if(this.collapsed){
37605             this.updateBody(box.width, null);
37606         }
37607         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37608     }
37609 });
37610
37611 Roo.bootstrap.layout.East = function(config){
37612     config.region = "east";
37613     config.cursor = "e-resize";
37614     Roo.bootstrap.layout.Split.call(this, config);
37615     if(this.split){
37616         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37617         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37618         this.split.el.addClass("roo-layout-split-h");
37619     }
37620     var size = config.initialSize || config.width;
37621     if(typeof size != "undefined"){
37622         this.el.setWidth(size);
37623     }
37624 };
37625 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37626     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37627     getBox : function(){
37628         if(this.collapsed){
37629             return this.collapsedEl.getBox();
37630         }
37631         var box = this.el.getBox();
37632         if(this.split){
37633             var sw = this.split.el.getWidth();
37634             box.width += sw;
37635             box.x -= sw;
37636         }
37637         return box;
37638     },
37639
37640     updateBox : function(box){
37641         if(this.split && !this.collapsed){
37642             var sw = this.split.el.getWidth();
37643             box.width -= sw;
37644             this.split.el.setLeft(box.x);
37645             this.split.el.setTop(box.y);
37646             this.split.el.setHeight(box.height);
37647             box.x += sw;
37648         }
37649         if(this.collapsed){
37650             this.updateBody(null, box.height);
37651         }
37652         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37653     }
37654 });
37655
37656 Roo.bootstrap.layout.West = function(config){
37657     config.region = "west";
37658     config.cursor = "w-resize";
37659     
37660     Roo.bootstrap.layout.Split.call(this, config);
37661     if(this.split){
37662         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37663         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37664         this.split.el.addClass("roo-layout-split-h");
37665     }
37666     
37667 };
37668 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37669     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37670     
37671     onRender: function(ctr, pos)
37672     {
37673         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37674         var size = this.config.initialSize || this.config.width;
37675         if(typeof size != "undefined"){
37676             this.el.setWidth(size);
37677         }
37678     },
37679     
37680     getBox : function(){
37681         if(this.collapsed){
37682             return this.collapsedEl.getBox();
37683         }
37684         var box = this.el.getBox();
37685         if(this.split){
37686             box.width += this.split.el.getWidth();
37687         }
37688         return box;
37689     },
37690     
37691     updateBox : function(box){
37692         if(this.split && !this.collapsed){
37693             var sw = this.split.el.getWidth();
37694             box.width -= sw;
37695             this.split.el.setLeft(box.x+box.width);
37696             this.split.el.setTop(box.y);
37697             this.split.el.setHeight(box.height);
37698         }
37699         if(this.collapsed){
37700             this.updateBody(null, box.height);
37701         }
37702         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37703     }
37704 });Roo.namespace("Roo.bootstrap.panel");/*
37705  * Based on:
37706  * Ext JS Library 1.1.1
37707  * Copyright(c) 2006-2007, Ext JS, LLC.
37708  *
37709  * Originally Released Under LGPL - original licence link has changed is not relivant.
37710  *
37711  * Fork - LGPL
37712  * <script type="text/javascript">
37713  */
37714 /**
37715  * @class Roo.ContentPanel
37716  * @extends Roo.util.Observable
37717  * A basic ContentPanel element.
37718  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37719  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37720  * @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
37721  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37722  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37723  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37724  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37725  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37726  * @cfg {String} title          The title for this panel
37727  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37728  * @cfg {String} url            Calls {@link #setUrl} with this value
37729  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37730  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37731  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37732  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37733  * @cfg {Boolean} badges render the badges
37734
37735  * @constructor
37736  * Create a new ContentPanel.
37737  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37738  * @param {String/Object} config A string to set only the title or a config object
37739  * @param {String} content (optional) Set the HTML content for this panel
37740  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37741  */
37742 Roo.bootstrap.panel.Content = function( config){
37743     
37744     this.tpl = config.tpl || false;
37745     
37746     var el = config.el;
37747     var content = config.content;
37748
37749     if(config.autoCreate){ // xtype is available if this is called from factory
37750         el = Roo.id();
37751     }
37752     this.el = Roo.get(el);
37753     if(!this.el && config && config.autoCreate){
37754         if(typeof config.autoCreate == "object"){
37755             if(!config.autoCreate.id){
37756                 config.autoCreate.id = config.id||el;
37757             }
37758             this.el = Roo.DomHelper.append(document.body,
37759                         config.autoCreate, true);
37760         }else{
37761             var elcfg =  {   tag: "div",
37762                             cls: "roo-layout-inactive-content",
37763                             id: config.id||el
37764                             };
37765             if (config.html) {
37766                 elcfg.html = config.html;
37767                 
37768             }
37769                         
37770             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37771         }
37772     } 
37773     this.closable = false;
37774     this.loaded = false;
37775     this.active = false;
37776    
37777       
37778     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37779         
37780         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37781         
37782         this.wrapEl = this.el; //this.el.wrap();
37783         var ti = [];
37784         if (config.toolbar.items) {
37785             ti = config.toolbar.items ;
37786             delete config.toolbar.items ;
37787         }
37788         
37789         var nitems = [];
37790         this.toolbar.render(this.wrapEl, 'before');
37791         for(var i =0;i < ti.length;i++) {
37792           //  Roo.log(['add child', items[i]]);
37793             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37794         }
37795         this.toolbar.items = nitems;
37796         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37797         delete config.toolbar;
37798         
37799     }
37800     /*
37801     // xtype created footer. - not sure if will work as we normally have to render first..
37802     if (this.footer && !this.footer.el && this.footer.xtype) {
37803         if (!this.wrapEl) {
37804             this.wrapEl = this.el.wrap();
37805         }
37806     
37807         this.footer.container = this.wrapEl.createChild();
37808          
37809         this.footer = Roo.factory(this.footer, Roo);
37810         
37811     }
37812     */
37813     
37814      if(typeof config == "string"){
37815         this.title = config;
37816     }else{
37817         Roo.apply(this, config);
37818     }
37819     
37820     if(this.resizeEl){
37821         this.resizeEl = Roo.get(this.resizeEl, true);
37822     }else{
37823         this.resizeEl = this.el;
37824     }
37825     // handle view.xtype
37826     
37827  
37828     
37829     
37830     this.addEvents({
37831         /**
37832          * @event activate
37833          * Fires when this panel is activated. 
37834          * @param {Roo.ContentPanel} this
37835          */
37836         "activate" : true,
37837         /**
37838          * @event deactivate
37839          * Fires when this panel is activated. 
37840          * @param {Roo.ContentPanel} this
37841          */
37842         "deactivate" : true,
37843
37844         /**
37845          * @event resize
37846          * Fires when this panel is resized if fitToFrame is true.
37847          * @param {Roo.ContentPanel} this
37848          * @param {Number} width The width after any component adjustments
37849          * @param {Number} height The height after any component adjustments
37850          */
37851         "resize" : true,
37852         
37853          /**
37854          * @event render
37855          * Fires when this tab is created
37856          * @param {Roo.ContentPanel} this
37857          */
37858         "render" : true
37859         
37860         
37861         
37862     });
37863     
37864
37865     
37866     
37867     if(this.autoScroll){
37868         this.resizeEl.setStyle("overflow", "auto");
37869     } else {
37870         // fix randome scrolling
37871         //this.el.on('scroll', function() {
37872         //    Roo.log('fix random scolling');
37873         //    this.scrollTo('top',0); 
37874         //});
37875     }
37876     content = content || this.content;
37877     if(content){
37878         this.setContent(content);
37879     }
37880     if(config && config.url){
37881         this.setUrl(this.url, this.params, this.loadOnce);
37882     }
37883     
37884     
37885     
37886     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37887     
37888     if (this.view && typeof(this.view.xtype) != 'undefined') {
37889         this.view.el = this.el.appendChild(document.createElement("div"));
37890         this.view = Roo.factory(this.view); 
37891         this.view.render  &&  this.view.render(false, '');  
37892     }
37893     
37894     
37895     this.fireEvent('render', this);
37896 };
37897
37898 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37899     
37900     tabTip : '',
37901     
37902     setRegion : function(region){
37903         this.region = region;
37904         this.setActiveClass(region && !this.background);
37905     },
37906     
37907     
37908     setActiveClass: function(state)
37909     {
37910         if(state){
37911            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37912            this.el.setStyle('position','relative');
37913         }else{
37914            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37915            this.el.setStyle('position', 'absolute');
37916         } 
37917     },
37918     
37919     /**
37920      * Returns the toolbar for this Panel if one was configured. 
37921      * @return {Roo.Toolbar} 
37922      */
37923     getToolbar : function(){
37924         return this.toolbar;
37925     },
37926     
37927     setActiveState : function(active)
37928     {
37929         this.active = active;
37930         this.setActiveClass(active);
37931         if(!active){
37932             if(this.fireEvent("deactivate", this) === false){
37933                 return false;
37934             }
37935             return true;
37936         }
37937         this.fireEvent("activate", this);
37938         return true;
37939     },
37940     /**
37941      * Updates this panel's element
37942      * @param {String} content The new content
37943      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37944     */
37945     setContent : function(content, loadScripts){
37946         this.el.update(content, loadScripts);
37947     },
37948
37949     ignoreResize : function(w, h){
37950         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37951             return true;
37952         }else{
37953             this.lastSize = {width: w, height: h};
37954             return false;
37955         }
37956     },
37957     /**
37958      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37959      * @return {Roo.UpdateManager} The UpdateManager
37960      */
37961     getUpdateManager : function(){
37962         return this.el.getUpdateManager();
37963     },
37964      /**
37965      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37966      * @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:
37967 <pre><code>
37968 panel.load({
37969     url: "your-url.php",
37970     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37971     callback: yourFunction,
37972     scope: yourObject, //(optional scope)
37973     discardUrl: false,
37974     nocache: false,
37975     text: "Loading...",
37976     timeout: 30,
37977     scripts: false
37978 });
37979 </code></pre>
37980      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37981      * 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.
37982      * @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}
37983      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37984      * @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.
37985      * @return {Roo.ContentPanel} this
37986      */
37987     load : function(){
37988         var um = this.el.getUpdateManager();
37989         um.update.apply(um, arguments);
37990         return this;
37991     },
37992
37993
37994     /**
37995      * 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.
37996      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37997      * @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)
37998      * @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)
37999      * @return {Roo.UpdateManager} The UpdateManager
38000      */
38001     setUrl : function(url, params, loadOnce){
38002         if(this.refreshDelegate){
38003             this.removeListener("activate", this.refreshDelegate);
38004         }
38005         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38006         this.on("activate", this.refreshDelegate);
38007         return this.el.getUpdateManager();
38008     },
38009     
38010     _handleRefresh : function(url, params, loadOnce){
38011         if(!loadOnce || !this.loaded){
38012             var updater = this.el.getUpdateManager();
38013             updater.update(url, params, this._setLoaded.createDelegate(this));
38014         }
38015     },
38016     
38017     _setLoaded : function(){
38018         this.loaded = true;
38019     }, 
38020     
38021     /**
38022      * Returns this panel's id
38023      * @return {String} 
38024      */
38025     getId : function(){
38026         return this.el.id;
38027     },
38028     
38029     /** 
38030      * Returns this panel's element - used by regiosn to add.
38031      * @return {Roo.Element} 
38032      */
38033     getEl : function(){
38034         return this.wrapEl || this.el;
38035     },
38036     
38037    
38038     
38039     adjustForComponents : function(width, height)
38040     {
38041         //Roo.log('adjustForComponents ');
38042         if(this.resizeEl != this.el){
38043             width -= this.el.getFrameWidth('lr');
38044             height -= this.el.getFrameWidth('tb');
38045         }
38046         if(this.toolbar){
38047             var te = this.toolbar.getEl();
38048             te.setWidth(width);
38049             height -= te.getHeight();
38050         }
38051         if(this.footer){
38052             var te = this.footer.getEl();
38053             te.setWidth(width);
38054             height -= te.getHeight();
38055         }
38056         
38057         
38058         if(this.adjustments){
38059             width += this.adjustments[0];
38060             height += this.adjustments[1];
38061         }
38062         return {"width": width, "height": height};
38063     },
38064     
38065     setSize : function(width, height){
38066         if(this.fitToFrame && !this.ignoreResize(width, height)){
38067             if(this.fitContainer && this.resizeEl != this.el){
38068                 this.el.setSize(width, height);
38069             }
38070             var size = this.adjustForComponents(width, height);
38071             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38072             this.fireEvent('resize', this, size.width, size.height);
38073         }
38074     },
38075     
38076     /**
38077      * Returns this panel's title
38078      * @return {String} 
38079      */
38080     getTitle : function(){
38081         
38082         if (typeof(this.title) != 'object') {
38083             return this.title;
38084         }
38085         
38086         var t = '';
38087         for (var k in this.title) {
38088             if (!this.title.hasOwnProperty(k)) {
38089                 continue;
38090             }
38091             
38092             if (k.indexOf('-') >= 0) {
38093                 var s = k.split('-');
38094                 for (var i = 0; i<s.length; i++) {
38095                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38096                 }
38097             } else {
38098                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38099             }
38100         }
38101         return t;
38102     },
38103     
38104     /**
38105      * Set this panel's title
38106      * @param {String} title
38107      */
38108     setTitle : function(title){
38109         this.title = title;
38110         if(this.region){
38111             this.region.updatePanelTitle(this, title);
38112         }
38113     },
38114     
38115     /**
38116      * Returns true is this panel was configured to be closable
38117      * @return {Boolean} 
38118      */
38119     isClosable : function(){
38120         return this.closable;
38121     },
38122     
38123     beforeSlide : function(){
38124         this.el.clip();
38125         this.resizeEl.clip();
38126     },
38127     
38128     afterSlide : function(){
38129         this.el.unclip();
38130         this.resizeEl.unclip();
38131     },
38132     
38133     /**
38134      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38135      *   Will fail silently if the {@link #setUrl} method has not been called.
38136      *   This does not activate the panel, just updates its content.
38137      */
38138     refresh : function(){
38139         if(this.refreshDelegate){
38140            this.loaded = false;
38141            this.refreshDelegate();
38142         }
38143     },
38144     
38145     /**
38146      * Destroys this panel
38147      */
38148     destroy : function(){
38149         this.el.removeAllListeners();
38150         var tempEl = document.createElement("span");
38151         tempEl.appendChild(this.el.dom);
38152         tempEl.innerHTML = "";
38153         this.el.remove();
38154         this.el = null;
38155     },
38156     
38157     /**
38158      * form - if the content panel contains a form - this is a reference to it.
38159      * @type {Roo.form.Form}
38160      */
38161     form : false,
38162     /**
38163      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38164      *    This contains a reference to it.
38165      * @type {Roo.View}
38166      */
38167     view : false,
38168     
38169       /**
38170      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38171      * <pre><code>
38172
38173 layout.addxtype({
38174        xtype : 'Form',
38175        items: [ .... ]
38176    }
38177 );
38178
38179 </code></pre>
38180      * @param {Object} cfg Xtype definition of item to add.
38181      */
38182     
38183     
38184     getChildContainer: function () {
38185         return this.getEl();
38186     }
38187     
38188     
38189     /*
38190         var  ret = new Roo.factory(cfg);
38191         return ret;
38192         
38193         
38194         // add form..
38195         if (cfg.xtype.match(/^Form$/)) {
38196             
38197             var el;
38198             //if (this.footer) {
38199             //    el = this.footer.container.insertSibling(false, 'before');
38200             //} else {
38201                 el = this.el.createChild();
38202             //}
38203
38204             this.form = new  Roo.form.Form(cfg);
38205             
38206             
38207             if ( this.form.allItems.length) {
38208                 this.form.render(el.dom);
38209             }
38210             return this.form;
38211         }
38212         // should only have one of theses..
38213         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38214             // views.. should not be just added - used named prop 'view''
38215             
38216             cfg.el = this.el.appendChild(document.createElement("div"));
38217             // factory?
38218             
38219             var ret = new Roo.factory(cfg);
38220              
38221              ret.render && ret.render(false, ''); // render blank..
38222             this.view = ret;
38223             return ret;
38224         }
38225         return false;
38226     }
38227     \*/
38228 });
38229  
38230 /**
38231  * @class Roo.bootstrap.panel.Grid
38232  * @extends Roo.bootstrap.panel.Content
38233  * @constructor
38234  * Create a new GridPanel.
38235  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38236  * @param {Object} config A the config object
38237   
38238  */
38239
38240
38241
38242 Roo.bootstrap.panel.Grid = function(config)
38243 {
38244     
38245       
38246     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38247         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38248
38249     config.el = this.wrapper;
38250     //this.el = this.wrapper;
38251     
38252       if (config.container) {
38253         // ctor'ed from a Border/panel.grid
38254         
38255         
38256         this.wrapper.setStyle("overflow", "hidden");
38257         this.wrapper.addClass('roo-grid-container');
38258
38259     }
38260     
38261     
38262     if(config.toolbar){
38263         var tool_el = this.wrapper.createChild();    
38264         this.toolbar = Roo.factory(config.toolbar);
38265         var ti = [];
38266         if (config.toolbar.items) {
38267             ti = config.toolbar.items ;
38268             delete config.toolbar.items ;
38269         }
38270         
38271         var nitems = [];
38272         this.toolbar.render(tool_el);
38273         for(var i =0;i < ti.length;i++) {
38274           //  Roo.log(['add child', items[i]]);
38275             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38276         }
38277         this.toolbar.items = nitems;
38278         
38279         delete config.toolbar;
38280     }
38281     
38282     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38283     config.grid.scrollBody = true;;
38284     config.grid.monitorWindowResize = false; // turn off autosizing
38285     config.grid.autoHeight = false;
38286     config.grid.autoWidth = false;
38287     
38288     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38289     
38290     if (config.background) {
38291         // render grid on panel activation (if panel background)
38292         this.on('activate', function(gp) {
38293             if (!gp.grid.rendered) {
38294                 gp.grid.render(this.wrapper);
38295                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38296             }
38297         });
38298             
38299     } else {
38300         this.grid.render(this.wrapper);
38301         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38302
38303     }
38304     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38305     // ??? needed ??? config.el = this.wrapper;
38306     
38307     
38308     
38309   
38310     // xtype created footer. - not sure if will work as we normally have to render first..
38311     if (this.footer && !this.footer.el && this.footer.xtype) {
38312         
38313         var ctr = this.grid.getView().getFooterPanel(true);
38314         this.footer.dataSource = this.grid.dataSource;
38315         this.footer = Roo.factory(this.footer, Roo);
38316         this.footer.render(ctr);
38317         
38318     }
38319     
38320     
38321     
38322     
38323      
38324 };
38325
38326 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38327     getId : function(){
38328         return this.grid.id;
38329     },
38330     
38331     /**
38332      * Returns the grid for this panel
38333      * @return {Roo.bootstrap.Table} 
38334      */
38335     getGrid : function(){
38336         return this.grid;    
38337     },
38338     
38339     setSize : function(width, height){
38340         if(!this.ignoreResize(width, height)){
38341             var grid = this.grid;
38342             var size = this.adjustForComponents(width, height);
38343             var gridel = grid.getGridEl();
38344             gridel.setSize(size.width, size.height);
38345             /*
38346             var thd = grid.getGridEl().select('thead',true).first();
38347             var tbd = grid.getGridEl().select('tbody', true).first();
38348             if (tbd) {
38349                 tbd.setSize(width, height - thd.getHeight());
38350             }
38351             */
38352             grid.autoSize();
38353         }
38354     },
38355      
38356     
38357     
38358     beforeSlide : function(){
38359         this.grid.getView().scroller.clip();
38360     },
38361     
38362     afterSlide : function(){
38363         this.grid.getView().scroller.unclip();
38364     },
38365     
38366     destroy : function(){
38367         this.grid.destroy();
38368         delete this.grid;
38369         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38370     }
38371 });
38372
38373 /**
38374  * @class Roo.bootstrap.panel.Nest
38375  * @extends Roo.bootstrap.panel.Content
38376  * @constructor
38377  * Create a new Panel, that can contain a layout.Border.
38378  * 
38379  * 
38380  * @param {Roo.BorderLayout} layout The layout for this panel
38381  * @param {String/Object} config A string to set only the title or a config object
38382  */
38383 Roo.bootstrap.panel.Nest = function(config)
38384 {
38385     // construct with only one argument..
38386     /* FIXME - implement nicer consturctors
38387     if (layout.layout) {
38388         config = layout;
38389         layout = config.layout;
38390         delete config.layout;
38391     }
38392     if (layout.xtype && !layout.getEl) {
38393         // then layout needs constructing..
38394         layout = Roo.factory(layout, Roo);
38395     }
38396     */
38397     
38398     config.el =  config.layout.getEl();
38399     
38400     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38401     
38402     config.layout.monitorWindowResize = false; // turn off autosizing
38403     this.layout = config.layout;
38404     this.layout.getEl().addClass("roo-layout-nested-layout");
38405     this.layout.parent = this;
38406     
38407     
38408     
38409     
38410 };
38411
38412 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38413
38414     setSize : function(width, height){
38415         if(!this.ignoreResize(width, height)){
38416             var size = this.adjustForComponents(width, height);
38417             var el = this.layout.getEl();
38418             if (size.height < 1) {
38419                 el.setWidth(size.width);   
38420             } else {
38421                 el.setSize(size.width, size.height);
38422             }
38423             var touch = el.dom.offsetWidth;
38424             this.layout.layout();
38425             // ie requires a double layout on the first pass
38426             if(Roo.isIE && !this.initialized){
38427                 this.initialized = true;
38428                 this.layout.layout();
38429             }
38430         }
38431     },
38432     
38433     // activate all subpanels if not currently active..
38434     
38435     setActiveState : function(active){
38436         this.active = active;
38437         this.setActiveClass(active);
38438         
38439         if(!active){
38440             this.fireEvent("deactivate", this);
38441             return;
38442         }
38443         
38444         this.fireEvent("activate", this);
38445         // not sure if this should happen before or after..
38446         if (!this.layout) {
38447             return; // should not happen..
38448         }
38449         var reg = false;
38450         for (var r in this.layout.regions) {
38451             reg = this.layout.getRegion(r);
38452             if (reg.getActivePanel()) {
38453                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38454                 reg.setActivePanel(reg.getActivePanel());
38455                 continue;
38456             }
38457             if (!reg.panels.length) {
38458                 continue;
38459             }
38460             reg.showPanel(reg.getPanel(0));
38461         }
38462         
38463         
38464         
38465         
38466     },
38467     
38468     /**
38469      * Returns the nested BorderLayout for this panel
38470      * @return {Roo.BorderLayout} 
38471      */
38472     getLayout : function(){
38473         return this.layout;
38474     },
38475     
38476      /**
38477      * Adds a xtype elements to the layout of the nested panel
38478      * <pre><code>
38479
38480 panel.addxtype({
38481        xtype : 'ContentPanel',
38482        region: 'west',
38483        items: [ .... ]
38484    }
38485 );
38486
38487 panel.addxtype({
38488         xtype : 'NestedLayoutPanel',
38489         region: 'west',
38490         layout: {
38491            center: { },
38492            west: { }   
38493         },
38494         items : [ ... list of content panels or nested layout panels.. ]
38495    }
38496 );
38497 </code></pre>
38498      * @param {Object} cfg Xtype definition of item to add.
38499      */
38500     addxtype : function(cfg) {
38501         return this.layout.addxtype(cfg);
38502     
38503     }
38504 });/*
38505  * Based on:
38506  * Ext JS Library 1.1.1
38507  * Copyright(c) 2006-2007, Ext JS, LLC.
38508  *
38509  * Originally Released Under LGPL - original licence link has changed is not relivant.
38510  *
38511  * Fork - LGPL
38512  * <script type="text/javascript">
38513  */
38514 /**
38515  * @class Roo.TabPanel
38516  * @extends Roo.util.Observable
38517  * A lightweight tab container.
38518  * <br><br>
38519  * Usage:
38520  * <pre><code>
38521 // basic tabs 1, built from existing content
38522 var tabs = new Roo.TabPanel("tabs1");
38523 tabs.addTab("script", "View Script");
38524 tabs.addTab("markup", "View Markup");
38525 tabs.activate("script");
38526
38527 // more advanced tabs, built from javascript
38528 var jtabs = new Roo.TabPanel("jtabs");
38529 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38530
38531 // set up the UpdateManager
38532 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38533 var updater = tab2.getUpdateManager();
38534 updater.setDefaultUrl("ajax1.htm");
38535 tab2.on('activate', updater.refresh, updater, true);
38536
38537 // Use setUrl for Ajax loading
38538 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38539 tab3.setUrl("ajax2.htm", null, true);
38540
38541 // Disabled tab
38542 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38543 tab4.disable();
38544
38545 jtabs.activate("jtabs-1");
38546  * </code></pre>
38547  * @constructor
38548  * Create a new TabPanel.
38549  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38550  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38551  */
38552 Roo.bootstrap.panel.Tabs = function(config){
38553     /**
38554     * The container element for this TabPanel.
38555     * @type Roo.Element
38556     */
38557     this.el = Roo.get(config.el);
38558     delete config.el;
38559     if(config){
38560         if(typeof config == "boolean"){
38561             this.tabPosition = config ? "bottom" : "top";
38562         }else{
38563             Roo.apply(this, config);
38564         }
38565     }
38566     
38567     if(this.tabPosition == "bottom"){
38568         // if tabs are at the bottom = create the body first.
38569         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38570         this.el.addClass("roo-tabs-bottom");
38571     }
38572     // next create the tabs holders
38573     
38574     if (this.tabPosition == "west"){
38575         
38576         var reg = this.region; // fake it..
38577         while (reg) {
38578             if (!reg.mgr.parent) {
38579                 break;
38580             }
38581             reg = reg.mgr.parent.region;
38582         }
38583         Roo.log("got nest?");
38584         Roo.log(reg);
38585         if (reg.mgr.getRegion('west')) {
38586             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38587             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38588             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38589             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38590             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38591         
38592             
38593         }
38594         
38595         
38596     } else {
38597      
38598         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38599         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38600         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38601         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38602     }
38603     
38604     
38605     if(Roo.isIE){
38606         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38607     }
38608     
38609     // finally - if tabs are at the top, then create the body last..
38610     if(this.tabPosition != "bottom"){
38611         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38612          * @type Roo.Element
38613          */
38614         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38615         this.el.addClass("roo-tabs-top");
38616     }
38617     this.items = [];
38618
38619     this.bodyEl.setStyle("position", "relative");
38620
38621     this.active = null;
38622     this.activateDelegate = this.activate.createDelegate(this);
38623
38624     this.addEvents({
38625         /**
38626          * @event tabchange
38627          * Fires when the active tab changes
38628          * @param {Roo.TabPanel} this
38629          * @param {Roo.TabPanelItem} activePanel The new active tab
38630          */
38631         "tabchange": true,
38632         /**
38633          * @event beforetabchange
38634          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38635          * @param {Roo.TabPanel} this
38636          * @param {Object} e Set cancel to true on this object to cancel the tab change
38637          * @param {Roo.TabPanelItem} tab The tab being changed to
38638          */
38639         "beforetabchange" : true
38640     });
38641
38642     Roo.EventManager.onWindowResize(this.onResize, this);
38643     this.cpad = this.el.getPadding("lr");
38644     this.hiddenCount = 0;
38645
38646
38647     // toolbar on the tabbar support...
38648     if (this.toolbar) {
38649         alert("no toolbar support yet");
38650         this.toolbar  = false;
38651         /*
38652         var tcfg = this.toolbar;
38653         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38654         this.toolbar = new Roo.Toolbar(tcfg);
38655         if (Roo.isSafari) {
38656             var tbl = tcfg.container.child('table', true);
38657             tbl.setAttribute('width', '100%');
38658         }
38659         */
38660         
38661     }
38662    
38663
38664
38665     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38666 };
38667
38668 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38669     /*
38670      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38671      */
38672     tabPosition : "top",
38673     /*
38674      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38675      */
38676     currentTabWidth : 0,
38677     /*
38678      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38679      */
38680     minTabWidth : 40,
38681     /*
38682      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38683      */
38684     maxTabWidth : 250,
38685     /*
38686      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38687      */
38688     preferredTabWidth : 175,
38689     /*
38690      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38691      */
38692     resizeTabs : false,
38693     /*
38694      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38695      */
38696     monitorResize : true,
38697     /*
38698      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38699      */
38700     toolbar : false,  // set by caller..
38701     
38702     region : false, /// set by caller
38703     
38704     disableTooltips : true, // not used yet...
38705
38706     /**
38707      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38708      * @param {String} id The id of the div to use <b>or create</b>
38709      * @param {String} text The text for the tab
38710      * @param {String} content (optional) Content to put in the TabPanelItem body
38711      * @param {Boolean} closable (optional) True to create a close icon on the tab
38712      * @return {Roo.TabPanelItem} The created TabPanelItem
38713      */
38714     addTab : function(id, text, content, closable, tpl)
38715     {
38716         var item = new Roo.bootstrap.panel.TabItem({
38717             panel: this,
38718             id : id,
38719             text : text,
38720             closable : closable,
38721             tpl : tpl
38722         });
38723         this.addTabItem(item);
38724         if(content){
38725             item.setContent(content);
38726         }
38727         return item;
38728     },
38729
38730     /**
38731      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38732      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38733      * @return {Roo.TabPanelItem}
38734      */
38735     getTab : function(id){
38736         return this.items[id];
38737     },
38738
38739     /**
38740      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38741      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38742      */
38743     hideTab : function(id){
38744         var t = this.items[id];
38745         if(!t.isHidden()){
38746            t.setHidden(true);
38747            this.hiddenCount++;
38748            this.autoSizeTabs();
38749         }
38750     },
38751
38752     /**
38753      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38754      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38755      */
38756     unhideTab : function(id){
38757         var t = this.items[id];
38758         if(t.isHidden()){
38759            t.setHidden(false);
38760            this.hiddenCount--;
38761            this.autoSizeTabs();
38762         }
38763     },
38764
38765     /**
38766      * Adds an existing {@link Roo.TabPanelItem}.
38767      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38768      */
38769     addTabItem : function(item)
38770     {
38771         this.items[item.id] = item;
38772         this.items.push(item);
38773         this.autoSizeTabs();
38774       //  if(this.resizeTabs){
38775     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38776   //         this.autoSizeTabs();
38777 //        }else{
38778 //            item.autoSize();
38779        // }
38780     },
38781
38782     /**
38783      * Removes a {@link Roo.TabPanelItem}.
38784      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38785      */
38786     removeTab : function(id){
38787         var items = this.items;
38788         var tab = items[id];
38789         if(!tab) { return; }
38790         var index = items.indexOf(tab);
38791         if(this.active == tab && items.length > 1){
38792             var newTab = this.getNextAvailable(index);
38793             if(newTab) {
38794                 newTab.activate();
38795             }
38796         }
38797         this.stripEl.dom.removeChild(tab.pnode.dom);
38798         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38799             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38800         }
38801         items.splice(index, 1);
38802         delete this.items[tab.id];
38803         tab.fireEvent("close", tab);
38804         tab.purgeListeners();
38805         this.autoSizeTabs();
38806     },
38807
38808     getNextAvailable : function(start){
38809         var items = this.items;
38810         var index = start;
38811         // look for a next tab that will slide over to
38812         // replace the one being removed
38813         while(index < items.length){
38814             var item = items[++index];
38815             if(item && !item.isHidden()){
38816                 return item;
38817             }
38818         }
38819         // if one isn't found select the previous tab (on the left)
38820         index = start;
38821         while(index >= 0){
38822             var item = items[--index];
38823             if(item && !item.isHidden()){
38824                 return item;
38825             }
38826         }
38827         return null;
38828     },
38829
38830     /**
38831      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38832      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38833      */
38834     disableTab : function(id){
38835         var tab = this.items[id];
38836         if(tab && this.active != tab){
38837             tab.disable();
38838         }
38839     },
38840
38841     /**
38842      * Enables a {@link Roo.TabPanelItem} that is disabled.
38843      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38844      */
38845     enableTab : function(id){
38846         var tab = this.items[id];
38847         tab.enable();
38848     },
38849
38850     /**
38851      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38852      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38853      * @return {Roo.TabPanelItem} The TabPanelItem.
38854      */
38855     activate : function(id)
38856     {
38857         //Roo.log('activite:'  + id);
38858         
38859         var tab = this.items[id];
38860         if(!tab){
38861             return null;
38862         }
38863         if(tab == this.active || tab.disabled){
38864             return tab;
38865         }
38866         var e = {};
38867         this.fireEvent("beforetabchange", this, e, tab);
38868         if(e.cancel !== true && !tab.disabled){
38869             if(this.active){
38870                 this.active.hide();
38871             }
38872             this.active = this.items[id];
38873             this.active.show();
38874             this.fireEvent("tabchange", this, this.active);
38875         }
38876         return tab;
38877     },
38878
38879     /**
38880      * Gets the active {@link Roo.TabPanelItem}.
38881      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38882      */
38883     getActiveTab : function(){
38884         return this.active;
38885     },
38886
38887     /**
38888      * Updates the tab body element to fit the height of the container element
38889      * for overflow scrolling
38890      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38891      */
38892     syncHeight : function(targetHeight){
38893         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38894         var bm = this.bodyEl.getMargins();
38895         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38896         this.bodyEl.setHeight(newHeight);
38897         return newHeight;
38898     },
38899
38900     onResize : function(){
38901         if(this.monitorResize){
38902             this.autoSizeTabs();
38903         }
38904     },
38905
38906     /**
38907      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38908      */
38909     beginUpdate : function(){
38910         this.updating = true;
38911     },
38912
38913     /**
38914      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38915      */
38916     endUpdate : function(){
38917         this.updating = false;
38918         this.autoSizeTabs();
38919     },
38920
38921     /**
38922      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38923      */
38924     autoSizeTabs : function()
38925     {
38926         var count = this.items.length;
38927         var vcount = count - this.hiddenCount;
38928         
38929         if (vcount < 2) {
38930             this.stripEl.hide();
38931         } else {
38932             this.stripEl.show();
38933         }
38934         
38935         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38936             return;
38937         }
38938         
38939         
38940         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38941         var availWidth = Math.floor(w / vcount);
38942         var b = this.stripBody;
38943         if(b.getWidth() > w){
38944             var tabs = this.items;
38945             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38946             if(availWidth < this.minTabWidth){
38947                 /*if(!this.sleft){    // incomplete scrolling code
38948                     this.createScrollButtons();
38949                 }
38950                 this.showScroll();
38951                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38952             }
38953         }else{
38954             if(this.currentTabWidth < this.preferredTabWidth){
38955                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38956             }
38957         }
38958     },
38959
38960     /**
38961      * Returns the number of tabs in this TabPanel.
38962      * @return {Number}
38963      */
38964      getCount : function(){
38965          return this.items.length;
38966      },
38967
38968     /**
38969      * Resizes all the tabs to the passed width
38970      * @param {Number} The new width
38971      */
38972     setTabWidth : function(width){
38973         this.currentTabWidth = width;
38974         for(var i = 0, len = this.items.length; i < len; i++) {
38975                 if(!this.items[i].isHidden()) {
38976                 this.items[i].setWidth(width);
38977             }
38978         }
38979     },
38980
38981     /**
38982      * Destroys this TabPanel
38983      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38984      */
38985     destroy : function(removeEl){
38986         Roo.EventManager.removeResizeListener(this.onResize, this);
38987         for(var i = 0, len = this.items.length; i < len; i++){
38988             this.items[i].purgeListeners();
38989         }
38990         if(removeEl === true){
38991             this.el.update("");
38992             this.el.remove();
38993         }
38994     },
38995     
38996     createStrip : function(container)
38997     {
38998         var strip = document.createElement("nav");
38999         strip.className = Roo.bootstrap.version == 4 ?
39000             "navbar-light bg-light" : 
39001             "navbar navbar-default"; //"x-tabs-wrap";
39002         container.appendChild(strip);
39003         return strip;
39004     },
39005     
39006     createStripList : function(strip)
39007     {
39008         // div wrapper for retard IE
39009         // returns the "tr" element.
39010         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39011         //'<div class="x-tabs-strip-wrap">'+
39012           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39013           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39014         return strip.firstChild; //.firstChild.firstChild.firstChild;
39015     },
39016     createBody : function(container)
39017     {
39018         var body = document.createElement("div");
39019         Roo.id(body, "tab-body");
39020         //Roo.fly(body).addClass("x-tabs-body");
39021         Roo.fly(body).addClass("tab-content");
39022         container.appendChild(body);
39023         return body;
39024     },
39025     createItemBody :function(bodyEl, id){
39026         var body = Roo.getDom(id);
39027         if(!body){
39028             body = document.createElement("div");
39029             body.id = id;
39030         }
39031         //Roo.fly(body).addClass("x-tabs-item-body");
39032         Roo.fly(body).addClass("tab-pane");
39033          bodyEl.insertBefore(body, bodyEl.firstChild);
39034         return body;
39035     },
39036     /** @private */
39037     createStripElements :  function(stripEl, text, closable, tpl)
39038     {
39039         var td = document.createElement("li"); // was td..
39040         td.className = 'nav-item';
39041         
39042         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39043         
39044         
39045         stripEl.appendChild(td);
39046         /*if(closable){
39047             td.className = "x-tabs-closable";
39048             if(!this.closeTpl){
39049                 this.closeTpl = new Roo.Template(
39050                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39051                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39052                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39053                 );
39054             }
39055             var el = this.closeTpl.overwrite(td, {"text": text});
39056             var close = el.getElementsByTagName("div")[0];
39057             var inner = el.getElementsByTagName("em")[0];
39058             return {"el": el, "close": close, "inner": inner};
39059         } else {
39060         */
39061         // not sure what this is..
39062 //            if(!this.tabTpl){
39063                 //this.tabTpl = new Roo.Template(
39064                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39065                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39066                 //);
39067 //                this.tabTpl = new Roo.Template(
39068 //                   '<a href="#">' +
39069 //                   '<span unselectable="on"' +
39070 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39071 //                            ' >{text}</span></a>'
39072 //                );
39073 //                
39074 //            }
39075
39076
39077             var template = tpl || this.tabTpl || false;
39078             
39079             if(!template){
39080                 template =  new Roo.Template(
39081                         Roo.bootstrap.version == 4 ? 
39082                             (
39083                                 '<a class="nav-link" href="#" unselectable="on"' +
39084                                      (this.disableTooltips ? '' : ' title="{text}"') +
39085                                      ' >{text}</a>'
39086                             ) : (
39087                                 '<a class="nav-link" href="#">' +
39088                                 '<span unselectable="on"' +
39089                                          (this.disableTooltips ? '' : ' title="{text}"') +
39090                                     ' >{text}</span></a>'
39091                             )
39092                 );
39093             }
39094             
39095             switch (typeof(template)) {
39096                 case 'object' :
39097                     break;
39098                 case 'string' :
39099                     template = new Roo.Template(template);
39100                     break;
39101                 default :
39102                     break;
39103             }
39104             
39105             var el = template.overwrite(td, {"text": text});
39106             
39107             var inner = el.getElementsByTagName("span")[0];
39108             
39109             return {"el": el, "inner": inner};
39110             
39111     }
39112         
39113     
39114 });
39115
39116 /**
39117  * @class Roo.TabPanelItem
39118  * @extends Roo.util.Observable
39119  * Represents an individual item (tab plus body) in a TabPanel.
39120  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39121  * @param {String} id The id of this TabPanelItem
39122  * @param {String} text The text for the tab of this TabPanelItem
39123  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39124  */
39125 Roo.bootstrap.panel.TabItem = function(config){
39126     /**
39127      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39128      * @type Roo.TabPanel
39129      */
39130     this.tabPanel = config.panel;
39131     /**
39132      * The id for this TabPanelItem
39133      * @type String
39134      */
39135     this.id = config.id;
39136     /** @private */
39137     this.disabled = false;
39138     /** @private */
39139     this.text = config.text;
39140     /** @private */
39141     this.loaded = false;
39142     this.closable = config.closable;
39143
39144     /**
39145      * The body element for this TabPanelItem.
39146      * @type Roo.Element
39147      */
39148     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39149     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39150     this.bodyEl.setStyle("display", "block");
39151     this.bodyEl.setStyle("zoom", "1");
39152     //this.hideAction();
39153
39154     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39155     /** @private */
39156     this.el = Roo.get(els.el);
39157     this.inner = Roo.get(els.inner, true);
39158      this.textEl = Roo.bootstrap.version == 4 ?
39159         this.el : Roo.get(this.el.dom.firstChild, true);
39160
39161     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39162     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39163
39164     
39165 //    this.el.on("mousedown", this.onTabMouseDown, this);
39166     this.el.on("click", this.onTabClick, this);
39167     /** @private */
39168     if(config.closable){
39169         var c = Roo.get(els.close, true);
39170         c.dom.title = this.closeText;
39171         c.addClassOnOver("close-over");
39172         c.on("click", this.closeClick, this);
39173      }
39174
39175     this.addEvents({
39176          /**
39177          * @event activate
39178          * Fires when this tab becomes the active tab.
39179          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39180          * @param {Roo.TabPanelItem} this
39181          */
39182         "activate": true,
39183         /**
39184          * @event beforeclose
39185          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39186          * @param {Roo.TabPanelItem} this
39187          * @param {Object} e Set cancel to true on this object to cancel the close.
39188          */
39189         "beforeclose": true,
39190         /**
39191          * @event close
39192          * Fires when this tab is closed.
39193          * @param {Roo.TabPanelItem} this
39194          */
39195          "close": true,
39196         /**
39197          * @event deactivate
39198          * Fires when this tab is no longer the active tab.
39199          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39200          * @param {Roo.TabPanelItem} this
39201          */
39202          "deactivate" : true
39203     });
39204     this.hidden = false;
39205
39206     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39207 };
39208
39209 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39210            {
39211     purgeListeners : function(){
39212        Roo.util.Observable.prototype.purgeListeners.call(this);
39213        this.el.removeAllListeners();
39214     },
39215     /**
39216      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39217      */
39218     show : function(){
39219         this.status_node.addClass("active");
39220         this.showAction();
39221         if(Roo.isOpera){
39222             this.tabPanel.stripWrap.repaint();
39223         }
39224         this.fireEvent("activate", this.tabPanel, this);
39225     },
39226
39227     /**
39228      * Returns true if this tab is the active tab.
39229      * @return {Boolean}
39230      */
39231     isActive : function(){
39232         return this.tabPanel.getActiveTab() == this;
39233     },
39234
39235     /**
39236      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39237      */
39238     hide : function(){
39239         this.status_node.removeClass("active");
39240         this.hideAction();
39241         this.fireEvent("deactivate", this.tabPanel, this);
39242     },
39243
39244     hideAction : function(){
39245         this.bodyEl.hide();
39246         this.bodyEl.setStyle("position", "absolute");
39247         this.bodyEl.setLeft("-20000px");
39248         this.bodyEl.setTop("-20000px");
39249     },
39250
39251     showAction : function(){
39252         this.bodyEl.setStyle("position", "relative");
39253         this.bodyEl.setTop("");
39254         this.bodyEl.setLeft("");
39255         this.bodyEl.show();
39256     },
39257
39258     /**
39259      * Set the tooltip for the tab.
39260      * @param {String} tooltip The tab's tooltip
39261      */
39262     setTooltip : function(text){
39263         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39264             this.textEl.dom.qtip = text;
39265             this.textEl.dom.removeAttribute('title');
39266         }else{
39267             this.textEl.dom.title = text;
39268         }
39269     },
39270
39271     onTabClick : function(e){
39272         e.preventDefault();
39273         this.tabPanel.activate(this.id);
39274     },
39275
39276     onTabMouseDown : function(e){
39277         e.preventDefault();
39278         this.tabPanel.activate(this.id);
39279     },
39280 /*
39281     getWidth : function(){
39282         return this.inner.getWidth();
39283     },
39284
39285     setWidth : function(width){
39286         var iwidth = width - this.linode.getPadding("lr");
39287         this.inner.setWidth(iwidth);
39288         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39289         this.linode.setWidth(width);
39290     },
39291 */
39292     /**
39293      * Show or hide the tab
39294      * @param {Boolean} hidden True to hide or false to show.
39295      */
39296     setHidden : function(hidden){
39297         this.hidden = hidden;
39298         this.linode.setStyle("display", hidden ? "none" : "");
39299     },
39300
39301     /**
39302      * Returns true if this tab is "hidden"
39303      * @return {Boolean}
39304      */
39305     isHidden : function(){
39306         return this.hidden;
39307     },
39308
39309     /**
39310      * Returns the text for this tab
39311      * @return {String}
39312      */
39313     getText : function(){
39314         return this.text;
39315     },
39316     /*
39317     autoSize : function(){
39318         //this.el.beginMeasure();
39319         this.textEl.setWidth(1);
39320         /*
39321          *  #2804 [new] Tabs in Roojs
39322          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39323          */
39324         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39325         //this.el.endMeasure();
39326     //},
39327
39328     /**
39329      * Sets the text for the tab (Note: this also sets the tooltip text)
39330      * @param {String} text The tab's text and tooltip
39331      */
39332     setText : function(text){
39333         this.text = text;
39334         this.textEl.update(text);
39335         this.setTooltip(text);
39336         //if(!this.tabPanel.resizeTabs){
39337         //    this.autoSize();
39338         //}
39339     },
39340     /**
39341      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39342      */
39343     activate : function(){
39344         this.tabPanel.activate(this.id);
39345     },
39346
39347     /**
39348      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39349      */
39350     disable : function(){
39351         if(this.tabPanel.active != this){
39352             this.disabled = true;
39353             this.status_node.addClass("disabled");
39354         }
39355     },
39356
39357     /**
39358      * Enables this TabPanelItem if it was previously disabled.
39359      */
39360     enable : function(){
39361         this.disabled = false;
39362         this.status_node.removeClass("disabled");
39363     },
39364
39365     /**
39366      * Sets the content for this TabPanelItem.
39367      * @param {String} content The content
39368      * @param {Boolean} loadScripts true to look for and load scripts
39369      */
39370     setContent : function(content, loadScripts){
39371         this.bodyEl.update(content, loadScripts);
39372     },
39373
39374     /**
39375      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39376      * @return {Roo.UpdateManager} The UpdateManager
39377      */
39378     getUpdateManager : function(){
39379         return this.bodyEl.getUpdateManager();
39380     },
39381
39382     /**
39383      * Set a URL to be used to load the content for this TabPanelItem.
39384      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39385      * @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)
39386      * @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)
39387      * @return {Roo.UpdateManager} The UpdateManager
39388      */
39389     setUrl : function(url, params, loadOnce){
39390         if(this.refreshDelegate){
39391             this.un('activate', this.refreshDelegate);
39392         }
39393         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39394         this.on("activate", this.refreshDelegate);
39395         return this.bodyEl.getUpdateManager();
39396     },
39397
39398     /** @private */
39399     _handleRefresh : function(url, params, loadOnce){
39400         if(!loadOnce || !this.loaded){
39401             var updater = this.bodyEl.getUpdateManager();
39402             updater.update(url, params, this._setLoaded.createDelegate(this));
39403         }
39404     },
39405
39406     /**
39407      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39408      *   Will fail silently if the setUrl method has not been called.
39409      *   This does not activate the panel, just updates its content.
39410      */
39411     refresh : function(){
39412         if(this.refreshDelegate){
39413            this.loaded = false;
39414            this.refreshDelegate();
39415         }
39416     },
39417
39418     /** @private */
39419     _setLoaded : function(){
39420         this.loaded = true;
39421     },
39422
39423     /** @private */
39424     closeClick : function(e){
39425         var o = {};
39426         e.stopEvent();
39427         this.fireEvent("beforeclose", this, o);
39428         if(o.cancel !== true){
39429             this.tabPanel.removeTab(this.id);
39430         }
39431     },
39432     /**
39433      * The text displayed in the tooltip for the close icon.
39434      * @type String
39435      */
39436     closeText : "Close this tab"
39437 });
39438 /**
39439 *    This script refer to:
39440 *    Title: International Telephone Input
39441 *    Author: Jack O'Connor
39442 *    Code version:  v12.1.12
39443 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39444 **/
39445
39446 Roo.bootstrap.PhoneInputData = function() {
39447     var d = [
39448       [
39449         "Afghanistan (‫افغانستان‬‎)",
39450         "af",
39451         "93"
39452       ],
39453       [
39454         "Albania (Shqipëri)",
39455         "al",
39456         "355"
39457       ],
39458       [
39459         "Algeria (‫الجزائر‬‎)",
39460         "dz",
39461         "213"
39462       ],
39463       [
39464         "American Samoa",
39465         "as",
39466         "1684"
39467       ],
39468       [
39469         "Andorra",
39470         "ad",
39471         "376"
39472       ],
39473       [
39474         "Angola",
39475         "ao",
39476         "244"
39477       ],
39478       [
39479         "Anguilla",
39480         "ai",
39481         "1264"
39482       ],
39483       [
39484         "Antigua and Barbuda",
39485         "ag",
39486         "1268"
39487       ],
39488       [
39489         "Argentina",
39490         "ar",
39491         "54"
39492       ],
39493       [
39494         "Armenia (Հայաստան)",
39495         "am",
39496         "374"
39497       ],
39498       [
39499         "Aruba",
39500         "aw",
39501         "297"
39502       ],
39503       [
39504         "Australia",
39505         "au",
39506         "61",
39507         0
39508       ],
39509       [
39510         "Austria (Österreich)",
39511         "at",
39512         "43"
39513       ],
39514       [
39515         "Azerbaijan (Azərbaycan)",
39516         "az",
39517         "994"
39518       ],
39519       [
39520         "Bahamas",
39521         "bs",
39522         "1242"
39523       ],
39524       [
39525         "Bahrain (‫البحرين‬‎)",
39526         "bh",
39527         "973"
39528       ],
39529       [
39530         "Bangladesh (বাংলাদেশ)",
39531         "bd",
39532         "880"
39533       ],
39534       [
39535         "Barbados",
39536         "bb",
39537         "1246"
39538       ],
39539       [
39540         "Belarus (Беларусь)",
39541         "by",
39542         "375"
39543       ],
39544       [
39545         "Belgium (België)",
39546         "be",
39547         "32"
39548       ],
39549       [
39550         "Belize",
39551         "bz",
39552         "501"
39553       ],
39554       [
39555         "Benin (Bénin)",
39556         "bj",
39557         "229"
39558       ],
39559       [
39560         "Bermuda",
39561         "bm",
39562         "1441"
39563       ],
39564       [
39565         "Bhutan (འབྲུག)",
39566         "bt",
39567         "975"
39568       ],
39569       [
39570         "Bolivia",
39571         "bo",
39572         "591"
39573       ],
39574       [
39575         "Bosnia and Herzegovina (Босна и Херцеговина)",
39576         "ba",
39577         "387"
39578       ],
39579       [
39580         "Botswana",
39581         "bw",
39582         "267"
39583       ],
39584       [
39585         "Brazil (Brasil)",
39586         "br",
39587         "55"
39588       ],
39589       [
39590         "British Indian Ocean Territory",
39591         "io",
39592         "246"
39593       ],
39594       [
39595         "British Virgin Islands",
39596         "vg",
39597         "1284"
39598       ],
39599       [
39600         "Brunei",
39601         "bn",
39602         "673"
39603       ],
39604       [
39605         "Bulgaria (България)",
39606         "bg",
39607         "359"
39608       ],
39609       [
39610         "Burkina Faso",
39611         "bf",
39612         "226"
39613       ],
39614       [
39615         "Burundi (Uburundi)",
39616         "bi",
39617         "257"
39618       ],
39619       [
39620         "Cambodia (កម្ពុជា)",
39621         "kh",
39622         "855"
39623       ],
39624       [
39625         "Cameroon (Cameroun)",
39626         "cm",
39627         "237"
39628       ],
39629       [
39630         "Canada",
39631         "ca",
39632         "1",
39633         1,
39634         ["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"]
39635       ],
39636       [
39637         "Cape Verde (Kabu Verdi)",
39638         "cv",
39639         "238"
39640       ],
39641       [
39642         "Caribbean Netherlands",
39643         "bq",
39644         "599",
39645         1
39646       ],
39647       [
39648         "Cayman Islands",
39649         "ky",
39650         "1345"
39651       ],
39652       [
39653         "Central African Republic (République centrafricaine)",
39654         "cf",
39655         "236"
39656       ],
39657       [
39658         "Chad (Tchad)",
39659         "td",
39660         "235"
39661       ],
39662       [
39663         "Chile",
39664         "cl",
39665         "56"
39666       ],
39667       [
39668         "China (中国)",
39669         "cn",
39670         "86"
39671       ],
39672       [
39673         "Christmas Island",
39674         "cx",
39675         "61",
39676         2
39677       ],
39678       [
39679         "Cocos (Keeling) Islands",
39680         "cc",
39681         "61",
39682         1
39683       ],
39684       [
39685         "Colombia",
39686         "co",
39687         "57"
39688       ],
39689       [
39690         "Comoros (‫جزر القمر‬‎)",
39691         "km",
39692         "269"
39693       ],
39694       [
39695         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39696         "cd",
39697         "243"
39698       ],
39699       [
39700         "Congo (Republic) (Congo-Brazzaville)",
39701         "cg",
39702         "242"
39703       ],
39704       [
39705         "Cook Islands",
39706         "ck",
39707         "682"
39708       ],
39709       [
39710         "Costa Rica",
39711         "cr",
39712         "506"
39713       ],
39714       [
39715         "Côte d’Ivoire",
39716         "ci",
39717         "225"
39718       ],
39719       [
39720         "Croatia (Hrvatska)",
39721         "hr",
39722         "385"
39723       ],
39724       [
39725         "Cuba",
39726         "cu",
39727         "53"
39728       ],
39729       [
39730         "Curaçao",
39731         "cw",
39732         "599",
39733         0
39734       ],
39735       [
39736         "Cyprus (Κύπρος)",
39737         "cy",
39738         "357"
39739       ],
39740       [
39741         "Czech Republic (Česká republika)",
39742         "cz",
39743         "420"
39744       ],
39745       [
39746         "Denmark (Danmark)",
39747         "dk",
39748         "45"
39749       ],
39750       [
39751         "Djibouti",
39752         "dj",
39753         "253"
39754       ],
39755       [
39756         "Dominica",
39757         "dm",
39758         "1767"
39759       ],
39760       [
39761         "Dominican Republic (República Dominicana)",
39762         "do",
39763         "1",
39764         2,
39765         ["809", "829", "849"]
39766       ],
39767       [
39768         "Ecuador",
39769         "ec",
39770         "593"
39771       ],
39772       [
39773         "Egypt (‫مصر‬‎)",
39774         "eg",
39775         "20"
39776       ],
39777       [
39778         "El Salvador",
39779         "sv",
39780         "503"
39781       ],
39782       [
39783         "Equatorial Guinea (Guinea Ecuatorial)",
39784         "gq",
39785         "240"
39786       ],
39787       [
39788         "Eritrea",
39789         "er",
39790         "291"
39791       ],
39792       [
39793         "Estonia (Eesti)",
39794         "ee",
39795         "372"
39796       ],
39797       [
39798         "Ethiopia",
39799         "et",
39800         "251"
39801       ],
39802       [
39803         "Falkland Islands (Islas Malvinas)",
39804         "fk",
39805         "500"
39806       ],
39807       [
39808         "Faroe Islands (Føroyar)",
39809         "fo",
39810         "298"
39811       ],
39812       [
39813         "Fiji",
39814         "fj",
39815         "679"
39816       ],
39817       [
39818         "Finland (Suomi)",
39819         "fi",
39820         "358",
39821         0
39822       ],
39823       [
39824         "France",
39825         "fr",
39826         "33"
39827       ],
39828       [
39829         "French Guiana (Guyane française)",
39830         "gf",
39831         "594"
39832       ],
39833       [
39834         "French Polynesia (Polynésie française)",
39835         "pf",
39836         "689"
39837       ],
39838       [
39839         "Gabon",
39840         "ga",
39841         "241"
39842       ],
39843       [
39844         "Gambia",
39845         "gm",
39846         "220"
39847       ],
39848       [
39849         "Georgia (საქართველო)",
39850         "ge",
39851         "995"
39852       ],
39853       [
39854         "Germany (Deutschland)",
39855         "de",
39856         "49"
39857       ],
39858       [
39859         "Ghana (Gaana)",
39860         "gh",
39861         "233"
39862       ],
39863       [
39864         "Gibraltar",
39865         "gi",
39866         "350"
39867       ],
39868       [
39869         "Greece (Ελλάδα)",
39870         "gr",
39871         "30"
39872       ],
39873       [
39874         "Greenland (Kalaallit Nunaat)",
39875         "gl",
39876         "299"
39877       ],
39878       [
39879         "Grenada",
39880         "gd",
39881         "1473"
39882       ],
39883       [
39884         "Guadeloupe",
39885         "gp",
39886         "590",
39887         0
39888       ],
39889       [
39890         "Guam",
39891         "gu",
39892         "1671"
39893       ],
39894       [
39895         "Guatemala",
39896         "gt",
39897         "502"
39898       ],
39899       [
39900         "Guernsey",
39901         "gg",
39902         "44",
39903         1
39904       ],
39905       [
39906         "Guinea (Guinée)",
39907         "gn",
39908         "224"
39909       ],
39910       [
39911         "Guinea-Bissau (Guiné Bissau)",
39912         "gw",
39913         "245"
39914       ],
39915       [
39916         "Guyana",
39917         "gy",
39918         "592"
39919       ],
39920       [
39921         "Haiti",
39922         "ht",
39923         "509"
39924       ],
39925       [
39926         "Honduras",
39927         "hn",
39928         "504"
39929       ],
39930       [
39931         "Hong Kong (香港)",
39932         "hk",
39933         "852"
39934       ],
39935       [
39936         "Hungary (Magyarország)",
39937         "hu",
39938         "36"
39939       ],
39940       [
39941         "Iceland (Ísland)",
39942         "is",
39943         "354"
39944       ],
39945       [
39946         "India (भारत)",
39947         "in",
39948         "91"
39949       ],
39950       [
39951         "Indonesia",
39952         "id",
39953         "62"
39954       ],
39955       [
39956         "Iran (‫ایران‬‎)",
39957         "ir",
39958         "98"
39959       ],
39960       [
39961         "Iraq (‫العراق‬‎)",
39962         "iq",
39963         "964"
39964       ],
39965       [
39966         "Ireland",
39967         "ie",
39968         "353"
39969       ],
39970       [
39971         "Isle of Man",
39972         "im",
39973         "44",
39974         2
39975       ],
39976       [
39977         "Israel (‫ישראל‬‎)",
39978         "il",
39979         "972"
39980       ],
39981       [
39982         "Italy (Italia)",
39983         "it",
39984         "39",
39985         0
39986       ],
39987       [
39988         "Jamaica",
39989         "jm",
39990         "1876"
39991       ],
39992       [
39993         "Japan (日本)",
39994         "jp",
39995         "81"
39996       ],
39997       [
39998         "Jersey",
39999         "je",
40000         "44",
40001         3
40002       ],
40003       [
40004         "Jordan (‫الأردن‬‎)",
40005         "jo",
40006         "962"
40007       ],
40008       [
40009         "Kazakhstan (Казахстан)",
40010         "kz",
40011         "7",
40012         1
40013       ],
40014       [
40015         "Kenya",
40016         "ke",
40017         "254"
40018       ],
40019       [
40020         "Kiribati",
40021         "ki",
40022         "686"
40023       ],
40024       [
40025         "Kosovo",
40026         "xk",
40027         "383"
40028       ],
40029       [
40030         "Kuwait (‫الكويت‬‎)",
40031         "kw",
40032         "965"
40033       ],
40034       [
40035         "Kyrgyzstan (Кыргызстан)",
40036         "kg",
40037         "996"
40038       ],
40039       [
40040         "Laos (ລາວ)",
40041         "la",
40042         "856"
40043       ],
40044       [
40045         "Latvia (Latvija)",
40046         "lv",
40047         "371"
40048       ],
40049       [
40050         "Lebanon (‫لبنان‬‎)",
40051         "lb",
40052         "961"
40053       ],
40054       [
40055         "Lesotho",
40056         "ls",
40057         "266"
40058       ],
40059       [
40060         "Liberia",
40061         "lr",
40062         "231"
40063       ],
40064       [
40065         "Libya (‫ليبيا‬‎)",
40066         "ly",
40067         "218"
40068       ],
40069       [
40070         "Liechtenstein",
40071         "li",
40072         "423"
40073       ],
40074       [
40075         "Lithuania (Lietuva)",
40076         "lt",
40077         "370"
40078       ],
40079       [
40080         "Luxembourg",
40081         "lu",
40082         "352"
40083       ],
40084       [
40085         "Macau (澳門)",
40086         "mo",
40087         "853"
40088       ],
40089       [
40090         "Macedonia (FYROM) (Македонија)",
40091         "mk",
40092         "389"
40093       ],
40094       [
40095         "Madagascar (Madagasikara)",
40096         "mg",
40097         "261"
40098       ],
40099       [
40100         "Malawi",
40101         "mw",
40102         "265"
40103       ],
40104       [
40105         "Malaysia",
40106         "my",
40107         "60"
40108       ],
40109       [
40110         "Maldives",
40111         "mv",
40112         "960"
40113       ],
40114       [
40115         "Mali",
40116         "ml",
40117         "223"
40118       ],
40119       [
40120         "Malta",
40121         "mt",
40122         "356"
40123       ],
40124       [
40125         "Marshall Islands",
40126         "mh",
40127         "692"
40128       ],
40129       [
40130         "Martinique",
40131         "mq",
40132         "596"
40133       ],
40134       [
40135         "Mauritania (‫موريتانيا‬‎)",
40136         "mr",
40137         "222"
40138       ],
40139       [
40140         "Mauritius (Moris)",
40141         "mu",
40142         "230"
40143       ],
40144       [
40145         "Mayotte",
40146         "yt",
40147         "262",
40148         1
40149       ],
40150       [
40151         "Mexico (México)",
40152         "mx",
40153         "52"
40154       ],
40155       [
40156         "Micronesia",
40157         "fm",
40158         "691"
40159       ],
40160       [
40161         "Moldova (Republica Moldova)",
40162         "md",
40163         "373"
40164       ],
40165       [
40166         "Monaco",
40167         "mc",
40168         "377"
40169       ],
40170       [
40171         "Mongolia (Монгол)",
40172         "mn",
40173         "976"
40174       ],
40175       [
40176         "Montenegro (Crna Gora)",
40177         "me",
40178         "382"
40179       ],
40180       [
40181         "Montserrat",
40182         "ms",
40183         "1664"
40184       ],
40185       [
40186         "Morocco (‫المغرب‬‎)",
40187         "ma",
40188         "212",
40189         0
40190       ],
40191       [
40192         "Mozambique (Moçambique)",
40193         "mz",
40194         "258"
40195       ],
40196       [
40197         "Myanmar (Burma) (မြန်မာ)",
40198         "mm",
40199         "95"
40200       ],
40201       [
40202         "Namibia (Namibië)",
40203         "na",
40204         "264"
40205       ],
40206       [
40207         "Nauru",
40208         "nr",
40209         "674"
40210       ],
40211       [
40212         "Nepal (नेपाल)",
40213         "np",
40214         "977"
40215       ],
40216       [
40217         "Netherlands (Nederland)",
40218         "nl",
40219         "31"
40220       ],
40221       [
40222         "New Caledonia (Nouvelle-Calédonie)",
40223         "nc",
40224         "687"
40225       ],
40226       [
40227         "New Zealand",
40228         "nz",
40229         "64"
40230       ],
40231       [
40232         "Nicaragua",
40233         "ni",
40234         "505"
40235       ],
40236       [
40237         "Niger (Nijar)",
40238         "ne",
40239         "227"
40240       ],
40241       [
40242         "Nigeria",
40243         "ng",
40244         "234"
40245       ],
40246       [
40247         "Niue",
40248         "nu",
40249         "683"
40250       ],
40251       [
40252         "Norfolk Island",
40253         "nf",
40254         "672"
40255       ],
40256       [
40257         "North Korea (조선 민주주의 인민 공화국)",
40258         "kp",
40259         "850"
40260       ],
40261       [
40262         "Northern Mariana Islands",
40263         "mp",
40264         "1670"
40265       ],
40266       [
40267         "Norway (Norge)",
40268         "no",
40269         "47",
40270         0
40271       ],
40272       [
40273         "Oman (‫عُمان‬‎)",
40274         "om",
40275         "968"
40276       ],
40277       [
40278         "Pakistan (‫پاکستان‬‎)",
40279         "pk",
40280         "92"
40281       ],
40282       [
40283         "Palau",
40284         "pw",
40285         "680"
40286       ],
40287       [
40288         "Palestine (‫فلسطين‬‎)",
40289         "ps",
40290         "970"
40291       ],
40292       [
40293         "Panama (Panamá)",
40294         "pa",
40295         "507"
40296       ],
40297       [
40298         "Papua New Guinea",
40299         "pg",
40300         "675"
40301       ],
40302       [
40303         "Paraguay",
40304         "py",
40305         "595"
40306       ],
40307       [
40308         "Peru (Perú)",
40309         "pe",
40310         "51"
40311       ],
40312       [
40313         "Philippines",
40314         "ph",
40315         "63"
40316       ],
40317       [
40318         "Poland (Polska)",
40319         "pl",
40320         "48"
40321       ],
40322       [
40323         "Portugal",
40324         "pt",
40325         "351"
40326       ],
40327       [
40328         "Puerto Rico",
40329         "pr",
40330         "1",
40331         3,
40332         ["787", "939"]
40333       ],
40334       [
40335         "Qatar (‫قطر‬‎)",
40336         "qa",
40337         "974"
40338       ],
40339       [
40340         "Réunion (La Réunion)",
40341         "re",
40342         "262",
40343         0
40344       ],
40345       [
40346         "Romania (România)",
40347         "ro",
40348         "40"
40349       ],
40350       [
40351         "Russia (Россия)",
40352         "ru",
40353         "7",
40354         0
40355       ],
40356       [
40357         "Rwanda",
40358         "rw",
40359         "250"
40360       ],
40361       [
40362         "Saint Barthélemy",
40363         "bl",
40364         "590",
40365         1
40366       ],
40367       [
40368         "Saint Helena",
40369         "sh",
40370         "290"
40371       ],
40372       [
40373         "Saint Kitts and Nevis",
40374         "kn",
40375         "1869"
40376       ],
40377       [
40378         "Saint Lucia",
40379         "lc",
40380         "1758"
40381       ],
40382       [
40383         "Saint Martin (Saint-Martin (partie française))",
40384         "mf",
40385         "590",
40386         2
40387       ],
40388       [
40389         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40390         "pm",
40391         "508"
40392       ],
40393       [
40394         "Saint Vincent and the Grenadines",
40395         "vc",
40396         "1784"
40397       ],
40398       [
40399         "Samoa",
40400         "ws",
40401         "685"
40402       ],
40403       [
40404         "San Marino",
40405         "sm",
40406         "378"
40407       ],
40408       [
40409         "São Tomé and Príncipe (São Tomé e Príncipe)",
40410         "st",
40411         "239"
40412       ],
40413       [
40414         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40415         "sa",
40416         "966"
40417       ],
40418       [
40419         "Senegal (Sénégal)",
40420         "sn",
40421         "221"
40422       ],
40423       [
40424         "Serbia (Србија)",
40425         "rs",
40426         "381"
40427       ],
40428       [
40429         "Seychelles",
40430         "sc",
40431         "248"
40432       ],
40433       [
40434         "Sierra Leone",
40435         "sl",
40436         "232"
40437       ],
40438       [
40439         "Singapore",
40440         "sg",
40441         "65"
40442       ],
40443       [
40444         "Sint Maarten",
40445         "sx",
40446         "1721"
40447       ],
40448       [
40449         "Slovakia (Slovensko)",
40450         "sk",
40451         "421"
40452       ],
40453       [
40454         "Slovenia (Slovenija)",
40455         "si",
40456         "386"
40457       ],
40458       [
40459         "Solomon Islands",
40460         "sb",
40461         "677"
40462       ],
40463       [
40464         "Somalia (Soomaaliya)",
40465         "so",
40466         "252"
40467       ],
40468       [
40469         "South Africa",
40470         "za",
40471         "27"
40472       ],
40473       [
40474         "South Korea (대한민국)",
40475         "kr",
40476         "82"
40477       ],
40478       [
40479         "South Sudan (‫جنوب السودان‬‎)",
40480         "ss",
40481         "211"
40482       ],
40483       [
40484         "Spain (España)",
40485         "es",
40486         "34"
40487       ],
40488       [
40489         "Sri Lanka (ශ්‍රී ලංකාව)",
40490         "lk",
40491         "94"
40492       ],
40493       [
40494         "Sudan (‫السودان‬‎)",
40495         "sd",
40496         "249"
40497       ],
40498       [
40499         "Suriname",
40500         "sr",
40501         "597"
40502       ],
40503       [
40504         "Svalbard and Jan Mayen",
40505         "sj",
40506         "47",
40507         1
40508       ],
40509       [
40510         "Swaziland",
40511         "sz",
40512         "268"
40513       ],
40514       [
40515         "Sweden (Sverige)",
40516         "se",
40517         "46"
40518       ],
40519       [
40520         "Switzerland (Schweiz)",
40521         "ch",
40522         "41"
40523       ],
40524       [
40525         "Syria (‫سوريا‬‎)",
40526         "sy",
40527         "963"
40528       ],
40529       [
40530         "Taiwan (台灣)",
40531         "tw",
40532         "886"
40533       ],
40534       [
40535         "Tajikistan",
40536         "tj",
40537         "992"
40538       ],
40539       [
40540         "Tanzania",
40541         "tz",
40542         "255"
40543       ],
40544       [
40545         "Thailand (ไทย)",
40546         "th",
40547         "66"
40548       ],
40549       [
40550         "Timor-Leste",
40551         "tl",
40552         "670"
40553       ],
40554       [
40555         "Togo",
40556         "tg",
40557         "228"
40558       ],
40559       [
40560         "Tokelau",
40561         "tk",
40562         "690"
40563       ],
40564       [
40565         "Tonga",
40566         "to",
40567         "676"
40568       ],
40569       [
40570         "Trinidad and Tobago",
40571         "tt",
40572         "1868"
40573       ],
40574       [
40575         "Tunisia (‫تونس‬‎)",
40576         "tn",
40577         "216"
40578       ],
40579       [
40580         "Turkey (Türkiye)",
40581         "tr",
40582         "90"
40583       ],
40584       [
40585         "Turkmenistan",
40586         "tm",
40587         "993"
40588       ],
40589       [
40590         "Turks and Caicos Islands",
40591         "tc",
40592         "1649"
40593       ],
40594       [
40595         "Tuvalu",
40596         "tv",
40597         "688"
40598       ],
40599       [
40600         "U.S. Virgin Islands",
40601         "vi",
40602         "1340"
40603       ],
40604       [
40605         "Uganda",
40606         "ug",
40607         "256"
40608       ],
40609       [
40610         "Ukraine (Україна)",
40611         "ua",
40612         "380"
40613       ],
40614       [
40615         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40616         "ae",
40617         "971"
40618       ],
40619       [
40620         "United Kingdom",
40621         "gb",
40622         "44",
40623         0
40624       ],
40625       [
40626         "United States",
40627         "us",
40628         "1",
40629         0
40630       ],
40631       [
40632         "Uruguay",
40633         "uy",
40634         "598"
40635       ],
40636       [
40637         "Uzbekistan (Oʻzbekiston)",
40638         "uz",
40639         "998"
40640       ],
40641       [
40642         "Vanuatu",
40643         "vu",
40644         "678"
40645       ],
40646       [
40647         "Vatican City (Città del Vaticano)",
40648         "va",
40649         "39",
40650         1
40651       ],
40652       [
40653         "Venezuela",
40654         "ve",
40655         "58"
40656       ],
40657       [
40658         "Vietnam (Việt Nam)",
40659         "vn",
40660         "84"
40661       ],
40662       [
40663         "Wallis and Futuna (Wallis-et-Futuna)",
40664         "wf",
40665         "681"
40666       ],
40667       [
40668         "Western Sahara (‫الصحراء الغربية‬‎)",
40669         "eh",
40670         "212",
40671         1
40672       ],
40673       [
40674         "Yemen (‫اليمن‬‎)",
40675         "ye",
40676         "967"
40677       ],
40678       [
40679         "Zambia",
40680         "zm",
40681         "260"
40682       ],
40683       [
40684         "Zimbabwe",
40685         "zw",
40686         "263"
40687       ],
40688       [
40689         "Åland Islands",
40690         "ax",
40691         "358",
40692         1
40693       ]
40694   ];
40695   
40696   return d;
40697 }/**
40698 *    This script refer to:
40699 *    Title: International Telephone Input
40700 *    Author: Jack O'Connor
40701 *    Code version:  v12.1.12
40702 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40703 **/
40704
40705 /**
40706  * @class Roo.bootstrap.PhoneInput
40707  * @extends Roo.bootstrap.TriggerField
40708  * An input with International dial-code selection
40709  
40710  * @cfg {String} defaultDialCode default '+852'
40711  * @cfg {Array} preferedCountries default []
40712   
40713  * @constructor
40714  * Create a new PhoneInput.
40715  * @param {Object} config Configuration options
40716  */
40717
40718 Roo.bootstrap.PhoneInput = function(config) {
40719     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40720 };
40721
40722 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40723         
40724         listWidth: undefined,
40725         
40726         selectedClass: 'active',
40727         
40728         invalidClass : "has-warning",
40729         
40730         validClass: 'has-success',
40731         
40732         allowed: '0123456789',
40733         
40734         max_length: 15,
40735         
40736         /**
40737          * @cfg {String} defaultDialCode The default dial code when initializing the input
40738          */
40739         defaultDialCode: '+852',
40740         
40741         /**
40742          * @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
40743          */
40744         preferedCountries: false,
40745         
40746         getAutoCreate : function()
40747         {
40748             var data = Roo.bootstrap.PhoneInputData();
40749             var align = this.labelAlign || this.parentLabelAlign();
40750             var id = Roo.id();
40751             
40752             this.allCountries = [];
40753             this.dialCodeMapping = [];
40754             
40755             for (var i = 0; i < data.length; i++) {
40756               var c = data[i];
40757               this.allCountries[i] = {
40758                 name: c[0],
40759                 iso2: c[1],
40760                 dialCode: c[2],
40761                 priority: c[3] || 0,
40762                 areaCodes: c[4] || null
40763               };
40764               this.dialCodeMapping[c[2]] = {
40765                   name: c[0],
40766                   iso2: c[1],
40767                   priority: c[3] || 0,
40768                   areaCodes: c[4] || null
40769               };
40770             }
40771             
40772             var cfg = {
40773                 cls: 'form-group',
40774                 cn: []
40775             };
40776             
40777             var input =  {
40778                 tag: 'input',
40779                 id : id,
40780                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40781                 maxlength: this.max_length,
40782                 cls : 'form-control tel-input',
40783                 autocomplete: 'new-password'
40784             };
40785             
40786             var hiddenInput = {
40787                 tag: 'input',
40788                 type: 'hidden',
40789                 cls: 'hidden-tel-input'
40790             };
40791             
40792             if (this.name) {
40793                 hiddenInput.name = this.name;
40794             }
40795             
40796             if (this.disabled) {
40797                 input.disabled = true;
40798             }
40799             
40800             var flag_container = {
40801                 tag: 'div',
40802                 cls: 'flag-box',
40803                 cn: [
40804                     {
40805                         tag: 'div',
40806                         cls: 'flag'
40807                     },
40808                     {
40809                         tag: 'div',
40810                         cls: 'caret'
40811                     }
40812                 ]
40813             };
40814             
40815             var box = {
40816                 tag: 'div',
40817                 cls: this.hasFeedback ? 'has-feedback' : '',
40818                 cn: [
40819                     hiddenInput,
40820                     input,
40821                     {
40822                         tag: 'input',
40823                         cls: 'dial-code-holder',
40824                         disabled: true
40825                     }
40826                 ]
40827             };
40828             
40829             var container = {
40830                 cls: 'roo-select2-container input-group',
40831                 cn: [
40832                     flag_container,
40833                     box
40834                 ]
40835             };
40836             
40837             if (this.fieldLabel.length) {
40838                 var indicator = {
40839                     tag: 'i',
40840                     tooltip: 'This field is required'
40841                 };
40842                 
40843                 var label = {
40844                     tag: 'label',
40845                     'for':  id,
40846                     cls: 'control-label',
40847                     cn: []
40848                 };
40849                 
40850                 var label_text = {
40851                     tag: 'span',
40852                     html: this.fieldLabel
40853                 };
40854                 
40855                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40856                 label.cn = [
40857                     indicator,
40858                     label_text
40859                 ];
40860                 
40861                 if(this.indicatorpos == 'right') {
40862                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40863                     label.cn = [
40864                         label_text,
40865                         indicator
40866                     ];
40867                 }
40868                 
40869                 if(align == 'left') {
40870                     container = {
40871                         tag: 'div',
40872                         cn: [
40873                             container
40874                         ]
40875                     };
40876                     
40877                     if(this.labelWidth > 12){
40878                         label.style = "width: " + this.labelWidth + 'px';
40879                     }
40880                     if(this.labelWidth < 13 && this.labelmd == 0){
40881                         this.labelmd = this.labelWidth;
40882                     }
40883                     if(this.labellg > 0){
40884                         label.cls += ' col-lg-' + this.labellg;
40885                         input.cls += ' col-lg-' + (12 - this.labellg);
40886                     }
40887                     if(this.labelmd > 0){
40888                         label.cls += ' col-md-' + this.labelmd;
40889                         container.cls += ' col-md-' + (12 - this.labelmd);
40890                     }
40891                     if(this.labelsm > 0){
40892                         label.cls += ' col-sm-' + this.labelsm;
40893                         container.cls += ' col-sm-' + (12 - this.labelsm);
40894                     }
40895                     if(this.labelxs > 0){
40896                         label.cls += ' col-xs-' + this.labelxs;
40897                         container.cls += ' col-xs-' + (12 - this.labelxs);
40898                     }
40899                 }
40900             }
40901             
40902             cfg.cn = [
40903                 label,
40904                 container
40905             ];
40906             
40907             var settings = this;
40908             
40909             ['xs','sm','md','lg'].map(function(size){
40910                 if (settings[size]) {
40911                     cfg.cls += ' col-' + size + '-' + settings[size];
40912                 }
40913             });
40914             
40915             this.store = new Roo.data.Store({
40916                 proxy : new Roo.data.MemoryProxy({}),
40917                 reader : new Roo.data.JsonReader({
40918                     fields : [
40919                         {
40920                             'name' : 'name',
40921                             'type' : 'string'
40922                         },
40923                         {
40924                             'name' : 'iso2',
40925                             'type' : 'string'
40926                         },
40927                         {
40928                             'name' : 'dialCode',
40929                             'type' : 'string'
40930                         },
40931                         {
40932                             'name' : 'priority',
40933                             'type' : 'string'
40934                         },
40935                         {
40936                             'name' : 'areaCodes',
40937                             'type' : 'string'
40938                         }
40939                     ]
40940                 })
40941             });
40942             
40943             if(!this.preferedCountries) {
40944                 this.preferedCountries = [
40945                     'hk',
40946                     'gb',
40947                     'us'
40948                 ];
40949             }
40950             
40951             var p = this.preferedCountries.reverse();
40952             
40953             if(p) {
40954                 for (var i = 0; i < p.length; i++) {
40955                     for (var j = 0; j < this.allCountries.length; j++) {
40956                         if(this.allCountries[j].iso2 == p[i]) {
40957                             var t = this.allCountries[j];
40958                             this.allCountries.splice(j,1);
40959                             this.allCountries.unshift(t);
40960                         }
40961                     } 
40962                 }
40963             }
40964             
40965             this.store.proxy.data = {
40966                 success: true,
40967                 data: this.allCountries
40968             };
40969             
40970             return cfg;
40971         },
40972         
40973         initEvents : function()
40974         {
40975             this.createList();
40976             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40977             
40978             this.indicator = this.indicatorEl();
40979             this.flag = this.flagEl();
40980             this.dialCodeHolder = this.dialCodeHolderEl();
40981             
40982             this.trigger = this.el.select('div.flag-box',true).first();
40983             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40984             
40985             var _this = this;
40986             
40987             (function(){
40988                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40989                 _this.list.setWidth(lw);
40990             }).defer(100);
40991             
40992             this.list.on('mouseover', this.onViewOver, this);
40993             this.list.on('mousemove', this.onViewMove, this);
40994             this.inputEl().on("keyup", this.onKeyUp, this);
40995             this.inputEl().on("keypress", this.onKeyPress, this);
40996             
40997             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40998
40999             this.view = new Roo.View(this.list, this.tpl, {
41000                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41001             });
41002             
41003             this.view.on('click', this.onViewClick, this);
41004             this.setValue(this.defaultDialCode);
41005         },
41006         
41007         onTriggerClick : function(e)
41008         {
41009             Roo.log('trigger click');
41010             if(this.disabled){
41011                 return;
41012             }
41013             
41014             if(this.isExpanded()){
41015                 this.collapse();
41016                 this.hasFocus = false;
41017             }else {
41018                 this.store.load({});
41019                 this.hasFocus = true;
41020                 this.expand();
41021             }
41022         },
41023         
41024         isExpanded : function()
41025         {
41026             return this.list.isVisible();
41027         },
41028         
41029         collapse : function()
41030         {
41031             if(!this.isExpanded()){
41032                 return;
41033             }
41034             this.list.hide();
41035             Roo.get(document).un('mousedown', this.collapseIf, this);
41036             Roo.get(document).un('mousewheel', this.collapseIf, this);
41037             this.fireEvent('collapse', this);
41038             this.validate();
41039         },
41040         
41041         expand : function()
41042         {
41043             Roo.log('expand');
41044
41045             if(this.isExpanded() || !this.hasFocus){
41046                 return;
41047             }
41048             
41049             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41050             this.list.setWidth(lw);
41051             
41052             this.list.show();
41053             this.restrictHeight();
41054             
41055             Roo.get(document).on('mousedown', this.collapseIf, this);
41056             Roo.get(document).on('mousewheel', this.collapseIf, this);
41057             
41058             this.fireEvent('expand', this);
41059         },
41060         
41061         restrictHeight : function()
41062         {
41063             this.list.alignTo(this.inputEl(), this.listAlign);
41064             this.list.alignTo(this.inputEl(), this.listAlign);
41065         },
41066         
41067         onViewOver : function(e, t)
41068         {
41069             if(this.inKeyMode){
41070                 return;
41071             }
41072             var item = this.view.findItemFromChild(t);
41073             
41074             if(item){
41075                 var index = this.view.indexOf(item);
41076                 this.select(index, false);
41077             }
41078         },
41079
41080         // private
41081         onViewClick : function(view, doFocus, el, e)
41082         {
41083             var index = this.view.getSelectedIndexes()[0];
41084             
41085             var r = this.store.getAt(index);
41086             
41087             if(r){
41088                 this.onSelect(r, index);
41089             }
41090             if(doFocus !== false && !this.blockFocus){
41091                 this.inputEl().focus();
41092             }
41093         },
41094         
41095         onViewMove : function(e, t)
41096         {
41097             this.inKeyMode = false;
41098         },
41099         
41100         select : function(index, scrollIntoView)
41101         {
41102             this.selectedIndex = index;
41103             this.view.select(index);
41104             if(scrollIntoView !== false){
41105                 var el = this.view.getNode(index);
41106                 if(el){
41107                     this.list.scrollChildIntoView(el, false);
41108                 }
41109             }
41110         },
41111         
41112         createList : function()
41113         {
41114             this.list = Roo.get(document.body).createChild({
41115                 tag: 'ul',
41116                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41117                 style: 'display:none'
41118             });
41119             
41120             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41121         },
41122         
41123         collapseIf : function(e)
41124         {
41125             var in_combo  = e.within(this.el);
41126             var in_list =  e.within(this.list);
41127             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41128             
41129             if (in_combo || in_list || is_list) {
41130                 return;
41131             }
41132             this.collapse();
41133         },
41134         
41135         onSelect : function(record, index)
41136         {
41137             if(this.fireEvent('beforeselect', this, record, index) !== false){
41138                 
41139                 this.setFlagClass(record.data.iso2);
41140                 this.setDialCode(record.data.dialCode);
41141                 this.hasFocus = false;
41142                 this.collapse();
41143                 this.fireEvent('select', this, record, index);
41144             }
41145         },
41146         
41147         flagEl : function()
41148         {
41149             var flag = this.el.select('div.flag',true).first();
41150             if(!flag){
41151                 return false;
41152             }
41153             return flag;
41154         },
41155         
41156         dialCodeHolderEl : function()
41157         {
41158             var d = this.el.select('input.dial-code-holder',true).first();
41159             if(!d){
41160                 return false;
41161             }
41162             return d;
41163         },
41164         
41165         setDialCode : function(v)
41166         {
41167             this.dialCodeHolder.dom.value = '+'+v;
41168         },
41169         
41170         setFlagClass : function(n)
41171         {
41172             this.flag.dom.className = 'flag '+n;
41173         },
41174         
41175         getValue : function()
41176         {
41177             var v = this.inputEl().getValue();
41178             if(this.dialCodeHolder) {
41179                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41180             }
41181             return v;
41182         },
41183         
41184         setValue : function(v)
41185         {
41186             var d = this.getDialCode(v);
41187             
41188             //invalid dial code
41189             if(v.length == 0 || !d || d.length == 0) {
41190                 if(this.rendered){
41191                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41192                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41193                 }
41194                 return;
41195             }
41196             
41197             //valid dial code
41198             this.setFlagClass(this.dialCodeMapping[d].iso2);
41199             this.setDialCode(d);
41200             this.inputEl().dom.value = v.replace('+'+d,'');
41201             this.hiddenEl().dom.value = this.getValue();
41202             
41203             this.validate();
41204         },
41205         
41206         getDialCode : function(v)
41207         {
41208             v = v ||  '';
41209             
41210             if (v.length == 0) {
41211                 return this.dialCodeHolder.dom.value;
41212             }
41213             
41214             var dialCode = "";
41215             if (v.charAt(0) != "+") {
41216                 return false;
41217             }
41218             var numericChars = "";
41219             for (var i = 1; i < v.length; i++) {
41220               var c = v.charAt(i);
41221               if (!isNaN(c)) {
41222                 numericChars += c;
41223                 if (this.dialCodeMapping[numericChars]) {
41224                   dialCode = v.substr(1, i);
41225                 }
41226                 if (numericChars.length == 4) {
41227                   break;
41228                 }
41229               }
41230             }
41231             return dialCode;
41232         },
41233         
41234         reset : function()
41235         {
41236             this.setValue(this.defaultDialCode);
41237             this.validate();
41238         },
41239         
41240         hiddenEl : function()
41241         {
41242             return this.el.select('input.hidden-tel-input',true).first();
41243         },
41244         
41245         // after setting val
41246         onKeyUp : function(e){
41247             this.setValue(this.getValue());
41248         },
41249         
41250         onKeyPress : function(e){
41251             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41252                 e.stopEvent();
41253             }
41254         }
41255         
41256 });
41257 /**
41258  * @class Roo.bootstrap.MoneyField
41259  * @extends Roo.bootstrap.ComboBox
41260  * Bootstrap MoneyField class
41261  * 
41262  * @constructor
41263  * Create a new MoneyField.
41264  * @param {Object} config Configuration options
41265  */
41266
41267 Roo.bootstrap.MoneyField = function(config) {
41268     
41269     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41270     
41271 };
41272
41273 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41274     
41275     /**
41276      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41277      */
41278     allowDecimals : true,
41279     /**
41280      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41281      */
41282     decimalSeparator : ".",
41283     /**
41284      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41285      */
41286     decimalPrecision : 0,
41287     /**
41288      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41289      */
41290     allowNegative : true,
41291     /**
41292      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41293      */
41294     allowZero: true,
41295     /**
41296      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41297      */
41298     minValue : Number.NEGATIVE_INFINITY,
41299     /**
41300      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41301      */
41302     maxValue : Number.MAX_VALUE,
41303     /**
41304      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41305      */
41306     minText : "The minimum value for this field is {0}",
41307     /**
41308      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41309      */
41310     maxText : "The maximum value for this field is {0}",
41311     /**
41312      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41313      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41314      */
41315     nanText : "{0} is not a valid number",
41316     /**
41317      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41318      */
41319     castInt : true,
41320     /**
41321      * @cfg {String} defaults currency of the MoneyField
41322      * value should be in lkey
41323      */
41324     defaultCurrency : false,
41325     /**
41326      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41327      */
41328     thousandsDelimiter : false,
41329     /**
41330      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41331      */
41332     max_length: false,
41333     
41334     inputlg : 9,
41335     inputmd : 9,
41336     inputsm : 9,
41337     inputxs : 6,
41338     
41339     store : false,
41340     
41341     getAutoCreate : function()
41342     {
41343         var align = this.labelAlign || this.parentLabelAlign();
41344         
41345         var id = Roo.id();
41346
41347         var cfg = {
41348             cls: 'form-group',
41349             cn: []
41350         };
41351
41352         var input =  {
41353             tag: 'input',
41354             id : id,
41355             cls : 'form-control roo-money-amount-input',
41356             autocomplete: 'new-password'
41357         };
41358         
41359         var hiddenInput = {
41360             tag: 'input',
41361             type: 'hidden',
41362             id: Roo.id(),
41363             cls: 'hidden-number-input'
41364         };
41365         
41366         if(this.max_length) {
41367             input.maxlength = this.max_length; 
41368         }
41369         
41370         if (this.name) {
41371             hiddenInput.name = this.name;
41372         }
41373
41374         if (this.disabled) {
41375             input.disabled = true;
41376         }
41377
41378         var clg = 12 - this.inputlg;
41379         var cmd = 12 - this.inputmd;
41380         var csm = 12 - this.inputsm;
41381         var cxs = 12 - this.inputxs;
41382         
41383         var container = {
41384             tag : 'div',
41385             cls : 'row roo-money-field',
41386             cn : [
41387                 {
41388                     tag : 'div',
41389                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41390                     cn : [
41391                         {
41392                             tag : 'div',
41393                             cls: 'roo-select2-container input-group',
41394                             cn: [
41395                                 {
41396                                     tag : 'input',
41397                                     cls : 'form-control roo-money-currency-input',
41398                                     autocomplete: 'new-password',
41399                                     readOnly : 1,
41400                                     name : this.currencyName
41401                                 },
41402                                 {
41403                                     tag :'span',
41404                                     cls : 'input-group-addon',
41405                                     cn : [
41406                                         {
41407                                             tag: 'span',
41408                                             cls: 'caret'
41409                                         }
41410                                     ]
41411                                 }
41412                             ]
41413                         }
41414                     ]
41415                 },
41416                 {
41417                     tag : 'div',
41418                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41419                     cn : [
41420                         {
41421                             tag: 'div',
41422                             cls: this.hasFeedback ? 'has-feedback' : '',
41423                             cn: [
41424                                 input
41425                             ]
41426                         }
41427                     ]
41428                 }
41429             ]
41430             
41431         };
41432         
41433         if (this.fieldLabel.length) {
41434             var indicator = {
41435                 tag: 'i',
41436                 tooltip: 'This field is required'
41437             };
41438
41439             var label = {
41440                 tag: 'label',
41441                 'for':  id,
41442                 cls: 'control-label',
41443                 cn: []
41444             };
41445
41446             var label_text = {
41447                 tag: 'span',
41448                 html: this.fieldLabel
41449             };
41450
41451             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41452             label.cn = [
41453                 indicator,
41454                 label_text
41455             ];
41456
41457             if(this.indicatorpos == 'right') {
41458                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41459                 label.cn = [
41460                     label_text,
41461                     indicator
41462                 ];
41463             }
41464
41465             if(align == 'left') {
41466                 container = {
41467                     tag: 'div',
41468                     cn: [
41469                         container
41470                     ]
41471                 };
41472
41473                 if(this.labelWidth > 12){
41474                     label.style = "width: " + this.labelWidth + 'px';
41475                 }
41476                 if(this.labelWidth < 13 && this.labelmd == 0){
41477                     this.labelmd = this.labelWidth;
41478                 }
41479                 if(this.labellg > 0){
41480                     label.cls += ' col-lg-' + this.labellg;
41481                     input.cls += ' col-lg-' + (12 - this.labellg);
41482                 }
41483                 if(this.labelmd > 0){
41484                     label.cls += ' col-md-' + this.labelmd;
41485                     container.cls += ' col-md-' + (12 - this.labelmd);
41486                 }
41487                 if(this.labelsm > 0){
41488                     label.cls += ' col-sm-' + this.labelsm;
41489                     container.cls += ' col-sm-' + (12 - this.labelsm);
41490                 }
41491                 if(this.labelxs > 0){
41492                     label.cls += ' col-xs-' + this.labelxs;
41493                     container.cls += ' col-xs-' + (12 - this.labelxs);
41494                 }
41495             }
41496         }
41497
41498         cfg.cn = [
41499             label,
41500             container,
41501             hiddenInput
41502         ];
41503         
41504         var settings = this;
41505
41506         ['xs','sm','md','lg'].map(function(size){
41507             if (settings[size]) {
41508                 cfg.cls += ' col-' + size + '-' + settings[size];
41509             }
41510         });
41511         
41512         return cfg;
41513     },
41514     
41515     initEvents : function()
41516     {
41517         this.indicator = this.indicatorEl();
41518         
41519         this.initCurrencyEvent();
41520         
41521         this.initNumberEvent();
41522     },
41523     
41524     initCurrencyEvent : function()
41525     {
41526         if (!this.store) {
41527             throw "can not find store for combo";
41528         }
41529         
41530         this.store = Roo.factory(this.store, Roo.data);
41531         this.store.parent = this;
41532         
41533         this.createList();
41534         
41535         this.triggerEl = this.el.select('.input-group-addon', true).first();
41536         
41537         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41538         
41539         var _this = this;
41540         
41541         (function(){
41542             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41543             _this.list.setWidth(lw);
41544         }).defer(100);
41545         
41546         this.list.on('mouseover', this.onViewOver, this);
41547         this.list.on('mousemove', this.onViewMove, this);
41548         this.list.on('scroll', this.onViewScroll, this);
41549         
41550         if(!this.tpl){
41551             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41552         }
41553         
41554         this.view = new Roo.View(this.list, this.tpl, {
41555             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41556         });
41557         
41558         this.view.on('click', this.onViewClick, this);
41559         
41560         this.store.on('beforeload', this.onBeforeLoad, this);
41561         this.store.on('load', this.onLoad, this);
41562         this.store.on('loadexception', this.onLoadException, this);
41563         
41564         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41565             "up" : function(e){
41566                 this.inKeyMode = true;
41567                 this.selectPrev();
41568             },
41569
41570             "down" : function(e){
41571                 if(!this.isExpanded()){
41572                     this.onTriggerClick();
41573                 }else{
41574                     this.inKeyMode = true;
41575                     this.selectNext();
41576                 }
41577             },
41578
41579             "enter" : function(e){
41580                 this.collapse();
41581                 
41582                 if(this.fireEvent("specialkey", this, e)){
41583                     this.onViewClick(false);
41584                 }
41585                 
41586                 return true;
41587             },
41588
41589             "esc" : function(e){
41590                 this.collapse();
41591             },
41592
41593             "tab" : function(e){
41594                 this.collapse();
41595                 
41596                 if(this.fireEvent("specialkey", this, e)){
41597                     this.onViewClick(false);
41598                 }
41599                 
41600                 return true;
41601             },
41602
41603             scope : this,
41604
41605             doRelay : function(foo, bar, hname){
41606                 if(hname == 'down' || this.scope.isExpanded()){
41607                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41608                 }
41609                 return true;
41610             },
41611
41612             forceKeyDown: true
41613         });
41614         
41615         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41616         
41617     },
41618     
41619     initNumberEvent : function(e)
41620     {
41621         this.inputEl().on("keydown" , this.fireKey,  this);
41622         this.inputEl().on("focus", this.onFocus,  this);
41623         this.inputEl().on("blur", this.onBlur,  this);
41624         
41625         this.inputEl().relayEvent('keyup', this);
41626         
41627         if(this.indicator){
41628             this.indicator.addClass('invisible');
41629         }
41630  
41631         this.originalValue = this.getValue();
41632         
41633         if(this.validationEvent == 'keyup'){
41634             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41635             this.inputEl().on('keyup', this.filterValidation, this);
41636         }
41637         else if(this.validationEvent !== false){
41638             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41639         }
41640         
41641         if(this.selectOnFocus){
41642             this.on("focus", this.preFocus, this);
41643             
41644         }
41645         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41646             this.inputEl().on("keypress", this.filterKeys, this);
41647         } else {
41648             this.inputEl().relayEvent('keypress', this);
41649         }
41650         
41651         var allowed = "0123456789";
41652         
41653         if(this.allowDecimals){
41654             allowed += this.decimalSeparator;
41655         }
41656         
41657         if(this.allowNegative){
41658             allowed += "-";
41659         }
41660         
41661         if(this.thousandsDelimiter) {
41662             allowed += ",";
41663         }
41664         
41665         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41666         
41667         var keyPress = function(e){
41668             
41669             var k = e.getKey();
41670             
41671             var c = e.getCharCode();
41672             
41673             if(
41674                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41675                     allowed.indexOf(String.fromCharCode(c)) === -1
41676             ){
41677                 e.stopEvent();
41678                 return;
41679             }
41680             
41681             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41682                 return;
41683             }
41684             
41685             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41686                 e.stopEvent();
41687             }
41688         };
41689         
41690         this.inputEl().on("keypress", keyPress, this);
41691         
41692     },
41693     
41694     onTriggerClick : function(e)
41695     {   
41696         if(this.disabled){
41697             return;
41698         }
41699         
41700         this.page = 0;
41701         this.loadNext = false;
41702         
41703         if(this.isExpanded()){
41704             this.collapse();
41705             return;
41706         }
41707         
41708         this.hasFocus = true;
41709         
41710         if(this.triggerAction == 'all') {
41711             this.doQuery(this.allQuery, true);
41712             return;
41713         }
41714         
41715         this.doQuery(this.getRawValue());
41716     },
41717     
41718     getCurrency : function()
41719     {   
41720         var v = this.currencyEl().getValue();
41721         
41722         return v;
41723     },
41724     
41725     restrictHeight : function()
41726     {
41727         this.list.alignTo(this.currencyEl(), this.listAlign);
41728         this.list.alignTo(this.currencyEl(), this.listAlign);
41729     },
41730     
41731     onViewClick : function(view, doFocus, el, e)
41732     {
41733         var index = this.view.getSelectedIndexes()[0];
41734         
41735         var r = this.store.getAt(index);
41736         
41737         if(r){
41738             this.onSelect(r, index);
41739         }
41740     },
41741     
41742     onSelect : function(record, index){
41743         
41744         if(this.fireEvent('beforeselect', this, record, index) !== false){
41745         
41746             this.setFromCurrencyData(index > -1 ? record.data : false);
41747             
41748             this.collapse();
41749             
41750             this.fireEvent('select', this, record, index);
41751         }
41752     },
41753     
41754     setFromCurrencyData : function(o)
41755     {
41756         var currency = '';
41757         
41758         this.lastCurrency = o;
41759         
41760         if (this.currencyField) {
41761             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41762         } else {
41763             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41764         }
41765         
41766         this.lastSelectionText = currency;
41767         
41768         //setting default currency
41769         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41770             this.setCurrency(this.defaultCurrency);
41771             return;
41772         }
41773         
41774         this.setCurrency(currency);
41775     },
41776     
41777     setFromData : function(o)
41778     {
41779         var c = {};
41780         
41781         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41782         
41783         this.setFromCurrencyData(c);
41784         
41785         var value = '';
41786         
41787         if (this.name) {
41788             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41789         } else {
41790             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41791         }
41792         
41793         this.setValue(value);
41794         
41795     },
41796     
41797     setCurrency : function(v)
41798     {   
41799         this.currencyValue = v;
41800         
41801         if(this.rendered){
41802             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41803             this.validate();
41804         }
41805     },
41806     
41807     setValue : function(v)
41808     {
41809         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41810         
41811         this.value = v;
41812         
41813         if(this.rendered){
41814             
41815             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41816             
41817             this.inputEl().dom.value = (v == '') ? '' :
41818                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41819             
41820             if(!this.allowZero && v === '0') {
41821                 this.hiddenEl().dom.value = '';
41822                 this.inputEl().dom.value = '';
41823             }
41824             
41825             this.validate();
41826         }
41827     },
41828     
41829     getRawValue : function()
41830     {
41831         var v = this.inputEl().getValue();
41832         
41833         return v;
41834     },
41835     
41836     getValue : function()
41837     {
41838         return this.fixPrecision(this.parseValue(this.getRawValue()));
41839     },
41840     
41841     parseValue : function(value)
41842     {
41843         if(this.thousandsDelimiter) {
41844             value += "";
41845             r = new RegExp(",", "g");
41846             value = value.replace(r, "");
41847         }
41848         
41849         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41850         return isNaN(value) ? '' : value;
41851         
41852     },
41853     
41854     fixPrecision : function(value)
41855     {
41856         if(this.thousandsDelimiter) {
41857             value += "";
41858             r = new RegExp(",", "g");
41859             value = value.replace(r, "");
41860         }
41861         
41862         var nan = isNaN(value);
41863         
41864         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41865             return nan ? '' : value;
41866         }
41867         return parseFloat(value).toFixed(this.decimalPrecision);
41868     },
41869     
41870     decimalPrecisionFcn : function(v)
41871     {
41872         return Math.floor(v);
41873     },
41874     
41875     validateValue : function(value)
41876     {
41877         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41878             return false;
41879         }
41880         
41881         var num = this.parseValue(value);
41882         
41883         if(isNaN(num)){
41884             this.markInvalid(String.format(this.nanText, value));
41885             return false;
41886         }
41887         
41888         if(num < this.minValue){
41889             this.markInvalid(String.format(this.minText, this.minValue));
41890             return false;
41891         }
41892         
41893         if(num > this.maxValue){
41894             this.markInvalid(String.format(this.maxText, this.maxValue));
41895             return false;
41896         }
41897         
41898         return true;
41899     },
41900     
41901     validate : function()
41902     {
41903         if(this.disabled || this.allowBlank){
41904             this.markValid();
41905             return true;
41906         }
41907         
41908         var currency = this.getCurrency();
41909         
41910         if(this.validateValue(this.getRawValue()) && currency.length){
41911             this.markValid();
41912             return true;
41913         }
41914         
41915         this.markInvalid();
41916         return false;
41917     },
41918     
41919     getName: function()
41920     {
41921         return this.name;
41922     },
41923     
41924     beforeBlur : function()
41925     {
41926         if(!this.castInt){
41927             return;
41928         }
41929         
41930         var v = this.parseValue(this.getRawValue());
41931         
41932         if(v || v == 0){
41933             this.setValue(v);
41934         }
41935     },
41936     
41937     onBlur : function()
41938     {
41939         this.beforeBlur();
41940         
41941         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41942             //this.el.removeClass(this.focusClass);
41943         }
41944         
41945         this.hasFocus = false;
41946         
41947         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41948             this.validate();
41949         }
41950         
41951         var v = this.getValue();
41952         
41953         if(String(v) !== String(this.startValue)){
41954             this.fireEvent('change', this, v, this.startValue);
41955         }
41956         
41957         this.fireEvent("blur", this);
41958     },
41959     
41960     inputEl : function()
41961     {
41962         return this.el.select('.roo-money-amount-input', true).first();
41963     },
41964     
41965     currencyEl : function()
41966     {
41967         return this.el.select('.roo-money-currency-input', true).first();
41968     },
41969     
41970     hiddenEl : function()
41971     {
41972         return this.el.select('input.hidden-number-input',true).first();
41973     }
41974     
41975 });/**
41976  * @class Roo.bootstrap.BezierSignature
41977  * @extends Roo.bootstrap.Component
41978  * Bootstrap BezierSignature class
41979  * This script refer to:
41980  *    Title: Signature Pad
41981  *    Author: szimek
41982  *    Availability: https://github.com/szimek/signature_pad
41983  *
41984  * @constructor
41985  * Create a new BezierSignature
41986  * @param {Object} config The config object
41987  */
41988
41989 Roo.bootstrap.BezierSignature = function(config){
41990     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41991     this.addEvents({
41992         "resize" : true
41993     });
41994 };
41995
41996 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41997 {
41998      
41999     curve_data: [],
42000     
42001     is_empty: true,
42002     
42003     mouse_btn_down: true,
42004     
42005     /**
42006      * @cfg {int} canvas height
42007      */
42008     canvas_height: '200px',
42009     
42010     /**
42011      * @cfg {float|function} Radius of a single dot.
42012      */ 
42013     dot_size: false,
42014     
42015     /**
42016      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42017      */
42018     min_width: 0.5,
42019     
42020     /**
42021      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42022      */
42023     max_width: 2.5,
42024     
42025     /**
42026      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42027      */
42028     throttle: 16,
42029     
42030     /**
42031      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42032      */
42033     min_distance: 5,
42034     
42035     /**
42036      * @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.
42037      */
42038     bg_color: 'rgba(0, 0, 0, 0)',
42039     
42040     /**
42041      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42042      */
42043     dot_color: 'black',
42044     
42045     /**
42046      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42047      */ 
42048     velocity_filter_weight: 0.7,
42049     
42050     /**
42051      * @cfg {function} Callback when stroke begin. 
42052      */
42053     onBegin: false,
42054     
42055     /**
42056      * @cfg {function} Callback when stroke end.
42057      */
42058     onEnd: false,
42059     
42060     getAutoCreate : function()
42061     {
42062         var cls = 'roo-signature column';
42063         
42064         if(this.cls){
42065             cls += ' ' + this.cls;
42066         }
42067         
42068         var col_sizes = [
42069             'lg',
42070             'md',
42071             'sm',
42072             'xs'
42073         ];
42074         
42075         for(var i = 0; i < col_sizes.length; i++) {
42076             if(this[col_sizes[i]]) {
42077                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42078             }
42079         }
42080         
42081         var cfg = {
42082             tag: 'div',
42083             cls: cls,
42084             cn: [
42085                 {
42086                     tag: 'div',
42087                     cls: 'roo-signature-body',
42088                     cn: [
42089                         {
42090                             tag: 'canvas',
42091                             cls: 'roo-signature-body-canvas',
42092                             height: this.canvas_height,
42093                             width: this.canvas_width
42094                         }
42095                     ]
42096                 },
42097                 {
42098                     tag: 'input',
42099                     type: 'file',
42100                     style: 'display: none'
42101                 }
42102             ]
42103         };
42104         
42105         return cfg;
42106     },
42107     
42108     initEvents: function() 
42109     {
42110         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42111         
42112         var canvas = this.canvasEl();
42113         
42114         // mouse && touch event swapping...
42115         canvas.dom.style.touchAction = 'none';
42116         canvas.dom.style.msTouchAction = 'none';
42117         
42118         this.mouse_btn_down = false;
42119         canvas.on('mousedown', this._handleMouseDown, this);
42120         canvas.on('mousemove', this._handleMouseMove, this);
42121         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42122         
42123         if (window.PointerEvent) {
42124             canvas.on('pointerdown', this._handleMouseDown, this);
42125             canvas.on('pointermove', this._handleMouseMove, this);
42126             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42127         }
42128         
42129         if ('ontouchstart' in window) {
42130             canvas.on('touchstart', this._handleTouchStart, this);
42131             canvas.on('touchmove', this._handleTouchMove, this);
42132             canvas.on('touchend', this._handleTouchEnd, this);
42133         }
42134         
42135         Roo.EventManager.onWindowResize(this.resize, this, true);
42136         
42137         // file input event
42138         this.fileEl().on('change', this.uploadImage, this);
42139         
42140         this.clear();
42141         
42142         this.resize();
42143     },
42144     
42145     resize: function(){
42146         
42147         var canvas = this.canvasEl().dom;
42148         var ctx = this.canvasElCtx();
42149         var img_data = false;
42150         
42151         if(canvas.width > 0) {
42152             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42153         }
42154         // setting canvas width will clean img data
42155         canvas.width = 0;
42156         
42157         var style = window.getComputedStyle ? 
42158             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42159             
42160         var padding_left = parseInt(style.paddingLeft) || 0;
42161         var padding_right = parseInt(style.paddingRight) || 0;
42162         
42163         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42164         
42165         if(img_data) {
42166             ctx.putImageData(img_data, 0, 0);
42167         }
42168     },
42169     
42170     _handleMouseDown: function(e)
42171     {
42172         if (e.browserEvent.which === 1) {
42173             this.mouse_btn_down = true;
42174             this.strokeBegin(e);
42175         }
42176     },
42177     
42178     _handleMouseMove: function (e)
42179     {
42180         if (this.mouse_btn_down) {
42181             this.strokeMoveUpdate(e);
42182         }
42183     },
42184     
42185     _handleMouseUp: function (e)
42186     {
42187         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42188             this.mouse_btn_down = false;
42189             this.strokeEnd(e);
42190         }
42191     },
42192     
42193     _handleTouchStart: function (e) {
42194         
42195         e.preventDefault();
42196         if (e.browserEvent.targetTouches.length === 1) {
42197             // var touch = e.browserEvent.changedTouches[0];
42198             // this.strokeBegin(touch);
42199             
42200              this.strokeBegin(e); // assume e catching the correct xy...
42201         }
42202     },
42203     
42204     _handleTouchMove: function (e) {
42205         e.preventDefault();
42206         // var touch = event.targetTouches[0];
42207         // _this._strokeMoveUpdate(touch);
42208         this.strokeMoveUpdate(e);
42209     },
42210     
42211     _handleTouchEnd: function (e) {
42212         var wasCanvasTouched = e.target === this.canvasEl().dom;
42213         if (wasCanvasTouched) {
42214             e.preventDefault();
42215             // var touch = event.changedTouches[0];
42216             // _this._strokeEnd(touch);
42217             this.strokeEnd(e);
42218         }
42219     },
42220     
42221     reset: function () {
42222         this._lastPoints = [];
42223         this._lastVelocity = 0;
42224         this._lastWidth = (this.min_width + this.max_width) / 2;
42225         this.canvasElCtx().fillStyle = this.dot_color;
42226     },
42227     
42228     strokeMoveUpdate: function(e)
42229     {
42230         this.strokeUpdate(e);
42231         
42232         if (this.throttle) {
42233             this.throttleStroke(this.strokeUpdate, this.throttle);
42234         }
42235         else {
42236             this.strokeUpdate(e);
42237         }
42238     },
42239     
42240     strokeBegin: function(e)
42241     {
42242         var newPointGroup = {
42243             color: this.dot_color,
42244             points: []
42245         };
42246         
42247         if (typeof this.onBegin === 'function') {
42248             this.onBegin(e);
42249         }
42250         
42251         this.curve_data.push(newPointGroup);
42252         this.reset();
42253         this.strokeUpdate(e);
42254     },
42255     
42256     strokeUpdate: function(e)
42257     {
42258         var rect = this.canvasEl().dom.getBoundingClientRect();
42259         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42260         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42261         var lastPoints = lastPointGroup.points;
42262         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42263         var isLastPointTooClose = lastPoint
42264             ? point.distanceTo(lastPoint) <= this.min_distance
42265             : false;
42266         var color = lastPointGroup.color;
42267         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42268             var curve = this.addPoint(point);
42269             if (!lastPoint) {
42270                 this.drawDot({color: color, point: point});
42271             }
42272             else if (curve) {
42273                 this.drawCurve({color: color, curve: curve});
42274             }
42275             lastPoints.push({
42276                 time: point.time,
42277                 x: point.x,
42278                 y: point.y
42279             });
42280         }
42281     },
42282     
42283     strokeEnd: function(e)
42284     {
42285         this.strokeUpdate(e);
42286         if (typeof this.onEnd === 'function') {
42287             this.onEnd(e);
42288         }
42289     },
42290     
42291     addPoint:  function (point) {
42292         var _lastPoints = this._lastPoints;
42293         _lastPoints.push(point);
42294         if (_lastPoints.length > 2) {
42295             if (_lastPoints.length === 3) {
42296                 _lastPoints.unshift(_lastPoints[0]);
42297             }
42298             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42299             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42300             _lastPoints.shift();
42301             return curve;
42302         }
42303         return null;
42304     },
42305     
42306     calculateCurveWidths: function (startPoint, endPoint) {
42307         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42308             (1 - this.velocity_filter_weight) * this._lastVelocity;
42309
42310         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42311         var widths = {
42312             end: newWidth,
42313             start: this._lastWidth
42314         };
42315         
42316         this._lastVelocity = velocity;
42317         this._lastWidth = newWidth;
42318         return widths;
42319     },
42320     
42321     drawDot: function (_a) {
42322         var color = _a.color, point = _a.point;
42323         var ctx = this.canvasElCtx();
42324         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42325         ctx.beginPath();
42326         this.drawCurveSegment(point.x, point.y, width);
42327         ctx.closePath();
42328         ctx.fillStyle = color;
42329         ctx.fill();
42330     },
42331     
42332     drawCurve: function (_a) {
42333         var color = _a.color, curve = _a.curve;
42334         var ctx = this.canvasElCtx();
42335         var widthDelta = curve.endWidth - curve.startWidth;
42336         var drawSteps = Math.floor(curve.length()) * 2;
42337         ctx.beginPath();
42338         ctx.fillStyle = color;
42339         for (var i = 0; i < drawSteps; i += 1) {
42340         var t = i / drawSteps;
42341         var tt = t * t;
42342         var ttt = tt * t;
42343         var u = 1 - t;
42344         var uu = u * u;
42345         var uuu = uu * u;
42346         var x = uuu * curve.startPoint.x;
42347         x += 3 * uu * t * curve.control1.x;
42348         x += 3 * u * tt * curve.control2.x;
42349         x += ttt * curve.endPoint.x;
42350         var y = uuu * curve.startPoint.y;
42351         y += 3 * uu * t * curve.control1.y;
42352         y += 3 * u * tt * curve.control2.y;
42353         y += ttt * curve.endPoint.y;
42354         var width = curve.startWidth + ttt * widthDelta;
42355         this.drawCurveSegment(x, y, width);
42356         }
42357         ctx.closePath();
42358         ctx.fill();
42359     },
42360     
42361     drawCurveSegment: function (x, y, width) {
42362         var ctx = this.canvasElCtx();
42363         ctx.moveTo(x, y);
42364         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42365         this.is_empty = false;
42366     },
42367     
42368     clear: function()
42369     {
42370         var ctx = this.canvasElCtx();
42371         var canvas = this.canvasEl().dom;
42372         ctx.fillStyle = this.bg_color;
42373         ctx.clearRect(0, 0, canvas.width, canvas.height);
42374         ctx.fillRect(0, 0, canvas.width, canvas.height);
42375         this.curve_data = [];
42376         this.reset();
42377         this.is_empty = true;
42378     },
42379     
42380     fileEl: function()
42381     {
42382         return  this.el.select('input',true).first();
42383     },
42384     
42385     canvasEl: function()
42386     {
42387         return this.el.select('canvas',true).first();
42388     },
42389     
42390     canvasElCtx: function()
42391     {
42392         return this.el.select('canvas',true).first().dom.getContext('2d');
42393     },
42394     
42395     getImage: function(type)
42396     {
42397         if(this.is_empty) {
42398             return false;
42399         }
42400         
42401         // encryption ?
42402         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42403     },
42404     
42405     drawFromImage: function(img_src)
42406     {
42407         var img = new Image();
42408         
42409         img.onload = function(){
42410             this.canvasElCtx().drawImage(img, 0, 0);
42411         }.bind(this);
42412         
42413         img.src = img_src;
42414         
42415         this.is_empty = false;
42416     },
42417     
42418     selectImage: function()
42419     {
42420         this.fileEl().dom.click();
42421     },
42422     
42423     uploadImage: function(e)
42424     {
42425         var reader = new FileReader();
42426         
42427         reader.onload = function(e){
42428             var img = new Image();
42429             img.onload = function(){
42430                 this.reset();
42431                 this.canvasElCtx().drawImage(img, 0, 0);
42432             }.bind(this);
42433             img.src = e.target.result;
42434         }.bind(this);
42435         
42436         reader.readAsDataURL(e.target.files[0]);
42437     },
42438     
42439     // Bezier Point Constructor
42440     Point: (function () {
42441         function Point(x, y, time) {
42442             this.x = x;
42443             this.y = y;
42444             this.time = time || Date.now();
42445         }
42446         Point.prototype.distanceTo = function (start) {
42447             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42448         };
42449         Point.prototype.equals = function (other) {
42450             return this.x === other.x && this.y === other.y && this.time === other.time;
42451         };
42452         Point.prototype.velocityFrom = function (start) {
42453             return this.time !== start.time
42454             ? this.distanceTo(start) / (this.time - start.time)
42455             : 0;
42456         };
42457         return Point;
42458     }()),
42459     
42460     
42461     // Bezier Constructor
42462     Bezier: (function () {
42463         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42464             this.startPoint = startPoint;
42465             this.control2 = control2;
42466             this.control1 = control1;
42467             this.endPoint = endPoint;
42468             this.startWidth = startWidth;
42469             this.endWidth = endWidth;
42470         }
42471         Bezier.fromPoints = function (points, widths, scope) {
42472             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42473             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42474             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42475         };
42476         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42477             var dx1 = s1.x - s2.x;
42478             var dy1 = s1.y - s2.y;
42479             var dx2 = s2.x - s3.x;
42480             var dy2 = s2.y - s3.y;
42481             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42482             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42483             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42484             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42485             var dxm = m1.x - m2.x;
42486             var dym = m1.y - m2.y;
42487             var k = l2 / (l1 + l2);
42488             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42489             var tx = s2.x - cm.x;
42490             var ty = s2.y - cm.y;
42491             return {
42492                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42493                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42494             };
42495         };
42496         Bezier.prototype.length = function () {
42497             var steps = 10;
42498             var length = 0;
42499             var px;
42500             var py;
42501             for (var i = 0; i <= steps; i += 1) {
42502                 var t = i / steps;
42503                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42504                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42505                 if (i > 0) {
42506                     var xdiff = cx - px;
42507                     var ydiff = cy - py;
42508                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42509                 }
42510                 px = cx;
42511                 py = cy;
42512             }
42513             return length;
42514         };
42515         Bezier.prototype.point = function (t, start, c1, c2, end) {
42516             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42517             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42518             + (3.0 * c2 * (1.0 - t) * t * t)
42519             + (end * t * t * t);
42520         };
42521         return Bezier;
42522     }()),
42523     
42524     throttleStroke: function(fn, wait) {
42525       if (wait === void 0) { wait = 250; }
42526       var previous = 0;
42527       var timeout = null;
42528       var result;
42529       var storedContext;
42530       var storedArgs;
42531       var later = function () {
42532           previous = Date.now();
42533           timeout = null;
42534           result = fn.apply(storedContext, storedArgs);
42535           if (!timeout) {
42536               storedContext = null;
42537               storedArgs = [];
42538           }
42539       };
42540       return function wrapper() {
42541           var args = [];
42542           for (var _i = 0; _i < arguments.length; _i++) {
42543               args[_i] = arguments[_i];
42544           }
42545           var now = Date.now();
42546           var remaining = wait - (now - previous);
42547           storedContext = this;
42548           storedArgs = args;
42549           if (remaining <= 0 || remaining > wait) {
42550               if (timeout) {
42551                   clearTimeout(timeout);
42552                   timeout = null;
42553               }
42554               previous = now;
42555               result = fn.apply(storedContext, storedArgs);
42556               if (!timeout) {
42557                   storedContext = null;
42558                   storedArgs = [];
42559               }
42560           }
42561           else if (!timeout) {
42562               timeout = window.setTimeout(later, remaining);
42563           }
42564           return result;
42565       };
42566   }
42567   
42568 });
42569
42570  
42571
42572