961210f1fd11856dd4b8cef5f6ce3fb4f81da958
[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 contenteditable="true" data-enable-grammerly="true" 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     initEvents : function ()
24843     {
24844         
24845     }
24846 });
24847 /**
24848  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24849  * @class Roo.bootstrap.Table.RowSelectionModel
24850  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24851  * It supports multiple selections and keyboard selection/navigation. 
24852  * @constructor
24853  * @param {Object} config
24854  */
24855
24856 Roo.bootstrap.Table.RowSelectionModel = function(config){
24857     Roo.apply(this, config);
24858     this.selections = new Roo.util.MixedCollection(false, function(o){
24859         return o.id;
24860     });
24861
24862     this.last = false;
24863     this.lastActive = false;
24864
24865     this.addEvents({
24866         /**
24867              * @event selectionchange
24868              * Fires when the selection changes
24869              * @param {SelectionModel} this
24870              */
24871             "selectionchange" : true,
24872         /**
24873              * @event afterselectionchange
24874              * Fires after the selection changes (eg. by key press or clicking)
24875              * @param {SelectionModel} this
24876              */
24877             "afterselectionchange" : true,
24878         /**
24879              * @event beforerowselect
24880              * Fires when a row is selected being selected, return false to cancel.
24881              * @param {SelectionModel} this
24882              * @param {Number} rowIndex The selected index
24883              * @param {Boolean} keepExisting False if other selections will be cleared
24884              */
24885             "beforerowselect" : true,
24886         /**
24887              * @event rowselect
24888              * Fires when a row is selected.
24889              * @param {SelectionModel} this
24890              * @param {Number} rowIndex The selected index
24891              * @param {Roo.data.Record} r The record
24892              */
24893             "rowselect" : true,
24894         /**
24895              * @event rowdeselect
24896              * Fires when a row is deselected.
24897              * @param {SelectionModel} this
24898              * @param {Number} rowIndex The selected index
24899              */
24900         "rowdeselect" : true
24901     });
24902     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24903     this.locked = false;
24904  };
24905
24906 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24907     /**
24908      * @cfg {Boolean} singleSelect
24909      * True to allow selection of only one row at a time (defaults to false)
24910      */
24911     singleSelect : false,
24912
24913     // private
24914     initEvents : function()
24915     {
24916
24917         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24918         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24919         //}else{ // allow click to work like normal
24920          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24921         //}
24922         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24923         this.grid.on("rowclick", this.handleMouseDown, this);
24924         
24925         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24926             "up" : function(e){
24927                 if(!e.shiftKey){
24928                     this.selectPrevious(e.shiftKey);
24929                 }else if(this.last !== false && this.lastActive !== false){
24930                     var last = this.last;
24931                     this.selectRange(this.last,  this.lastActive-1);
24932                     this.grid.getView().focusRow(this.lastActive);
24933                     if(last !== false){
24934                         this.last = last;
24935                     }
24936                 }else{
24937                     this.selectFirstRow();
24938                 }
24939                 this.fireEvent("afterselectionchange", this);
24940             },
24941             "down" : function(e){
24942                 if(!e.shiftKey){
24943                     this.selectNext(e.shiftKey);
24944                 }else if(this.last !== false && this.lastActive !== false){
24945                     var last = this.last;
24946                     this.selectRange(this.last,  this.lastActive+1);
24947                     this.grid.getView().focusRow(this.lastActive);
24948                     if(last !== false){
24949                         this.last = last;
24950                     }
24951                 }else{
24952                     this.selectFirstRow();
24953                 }
24954                 this.fireEvent("afterselectionchange", this);
24955             },
24956             scope: this
24957         });
24958         this.grid.store.on('load', function(){
24959             this.selections.clear();
24960         },this);
24961         /*
24962         var view = this.grid.view;
24963         view.on("refresh", this.onRefresh, this);
24964         view.on("rowupdated", this.onRowUpdated, this);
24965         view.on("rowremoved", this.onRemove, this);
24966         */
24967     },
24968
24969     // private
24970     onRefresh : function()
24971     {
24972         var ds = this.grid.store, i, v = this.grid.view;
24973         var s = this.selections;
24974         s.each(function(r){
24975             if((i = ds.indexOfId(r.id)) != -1){
24976                 v.onRowSelect(i);
24977             }else{
24978                 s.remove(r);
24979             }
24980         });
24981     },
24982
24983     // private
24984     onRemove : function(v, index, r){
24985         this.selections.remove(r);
24986     },
24987
24988     // private
24989     onRowUpdated : function(v, index, r){
24990         if(this.isSelected(r)){
24991             v.onRowSelect(index);
24992         }
24993     },
24994
24995     /**
24996      * Select records.
24997      * @param {Array} records The records to select
24998      * @param {Boolean} keepExisting (optional) True to keep existing selections
24999      */
25000     selectRecords : function(records, keepExisting)
25001     {
25002         if(!keepExisting){
25003             this.clearSelections();
25004         }
25005             var ds = this.grid.store;
25006         for(var i = 0, len = records.length; i < len; i++){
25007             this.selectRow(ds.indexOf(records[i]), true);
25008         }
25009     },
25010
25011     /**
25012      * Gets the number of selected rows.
25013      * @return {Number}
25014      */
25015     getCount : function(){
25016         return this.selections.length;
25017     },
25018
25019     /**
25020      * Selects the first row in the grid.
25021      */
25022     selectFirstRow : function(){
25023         this.selectRow(0);
25024     },
25025
25026     /**
25027      * Select the last row.
25028      * @param {Boolean} keepExisting (optional) True to keep existing selections
25029      */
25030     selectLastRow : function(keepExisting){
25031         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25032         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25033     },
25034
25035     /**
25036      * Selects the row immediately following the last selected row.
25037      * @param {Boolean} keepExisting (optional) True to keep existing selections
25038      */
25039     selectNext : function(keepExisting)
25040     {
25041             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25042             this.selectRow(this.last+1, keepExisting);
25043             this.grid.getView().focusRow(this.last);
25044         }
25045     },
25046
25047     /**
25048      * Selects the row that precedes the last selected row.
25049      * @param {Boolean} keepExisting (optional) True to keep existing selections
25050      */
25051     selectPrevious : function(keepExisting){
25052         if(this.last){
25053             this.selectRow(this.last-1, keepExisting);
25054             this.grid.getView().focusRow(this.last);
25055         }
25056     },
25057
25058     /**
25059      * Returns the selected records
25060      * @return {Array} Array of selected records
25061      */
25062     getSelections : function(){
25063         return [].concat(this.selections.items);
25064     },
25065
25066     /**
25067      * Returns the first selected record.
25068      * @return {Record}
25069      */
25070     getSelected : function(){
25071         return this.selections.itemAt(0);
25072     },
25073
25074
25075     /**
25076      * Clears all selections.
25077      */
25078     clearSelections : function(fast)
25079     {
25080         if(this.locked) {
25081             return;
25082         }
25083         if(fast !== true){
25084                 var ds = this.grid.store;
25085             var s = this.selections;
25086             s.each(function(r){
25087                 this.deselectRow(ds.indexOfId(r.id));
25088             }, this);
25089             s.clear();
25090         }else{
25091             this.selections.clear();
25092         }
25093         this.last = false;
25094     },
25095
25096
25097     /**
25098      * Selects all rows.
25099      */
25100     selectAll : function(){
25101         if(this.locked) {
25102             return;
25103         }
25104         this.selections.clear();
25105         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25106             this.selectRow(i, true);
25107         }
25108     },
25109
25110     /**
25111      * Returns True if there is a selection.
25112      * @return {Boolean}
25113      */
25114     hasSelection : function(){
25115         return this.selections.length > 0;
25116     },
25117
25118     /**
25119      * Returns True if the specified row is selected.
25120      * @param {Number/Record} record The record or index of the record to check
25121      * @return {Boolean}
25122      */
25123     isSelected : function(index){
25124             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25125         return (r && this.selections.key(r.id) ? true : false);
25126     },
25127
25128     /**
25129      * Returns True if the specified record id is selected.
25130      * @param {String} id The id of record to check
25131      * @return {Boolean}
25132      */
25133     isIdSelected : function(id){
25134         return (this.selections.key(id) ? true : false);
25135     },
25136
25137
25138     // private
25139     handleMouseDBClick : function(e, t){
25140         
25141     },
25142     // private
25143     handleMouseDown : function(e, t)
25144     {
25145             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25146         if(this.isLocked() || rowIndex < 0 ){
25147             return;
25148         };
25149         if(e.shiftKey && this.last !== false){
25150             var last = this.last;
25151             this.selectRange(last, rowIndex, e.ctrlKey);
25152             this.last = last; // reset the last
25153             t.focus();
25154     
25155         }else{
25156             var isSelected = this.isSelected(rowIndex);
25157             //Roo.log("select row:" + rowIndex);
25158             if(isSelected){
25159                 this.deselectRow(rowIndex);
25160             } else {
25161                         this.selectRow(rowIndex, true);
25162             }
25163     
25164             /*
25165                 if(e.button !== 0 && isSelected){
25166                 alert('rowIndex 2: ' + rowIndex);
25167                     view.focusRow(rowIndex);
25168                 }else if(e.ctrlKey && isSelected){
25169                     this.deselectRow(rowIndex);
25170                 }else if(!isSelected){
25171                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25172                     view.focusRow(rowIndex);
25173                 }
25174             */
25175         }
25176         this.fireEvent("afterselectionchange", this);
25177     },
25178     // private
25179     handleDragableRowClick :  function(grid, rowIndex, e) 
25180     {
25181         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25182             this.selectRow(rowIndex, false);
25183             grid.view.focusRow(rowIndex);
25184              this.fireEvent("afterselectionchange", this);
25185         }
25186     },
25187     
25188     /**
25189      * Selects multiple rows.
25190      * @param {Array} rows Array of the indexes of the row to select
25191      * @param {Boolean} keepExisting (optional) True to keep existing selections
25192      */
25193     selectRows : function(rows, keepExisting){
25194         if(!keepExisting){
25195             this.clearSelections();
25196         }
25197         for(var i = 0, len = rows.length; i < len; i++){
25198             this.selectRow(rows[i], true);
25199         }
25200     },
25201
25202     /**
25203      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25204      * @param {Number} startRow The index of the first row in the range
25205      * @param {Number} endRow The index of the last row in the range
25206      * @param {Boolean} keepExisting (optional) True to retain existing selections
25207      */
25208     selectRange : function(startRow, endRow, keepExisting){
25209         if(this.locked) {
25210             return;
25211         }
25212         if(!keepExisting){
25213             this.clearSelections();
25214         }
25215         if(startRow <= endRow){
25216             for(var i = startRow; i <= endRow; i++){
25217                 this.selectRow(i, true);
25218             }
25219         }else{
25220             for(var i = startRow; i >= endRow; i--){
25221                 this.selectRow(i, true);
25222             }
25223         }
25224     },
25225
25226     /**
25227      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25228      * @param {Number} startRow The index of the first row in the range
25229      * @param {Number} endRow The index of the last row in the range
25230      */
25231     deselectRange : function(startRow, endRow, preventViewNotify){
25232         if(this.locked) {
25233             return;
25234         }
25235         for(var i = startRow; i <= endRow; i++){
25236             this.deselectRow(i, preventViewNotify);
25237         }
25238     },
25239
25240     /**
25241      * Selects a row.
25242      * @param {Number} row The index of the row to select
25243      * @param {Boolean} keepExisting (optional) True to keep existing selections
25244      */
25245     selectRow : function(index, keepExisting, preventViewNotify)
25246     {
25247             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25248             return;
25249         }
25250         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25251             if(!keepExisting || this.singleSelect){
25252                 this.clearSelections();
25253             }
25254             
25255             var r = this.grid.store.getAt(index);
25256             //console.log('selectRow - record id :' + r.id);
25257             
25258             this.selections.add(r);
25259             this.last = this.lastActive = index;
25260             if(!preventViewNotify){
25261                 var proxy = new Roo.Element(
25262                                 this.grid.getRowDom(index)
25263                 );
25264                 proxy.addClass('bg-info info');
25265             }
25266             this.fireEvent("rowselect", this, index, r);
25267             this.fireEvent("selectionchange", this);
25268         }
25269     },
25270
25271     /**
25272      * Deselects a row.
25273      * @param {Number} row The index of the row to deselect
25274      */
25275     deselectRow : function(index, preventViewNotify)
25276     {
25277         if(this.locked) {
25278             return;
25279         }
25280         if(this.last == index){
25281             this.last = false;
25282         }
25283         if(this.lastActive == index){
25284             this.lastActive = false;
25285         }
25286         
25287         var r = this.grid.store.getAt(index);
25288         if (!r) {
25289             return;
25290         }
25291         
25292         this.selections.remove(r);
25293         //.console.log('deselectRow - record id :' + r.id);
25294         if(!preventViewNotify){
25295         
25296             var proxy = new Roo.Element(
25297                 this.grid.getRowDom(index)
25298             );
25299             proxy.removeClass('bg-info info');
25300         }
25301         this.fireEvent("rowdeselect", this, index);
25302         this.fireEvent("selectionchange", this);
25303     },
25304
25305     // private
25306     restoreLast : function(){
25307         if(this._last){
25308             this.last = this._last;
25309         }
25310     },
25311
25312     // private
25313     acceptsNav : function(row, col, cm){
25314         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25315     },
25316
25317     // private
25318     onEditorKey : function(field, e){
25319         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25320         if(k == e.TAB){
25321             e.stopEvent();
25322             ed.completeEdit();
25323             if(e.shiftKey){
25324                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25325             }else{
25326                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25327             }
25328         }else if(k == e.ENTER && !e.ctrlKey){
25329             e.stopEvent();
25330             ed.completeEdit();
25331             if(e.shiftKey){
25332                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25333             }else{
25334                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25335             }
25336         }else if(k == e.ESC){
25337             ed.cancelEdit();
25338         }
25339         if(newCell){
25340             g.startEditing(newCell[0], newCell[1]);
25341         }
25342     }
25343 });
25344 /*
25345  * Based on:
25346  * Ext JS Library 1.1.1
25347  * Copyright(c) 2006-2007, Ext JS, LLC.
25348  *
25349  * Originally Released Under LGPL - original licence link has changed is not relivant.
25350  *
25351  * Fork - LGPL
25352  * <script type="text/javascript">
25353  */
25354  
25355 /**
25356  * @class Roo.bootstrap.PagingToolbar
25357  * @extends Roo.bootstrap.NavSimplebar
25358  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25359  * @constructor
25360  * Create a new PagingToolbar
25361  * @param {Object} config The config object
25362  * @param {Roo.data.Store} store
25363  */
25364 Roo.bootstrap.PagingToolbar = function(config)
25365 {
25366     // old args format still supported... - xtype is prefered..
25367         // created from xtype...
25368     
25369     this.ds = config.dataSource;
25370     
25371     if (config.store && !this.ds) {
25372         this.store= Roo.factory(config.store, Roo.data);
25373         this.ds = this.store;
25374         this.ds.xmodule = this.xmodule || false;
25375     }
25376     
25377     this.toolbarItems = [];
25378     if (config.items) {
25379         this.toolbarItems = config.items;
25380     }
25381     
25382     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25383     
25384     this.cursor = 0;
25385     
25386     if (this.ds) { 
25387         this.bind(this.ds);
25388     }
25389     
25390     if (Roo.bootstrap.version == 4) {
25391         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25392     } else {
25393         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25394     }
25395     
25396 };
25397
25398 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25399     /**
25400      * @cfg {Roo.data.Store} dataSource
25401      * The underlying data store providing the paged data
25402      */
25403     /**
25404      * @cfg {String/HTMLElement/Element} container
25405      * container The id or element that will contain the toolbar
25406      */
25407     /**
25408      * @cfg {Boolean} displayInfo
25409      * True to display the displayMsg (defaults to false)
25410      */
25411     /**
25412      * @cfg {Number} pageSize
25413      * The number of records to display per page (defaults to 20)
25414      */
25415     pageSize: 20,
25416     /**
25417      * @cfg {String} displayMsg
25418      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25419      */
25420     displayMsg : 'Displaying {0} - {1} of {2}',
25421     /**
25422      * @cfg {String} emptyMsg
25423      * The message to display when no records are found (defaults to "No data to display")
25424      */
25425     emptyMsg : 'No data to display',
25426     /**
25427      * Customizable piece of the default paging text (defaults to "Page")
25428      * @type String
25429      */
25430     beforePageText : "Page",
25431     /**
25432      * Customizable piece of the default paging text (defaults to "of %0")
25433      * @type String
25434      */
25435     afterPageText : "of {0}",
25436     /**
25437      * Customizable piece of the default paging text (defaults to "First Page")
25438      * @type String
25439      */
25440     firstText : "First Page",
25441     /**
25442      * Customizable piece of the default paging text (defaults to "Previous Page")
25443      * @type String
25444      */
25445     prevText : "Previous Page",
25446     /**
25447      * Customizable piece of the default paging text (defaults to "Next Page")
25448      * @type String
25449      */
25450     nextText : "Next Page",
25451     /**
25452      * Customizable piece of the default paging text (defaults to "Last Page")
25453      * @type String
25454      */
25455     lastText : "Last Page",
25456     /**
25457      * Customizable piece of the default paging text (defaults to "Refresh")
25458      * @type String
25459      */
25460     refreshText : "Refresh",
25461
25462     buttons : false,
25463     // private
25464     onRender : function(ct, position) 
25465     {
25466         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25467         this.navgroup.parentId = this.id;
25468         this.navgroup.onRender(this.el, null);
25469         // add the buttons to the navgroup
25470         
25471         if(this.displayInfo){
25472             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25473             this.displayEl = this.el.select('.x-paging-info', true).first();
25474 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25475 //            this.displayEl = navel.el.select('span',true).first();
25476         }
25477         
25478         var _this = this;
25479         
25480         if(this.buttons){
25481             Roo.each(_this.buttons, function(e){ // this might need to use render????
25482                Roo.factory(e).render(_this.el);
25483             });
25484         }
25485             
25486         Roo.each(_this.toolbarItems, function(e) {
25487             _this.navgroup.addItem(e);
25488         });
25489         
25490         
25491         this.first = this.navgroup.addItem({
25492             tooltip: this.firstText,
25493             cls: "prev btn-outline-secondary",
25494             html : ' <i class="fa fa-step-backward"></i>',
25495             disabled: true,
25496             preventDefault: true,
25497             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25498         });
25499         
25500         this.prev =  this.navgroup.addItem({
25501             tooltip: this.prevText,
25502             cls: "prev btn-outline-secondary",
25503             html : ' <i class="fa fa-backward"></i>',
25504             disabled: true,
25505             preventDefault: true,
25506             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25507         });
25508     //this.addSeparator();
25509         
25510         
25511         var field = this.navgroup.addItem( {
25512             tagtype : 'span',
25513             cls : 'x-paging-position  btn-outline-secondary',
25514              disabled: true,
25515             html : this.beforePageText  +
25516                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25517                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25518          } ); //?? escaped?
25519         
25520         this.field = field.el.select('input', true).first();
25521         this.field.on("keydown", this.onPagingKeydown, this);
25522         this.field.on("focus", function(){this.dom.select();});
25523     
25524     
25525         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25526         //this.field.setHeight(18);
25527         //this.addSeparator();
25528         this.next = this.navgroup.addItem({
25529             tooltip: this.nextText,
25530             cls: "next btn-outline-secondary",
25531             html : ' <i class="fa fa-forward"></i>',
25532             disabled: true,
25533             preventDefault: true,
25534             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25535         });
25536         this.last = this.navgroup.addItem({
25537             tooltip: this.lastText,
25538             html : ' <i class="fa fa-step-forward"></i>',
25539             cls: "next btn-outline-secondary",
25540             disabled: true,
25541             preventDefault: true,
25542             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25543         });
25544     //this.addSeparator();
25545         this.loading = this.navgroup.addItem({
25546             tooltip: this.refreshText,
25547             cls: "btn-outline-secondary",
25548             html : ' <i class="fa fa-refresh"></i>',
25549             preventDefault: true,
25550             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25551         });
25552         
25553     },
25554
25555     // private
25556     updateInfo : function(){
25557         if(this.displayEl){
25558             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25559             var msg = count == 0 ?
25560                 this.emptyMsg :
25561                 String.format(
25562                     this.displayMsg,
25563                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25564                 );
25565             this.displayEl.update(msg);
25566         }
25567     },
25568
25569     // private
25570     onLoad : function(ds, r, o)
25571     {
25572         this.cursor = o.params.start ? o.params.start : 0;
25573         
25574         var d = this.getPageData(),
25575             ap = d.activePage,
25576             ps = d.pages;
25577         
25578         
25579         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25580         this.field.dom.value = ap;
25581         this.first.setDisabled(ap == 1);
25582         this.prev.setDisabled(ap == 1);
25583         this.next.setDisabled(ap == ps);
25584         this.last.setDisabled(ap == ps);
25585         this.loading.enable();
25586         this.updateInfo();
25587     },
25588
25589     // private
25590     getPageData : function(){
25591         var total = this.ds.getTotalCount();
25592         return {
25593             total : total,
25594             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25595             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25596         };
25597     },
25598
25599     // private
25600     onLoadError : function(){
25601         this.loading.enable();
25602     },
25603
25604     // private
25605     onPagingKeydown : function(e){
25606         var k = e.getKey();
25607         var d = this.getPageData();
25608         if(k == e.RETURN){
25609             var v = this.field.dom.value, pageNum;
25610             if(!v || isNaN(pageNum = parseInt(v, 10))){
25611                 this.field.dom.value = d.activePage;
25612                 return;
25613             }
25614             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25615             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25616             e.stopEvent();
25617         }
25618         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))
25619         {
25620           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25621           this.field.dom.value = pageNum;
25622           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25623           e.stopEvent();
25624         }
25625         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25626         {
25627           var v = this.field.dom.value, pageNum; 
25628           var increment = (e.shiftKey) ? 10 : 1;
25629           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25630                 increment *= -1;
25631           }
25632           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25633             this.field.dom.value = d.activePage;
25634             return;
25635           }
25636           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25637           {
25638             this.field.dom.value = parseInt(v, 10) + increment;
25639             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25640             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25641           }
25642           e.stopEvent();
25643         }
25644     },
25645
25646     // private
25647     beforeLoad : function(){
25648         if(this.loading){
25649             this.loading.disable();
25650         }
25651     },
25652
25653     // private
25654     onClick : function(which){
25655         
25656         var ds = this.ds;
25657         if (!ds) {
25658             return;
25659         }
25660         
25661         switch(which){
25662             case "first":
25663                 ds.load({params:{start: 0, limit: this.pageSize}});
25664             break;
25665             case "prev":
25666                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25667             break;
25668             case "next":
25669                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25670             break;
25671             case "last":
25672                 var total = ds.getTotalCount();
25673                 var extra = total % this.pageSize;
25674                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25675                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25676             break;
25677             case "refresh":
25678                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25679             break;
25680         }
25681     },
25682
25683     /**
25684      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25685      * @param {Roo.data.Store} store The data store to unbind
25686      */
25687     unbind : function(ds){
25688         ds.un("beforeload", this.beforeLoad, this);
25689         ds.un("load", this.onLoad, this);
25690         ds.un("loadexception", this.onLoadError, this);
25691         ds.un("remove", this.updateInfo, this);
25692         ds.un("add", this.updateInfo, this);
25693         this.ds = undefined;
25694     },
25695
25696     /**
25697      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25698      * @param {Roo.data.Store} store The data store to bind
25699      */
25700     bind : function(ds){
25701         ds.on("beforeload", this.beforeLoad, this);
25702         ds.on("load", this.onLoad, this);
25703         ds.on("loadexception", this.onLoadError, this);
25704         ds.on("remove", this.updateInfo, this);
25705         ds.on("add", this.updateInfo, this);
25706         this.ds = ds;
25707     }
25708 });/*
25709  * - LGPL
25710  *
25711  * element
25712  * 
25713  */
25714
25715 /**
25716  * @class Roo.bootstrap.MessageBar
25717  * @extends Roo.bootstrap.Component
25718  * Bootstrap MessageBar class
25719  * @cfg {String} html contents of the MessageBar
25720  * @cfg {String} weight (info | success | warning | danger) default info
25721  * @cfg {String} beforeClass insert the bar before the given class
25722  * @cfg {Boolean} closable (true | false) default false
25723  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25724  * 
25725  * @constructor
25726  * Create a new Element
25727  * @param {Object} config The config object
25728  */
25729
25730 Roo.bootstrap.MessageBar = function(config){
25731     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25732 };
25733
25734 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25735     
25736     html: '',
25737     weight: 'info',
25738     closable: false,
25739     fixed: false,
25740     beforeClass: 'bootstrap-sticky-wrap',
25741     
25742     getAutoCreate : function(){
25743         
25744         var cfg = {
25745             tag: 'div',
25746             cls: 'alert alert-dismissable alert-' + this.weight,
25747             cn: [
25748                 {
25749                     tag: 'span',
25750                     cls: 'message',
25751                     html: this.html || ''
25752                 }
25753             ]
25754         };
25755         
25756         if(this.fixed){
25757             cfg.cls += ' alert-messages-fixed';
25758         }
25759         
25760         if(this.closable){
25761             cfg.cn.push({
25762                 tag: 'button',
25763                 cls: 'close',
25764                 html: 'x'
25765             });
25766         }
25767         
25768         return cfg;
25769     },
25770     
25771     onRender : function(ct, position)
25772     {
25773         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25774         
25775         if(!this.el){
25776             var cfg = Roo.apply({},  this.getAutoCreate());
25777             cfg.id = Roo.id();
25778             
25779             if (this.cls) {
25780                 cfg.cls += ' ' + this.cls;
25781             }
25782             if (this.style) {
25783                 cfg.style = this.style;
25784             }
25785             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25786             
25787             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25788         }
25789         
25790         this.el.select('>button.close').on('click', this.hide, this);
25791         
25792     },
25793     
25794     show : function()
25795     {
25796         if (!this.rendered) {
25797             this.render();
25798         }
25799         
25800         this.el.show();
25801         
25802         this.fireEvent('show', this);
25803         
25804     },
25805     
25806     hide : function()
25807     {
25808         if (!this.rendered) {
25809             this.render();
25810         }
25811         
25812         this.el.hide();
25813         
25814         this.fireEvent('hide', this);
25815     },
25816     
25817     update : function()
25818     {
25819 //        var e = this.el.dom.firstChild;
25820 //        
25821 //        if(this.closable){
25822 //            e = e.nextSibling;
25823 //        }
25824 //        
25825 //        e.data = this.html || '';
25826
25827         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25828     }
25829    
25830 });
25831
25832  
25833
25834      /*
25835  * - LGPL
25836  *
25837  * Graph
25838  * 
25839  */
25840
25841
25842 /**
25843  * @class Roo.bootstrap.Graph
25844  * @extends Roo.bootstrap.Component
25845  * Bootstrap Graph class
25846 > Prameters
25847  -sm {number} sm 4
25848  -md {number} md 5
25849  @cfg {String} graphtype  bar | vbar | pie
25850  @cfg {number} g_x coodinator | centre x (pie)
25851  @cfg {number} g_y coodinator | centre y (pie)
25852  @cfg {number} g_r radius (pie)
25853  @cfg {number} g_height height of the chart (respected by all elements in the set)
25854  @cfg {number} g_width width of the chart (respected by all elements in the set)
25855  @cfg {Object} title The title of the chart
25856     
25857  -{Array}  values
25858  -opts (object) options for the chart 
25859      o {
25860      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25861      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25862      o vgutter (number)
25863      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.
25864      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25865      o to
25866      o stretch (boolean)
25867      o }
25868  -opts (object) options for the pie
25869      o{
25870      o cut
25871      o startAngle (number)
25872      o endAngle (number)
25873      } 
25874  *
25875  * @constructor
25876  * Create a new Input
25877  * @param {Object} config The config object
25878  */
25879
25880 Roo.bootstrap.Graph = function(config){
25881     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25882     
25883     this.addEvents({
25884         // img events
25885         /**
25886          * @event click
25887          * The img click event for the img.
25888          * @param {Roo.EventObject} e
25889          */
25890         "click" : true
25891     });
25892 };
25893
25894 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25895     
25896     sm: 4,
25897     md: 5,
25898     graphtype: 'bar',
25899     g_height: 250,
25900     g_width: 400,
25901     g_x: 50,
25902     g_y: 50,
25903     g_r: 30,
25904     opts:{
25905         //g_colors: this.colors,
25906         g_type: 'soft',
25907         g_gutter: '20%'
25908
25909     },
25910     title : false,
25911
25912     getAutoCreate : function(){
25913         
25914         var cfg = {
25915             tag: 'div',
25916             html : null
25917         };
25918         
25919         
25920         return  cfg;
25921     },
25922
25923     onRender : function(ct,position){
25924         
25925         
25926         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25927         
25928         if (typeof(Raphael) == 'undefined') {
25929             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25930             return;
25931         }
25932         
25933         this.raphael = Raphael(this.el.dom);
25934         
25935                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25936                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25937                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25938                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25939                 /*
25940                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25941                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25942                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25943                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25944                 
25945                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25946                 r.barchart(330, 10, 300, 220, data1);
25947                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25948                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25949                 */
25950                 
25951                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25952                 // r.barchart(30, 30, 560, 250,  xdata, {
25953                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25954                 //     axis : "0 0 1 1",
25955                 //     axisxlabels :  xdata
25956                 //     //yvalues : cols,
25957                    
25958                 // });
25959 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25960 //        
25961 //        this.load(null,xdata,{
25962 //                axis : "0 0 1 1",
25963 //                axisxlabels :  xdata
25964 //                });
25965
25966     },
25967
25968     load : function(graphtype,xdata,opts)
25969     {
25970         this.raphael.clear();
25971         if(!graphtype) {
25972             graphtype = this.graphtype;
25973         }
25974         if(!opts){
25975             opts = this.opts;
25976         }
25977         var r = this.raphael,
25978             fin = function () {
25979                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25980             },
25981             fout = function () {
25982                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25983             },
25984             pfin = function() {
25985                 this.sector.stop();
25986                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25987
25988                 if (this.label) {
25989                     this.label[0].stop();
25990                     this.label[0].attr({ r: 7.5 });
25991                     this.label[1].attr({ "font-weight": 800 });
25992                 }
25993             },
25994             pfout = function() {
25995                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25996
25997                 if (this.label) {
25998                     this.label[0].animate({ r: 5 }, 500, "bounce");
25999                     this.label[1].attr({ "font-weight": 400 });
26000                 }
26001             };
26002
26003         switch(graphtype){
26004             case 'bar':
26005                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26006                 break;
26007             case 'hbar':
26008                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26009                 break;
26010             case 'pie':
26011 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26012 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26013 //            
26014                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26015                 
26016                 break;
26017
26018         }
26019         
26020         if(this.title){
26021             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26022         }
26023         
26024     },
26025     
26026     setTitle: function(o)
26027     {
26028         this.title = o;
26029     },
26030     
26031     initEvents: function() {
26032         
26033         if(!this.href){
26034             this.el.on('click', this.onClick, this);
26035         }
26036     },
26037     
26038     onClick : function(e)
26039     {
26040         Roo.log('img onclick');
26041         this.fireEvent('click', this, e);
26042     }
26043    
26044 });
26045
26046  
26047 /*
26048  * - LGPL
26049  *
26050  * numberBox
26051  * 
26052  */
26053 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26054
26055 /**
26056  * @class Roo.bootstrap.dash.NumberBox
26057  * @extends Roo.bootstrap.Component
26058  * Bootstrap NumberBox class
26059  * @cfg {String} headline Box headline
26060  * @cfg {String} content Box content
26061  * @cfg {String} icon Box icon
26062  * @cfg {String} footer Footer text
26063  * @cfg {String} fhref Footer href
26064  * 
26065  * @constructor
26066  * Create a new NumberBox
26067  * @param {Object} config The config object
26068  */
26069
26070
26071 Roo.bootstrap.dash.NumberBox = function(config){
26072     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26073     
26074 };
26075
26076 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26077     
26078     headline : '',
26079     content : '',
26080     icon : '',
26081     footer : '',
26082     fhref : '',
26083     ficon : '',
26084     
26085     getAutoCreate : function(){
26086         
26087         var cfg = {
26088             tag : 'div',
26089             cls : 'small-box ',
26090             cn : [
26091                 {
26092                     tag : 'div',
26093                     cls : 'inner',
26094                     cn :[
26095                         {
26096                             tag : 'h3',
26097                             cls : 'roo-headline',
26098                             html : this.headline
26099                         },
26100                         {
26101                             tag : 'p',
26102                             cls : 'roo-content',
26103                             html : this.content
26104                         }
26105                     ]
26106                 }
26107             ]
26108         };
26109         
26110         if(this.icon){
26111             cfg.cn.push({
26112                 tag : 'div',
26113                 cls : 'icon',
26114                 cn :[
26115                     {
26116                         tag : 'i',
26117                         cls : 'ion ' + this.icon
26118                     }
26119                 ]
26120             });
26121         }
26122         
26123         if(this.footer){
26124             var footer = {
26125                 tag : 'a',
26126                 cls : 'small-box-footer',
26127                 href : this.fhref || '#',
26128                 html : this.footer
26129             };
26130             
26131             cfg.cn.push(footer);
26132             
26133         }
26134         
26135         return  cfg;
26136     },
26137
26138     onRender : function(ct,position){
26139         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26140
26141
26142        
26143                 
26144     },
26145
26146     setHeadline: function (value)
26147     {
26148         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26149     },
26150     
26151     setFooter: function (value, href)
26152     {
26153         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26154         
26155         if(href){
26156             this.el.select('a.small-box-footer',true).first().attr('href', href);
26157         }
26158         
26159     },
26160
26161     setContent: function (value)
26162     {
26163         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26164     },
26165
26166     initEvents: function() 
26167     {   
26168         
26169     }
26170     
26171 });
26172
26173  
26174 /*
26175  * - LGPL
26176  *
26177  * TabBox
26178  * 
26179  */
26180 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26181
26182 /**
26183  * @class Roo.bootstrap.dash.TabBox
26184  * @extends Roo.bootstrap.Component
26185  * Bootstrap TabBox class
26186  * @cfg {String} title Title of the TabBox
26187  * @cfg {String} icon Icon of the TabBox
26188  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26189  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26190  * 
26191  * @constructor
26192  * Create a new TabBox
26193  * @param {Object} config The config object
26194  */
26195
26196
26197 Roo.bootstrap.dash.TabBox = function(config){
26198     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26199     this.addEvents({
26200         // raw events
26201         /**
26202          * @event addpane
26203          * When a pane is added
26204          * @param {Roo.bootstrap.dash.TabPane} pane
26205          */
26206         "addpane" : true,
26207         /**
26208          * @event activatepane
26209          * When a pane is activated
26210          * @param {Roo.bootstrap.dash.TabPane} pane
26211          */
26212         "activatepane" : true
26213         
26214          
26215     });
26216     
26217     this.panes = [];
26218 };
26219
26220 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26221
26222     title : '',
26223     icon : false,
26224     showtabs : true,
26225     tabScrollable : false,
26226     
26227     getChildContainer : function()
26228     {
26229         return this.el.select('.tab-content', true).first();
26230     },
26231     
26232     getAutoCreate : function(){
26233         
26234         var header = {
26235             tag: 'li',
26236             cls: 'pull-left header',
26237             html: this.title,
26238             cn : []
26239         };
26240         
26241         if(this.icon){
26242             header.cn.push({
26243                 tag: 'i',
26244                 cls: 'fa ' + this.icon
26245             });
26246         }
26247         
26248         var h = {
26249             tag: 'ul',
26250             cls: 'nav nav-tabs pull-right',
26251             cn: [
26252                 header
26253             ]
26254         };
26255         
26256         if(this.tabScrollable){
26257             h = {
26258                 tag: 'div',
26259                 cls: 'tab-header',
26260                 cn: [
26261                     {
26262                         tag: 'ul',
26263                         cls: 'nav nav-tabs pull-right',
26264                         cn: [
26265                             header
26266                         ]
26267                     }
26268                 ]
26269             };
26270         }
26271         
26272         var cfg = {
26273             tag: 'div',
26274             cls: 'nav-tabs-custom',
26275             cn: [
26276                 h,
26277                 {
26278                     tag: 'div',
26279                     cls: 'tab-content no-padding',
26280                     cn: []
26281                 }
26282             ]
26283         };
26284
26285         return  cfg;
26286     },
26287     initEvents : function()
26288     {
26289         //Roo.log('add add pane handler');
26290         this.on('addpane', this.onAddPane, this);
26291     },
26292      /**
26293      * Updates the box title
26294      * @param {String} html to set the title to.
26295      */
26296     setTitle : function(value)
26297     {
26298         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26299     },
26300     onAddPane : function(pane)
26301     {
26302         this.panes.push(pane);
26303         //Roo.log('addpane');
26304         //Roo.log(pane);
26305         // tabs are rendere left to right..
26306         if(!this.showtabs){
26307             return;
26308         }
26309         
26310         var ctr = this.el.select('.nav-tabs', true).first();
26311          
26312          
26313         var existing = ctr.select('.nav-tab',true);
26314         var qty = existing.getCount();;
26315         
26316         
26317         var tab = ctr.createChild({
26318             tag : 'li',
26319             cls : 'nav-tab' + (qty ? '' : ' active'),
26320             cn : [
26321                 {
26322                     tag : 'a',
26323                     href:'#',
26324                     html : pane.title
26325                 }
26326             ]
26327         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26328         pane.tab = tab;
26329         
26330         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26331         if (!qty) {
26332             pane.el.addClass('active');
26333         }
26334         
26335                 
26336     },
26337     onTabClick : function(ev,un,ob,pane)
26338     {
26339         //Roo.log('tab - prev default');
26340         ev.preventDefault();
26341         
26342         
26343         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26344         pane.tab.addClass('active');
26345         //Roo.log(pane.title);
26346         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26347         // technically we should have a deactivate event.. but maybe add later.
26348         // and it should not de-activate the selected tab...
26349         this.fireEvent('activatepane', pane);
26350         pane.el.addClass('active');
26351         pane.fireEvent('activate');
26352         
26353         
26354     },
26355     
26356     getActivePane : function()
26357     {
26358         var r = false;
26359         Roo.each(this.panes, function(p) {
26360             if(p.el.hasClass('active')){
26361                 r = p;
26362                 return false;
26363             }
26364             
26365             return;
26366         });
26367         
26368         return r;
26369     }
26370     
26371     
26372 });
26373
26374  
26375 /*
26376  * - LGPL
26377  *
26378  * Tab pane
26379  * 
26380  */
26381 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26382 /**
26383  * @class Roo.bootstrap.TabPane
26384  * @extends Roo.bootstrap.Component
26385  * Bootstrap TabPane class
26386  * @cfg {Boolean} active (false | true) Default false
26387  * @cfg {String} title title of panel
26388
26389  * 
26390  * @constructor
26391  * Create a new TabPane
26392  * @param {Object} config The config object
26393  */
26394
26395 Roo.bootstrap.dash.TabPane = function(config){
26396     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26397     
26398     this.addEvents({
26399         // raw events
26400         /**
26401          * @event activate
26402          * When a pane is activated
26403          * @param {Roo.bootstrap.dash.TabPane} pane
26404          */
26405         "activate" : true
26406          
26407     });
26408 };
26409
26410 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26411     
26412     active : false,
26413     title : '',
26414     
26415     // the tabBox that this is attached to.
26416     tab : false,
26417      
26418     getAutoCreate : function() 
26419     {
26420         var cfg = {
26421             tag: 'div',
26422             cls: 'tab-pane'
26423         };
26424         
26425         if(this.active){
26426             cfg.cls += ' active';
26427         }
26428         
26429         return cfg;
26430     },
26431     initEvents  : function()
26432     {
26433         //Roo.log('trigger add pane handler');
26434         this.parent().fireEvent('addpane', this)
26435     },
26436     
26437      /**
26438      * Updates the tab title 
26439      * @param {String} html to set the title to.
26440      */
26441     setTitle: function(str)
26442     {
26443         if (!this.tab) {
26444             return;
26445         }
26446         this.title = str;
26447         this.tab.select('a', true).first().dom.innerHTML = str;
26448         
26449     }
26450     
26451     
26452     
26453 });
26454
26455  
26456
26457
26458  /*
26459  * - LGPL
26460  *
26461  * menu
26462  * 
26463  */
26464 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26465
26466 /**
26467  * @class Roo.bootstrap.menu.Menu
26468  * @extends Roo.bootstrap.Component
26469  * Bootstrap Menu class - container for Menu
26470  * @cfg {String} html Text of the menu
26471  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26472  * @cfg {String} icon Font awesome icon
26473  * @cfg {String} pos Menu align to (top | bottom) default bottom
26474  * 
26475  * 
26476  * @constructor
26477  * Create a new Menu
26478  * @param {Object} config The config object
26479  */
26480
26481
26482 Roo.bootstrap.menu.Menu = function(config){
26483     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26484     
26485     this.addEvents({
26486         /**
26487          * @event beforeshow
26488          * Fires before this menu is displayed
26489          * @param {Roo.bootstrap.menu.Menu} this
26490          */
26491         beforeshow : true,
26492         /**
26493          * @event beforehide
26494          * Fires before this menu is hidden
26495          * @param {Roo.bootstrap.menu.Menu} this
26496          */
26497         beforehide : true,
26498         /**
26499          * @event show
26500          * Fires after this menu is displayed
26501          * @param {Roo.bootstrap.menu.Menu} this
26502          */
26503         show : true,
26504         /**
26505          * @event hide
26506          * Fires after this menu is hidden
26507          * @param {Roo.bootstrap.menu.Menu} this
26508          */
26509         hide : true,
26510         /**
26511          * @event click
26512          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26513          * @param {Roo.bootstrap.menu.Menu} this
26514          * @param {Roo.EventObject} e
26515          */
26516         click : true
26517     });
26518     
26519 };
26520
26521 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26522     
26523     submenu : false,
26524     html : '',
26525     weight : 'default',
26526     icon : false,
26527     pos : 'bottom',
26528     
26529     
26530     getChildContainer : function() {
26531         if(this.isSubMenu){
26532             return this.el;
26533         }
26534         
26535         return this.el.select('ul.dropdown-menu', true).first();  
26536     },
26537     
26538     getAutoCreate : function()
26539     {
26540         var text = [
26541             {
26542                 tag : 'span',
26543                 cls : 'roo-menu-text',
26544                 html : this.html
26545             }
26546         ];
26547         
26548         if(this.icon){
26549             text.unshift({
26550                 tag : 'i',
26551                 cls : 'fa ' + this.icon
26552             })
26553         }
26554         
26555         
26556         var cfg = {
26557             tag : 'div',
26558             cls : 'btn-group',
26559             cn : [
26560                 {
26561                     tag : 'button',
26562                     cls : 'dropdown-button btn btn-' + this.weight,
26563                     cn : text
26564                 },
26565                 {
26566                     tag : 'button',
26567                     cls : 'dropdown-toggle btn btn-' + this.weight,
26568                     cn : [
26569                         {
26570                             tag : 'span',
26571                             cls : 'caret'
26572                         }
26573                     ]
26574                 },
26575                 {
26576                     tag : 'ul',
26577                     cls : 'dropdown-menu'
26578                 }
26579             ]
26580             
26581         };
26582         
26583         if(this.pos == 'top'){
26584             cfg.cls += ' dropup';
26585         }
26586         
26587         if(this.isSubMenu){
26588             cfg = {
26589                 tag : 'ul',
26590                 cls : 'dropdown-menu'
26591             }
26592         }
26593         
26594         return cfg;
26595     },
26596     
26597     onRender : function(ct, position)
26598     {
26599         this.isSubMenu = ct.hasClass('dropdown-submenu');
26600         
26601         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26602     },
26603     
26604     initEvents : function() 
26605     {
26606         if(this.isSubMenu){
26607             return;
26608         }
26609         
26610         this.hidden = true;
26611         
26612         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26613         this.triggerEl.on('click', this.onTriggerPress, this);
26614         
26615         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26616         this.buttonEl.on('click', this.onClick, this);
26617         
26618     },
26619     
26620     list : function()
26621     {
26622         if(this.isSubMenu){
26623             return this.el;
26624         }
26625         
26626         return this.el.select('ul.dropdown-menu', true).first();
26627     },
26628     
26629     onClick : function(e)
26630     {
26631         this.fireEvent("click", this, e);
26632     },
26633     
26634     onTriggerPress  : function(e)
26635     {   
26636         if (this.isVisible()) {
26637             this.hide();
26638         } else {
26639             this.show();
26640         }
26641     },
26642     
26643     isVisible : function(){
26644         return !this.hidden;
26645     },
26646     
26647     show : function()
26648     {
26649         this.fireEvent("beforeshow", this);
26650         
26651         this.hidden = false;
26652         this.el.addClass('open');
26653         
26654         Roo.get(document).on("mouseup", this.onMouseUp, this);
26655         
26656         this.fireEvent("show", this);
26657         
26658         
26659     },
26660     
26661     hide : function()
26662     {
26663         this.fireEvent("beforehide", this);
26664         
26665         this.hidden = true;
26666         this.el.removeClass('open');
26667         
26668         Roo.get(document).un("mouseup", this.onMouseUp);
26669         
26670         this.fireEvent("hide", this);
26671     },
26672     
26673     onMouseUp : function()
26674     {
26675         this.hide();
26676     }
26677     
26678 });
26679
26680  
26681  /*
26682  * - LGPL
26683  *
26684  * menu item
26685  * 
26686  */
26687 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26688
26689 /**
26690  * @class Roo.bootstrap.menu.Item
26691  * @extends Roo.bootstrap.Component
26692  * Bootstrap MenuItem class
26693  * @cfg {Boolean} submenu (true | false) default false
26694  * @cfg {String} html text of the item
26695  * @cfg {String} href the link
26696  * @cfg {Boolean} disable (true | false) default false
26697  * @cfg {Boolean} preventDefault (true | false) default true
26698  * @cfg {String} icon Font awesome icon
26699  * @cfg {String} pos Submenu align to (left | right) default right 
26700  * 
26701  * 
26702  * @constructor
26703  * Create a new Item
26704  * @param {Object} config The config object
26705  */
26706
26707
26708 Roo.bootstrap.menu.Item = function(config){
26709     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26710     this.addEvents({
26711         /**
26712          * @event mouseover
26713          * Fires when the mouse is hovering over this menu
26714          * @param {Roo.bootstrap.menu.Item} this
26715          * @param {Roo.EventObject} e
26716          */
26717         mouseover : true,
26718         /**
26719          * @event mouseout
26720          * Fires when the mouse exits this menu
26721          * @param {Roo.bootstrap.menu.Item} this
26722          * @param {Roo.EventObject} e
26723          */
26724         mouseout : true,
26725         // raw events
26726         /**
26727          * @event click
26728          * The raw click event for the entire grid.
26729          * @param {Roo.EventObject} e
26730          */
26731         click : true
26732     });
26733 };
26734
26735 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26736     
26737     submenu : false,
26738     href : '',
26739     html : '',
26740     preventDefault: true,
26741     disable : false,
26742     icon : false,
26743     pos : 'right',
26744     
26745     getAutoCreate : function()
26746     {
26747         var text = [
26748             {
26749                 tag : 'span',
26750                 cls : 'roo-menu-item-text',
26751                 html : this.html
26752             }
26753         ];
26754         
26755         if(this.icon){
26756             text.unshift({
26757                 tag : 'i',
26758                 cls : 'fa ' + this.icon
26759             })
26760         }
26761         
26762         var cfg = {
26763             tag : 'li',
26764             cn : [
26765                 {
26766                     tag : 'a',
26767                     href : this.href || '#',
26768                     cn : text
26769                 }
26770             ]
26771         };
26772         
26773         if(this.disable){
26774             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26775         }
26776         
26777         if(this.submenu){
26778             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26779             
26780             if(this.pos == 'left'){
26781                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26782             }
26783         }
26784         
26785         return cfg;
26786     },
26787     
26788     initEvents : function() 
26789     {
26790         this.el.on('mouseover', this.onMouseOver, this);
26791         this.el.on('mouseout', this.onMouseOut, this);
26792         
26793         this.el.select('a', true).first().on('click', this.onClick, this);
26794         
26795     },
26796     
26797     onClick : function(e)
26798     {
26799         if(this.preventDefault){
26800             e.preventDefault();
26801         }
26802         
26803         this.fireEvent("click", this, e);
26804     },
26805     
26806     onMouseOver : function(e)
26807     {
26808         if(this.submenu && this.pos == 'left'){
26809             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26810         }
26811         
26812         this.fireEvent("mouseover", this, e);
26813     },
26814     
26815     onMouseOut : function(e)
26816     {
26817         this.fireEvent("mouseout", this, e);
26818     }
26819 });
26820
26821  
26822
26823  /*
26824  * - LGPL
26825  *
26826  * menu separator
26827  * 
26828  */
26829 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26830
26831 /**
26832  * @class Roo.bootstrap.menu.Separator
26833  * @extends Roo.bootstrap.Component
26834  * Bootstrap Separator class
26835  * 
26836  * @constructor
26837  * Create a new Separator
26838  * @param {Object} config The config object
26839  */
26840
26841
26842 Roo.bootstrap.menu.Separator = function(config){
26843     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26844 };
26845
26846 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26847     
26848     getAutoCreate : function(){
26849         var cfg = {
26850             tag : 'li',
26851             cls: 'divider'
26852         };
26853         
26854         return cfg;
26855     }
26856    
26857 });
26858
26859  
26860
26861  /*
26862  * - LGPL
26863  *
26864  * Tooltip
26865  * 
26866  */
26867
26868 /**
26869  * @class Roo.bootstrap.Tooltip
26870  * Bootstrap Tooltip class
26871  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26872  * to determine which dom element triggers the tooltip.
26873  * 
26874  * It needs to add support for additional attributes like tooltip-position
26875  * 
26876  * @constructor
26877  * Create a new Toolti
26878  * @param {Object} config The config object
26879  */
26880
26881 Roo.bootstrap.Tooltip = function(config){
26882     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26883     
26884     this.alignment = Roo.bootstrap.Tooltip.alignment;
26885     
26886     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26887         this.alignment = config.alignment;
26888     }
26889     
26890 };
26891
26892 Roo.apply(Roo.bootstrap.Tooltip, {
26893     /**
26894      * @function init initialize tooltip monitoring.
26895      * @static
26896      */
26897     currentEl : false,
26898     currentTip : false,
26899     currentRegion : false,
26900     
26901     //  init : delay?
26902     
26903     init : function()
26904     {
26905         Roo.get(document).on('mouseover', this.enter ,this);
26906         Roo.get(document).on('mouseout', this.leave, this);
26907          
26908         
26909         this.currentTip = new Roo.bootstrap.Tooltip();
26910     },
26911     
26912     enter : function(ev)
26913     {
26914         var dom = ev.getTarget();
26915         
26916         //Roo.log(['enter',dom]);
26917         var el = Roo.fly(dom);
26918         if (this.currentEl) {
26919             //Roo.log(dom);
26920             //Roo.log(this.currentEl);
26921             //Roo.log(this.currentEl.contains(dom));
26922             if (this.currentEl == el) {
26923                 return;
26924             }
26925             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26926                 return;
26927             }
26928
26929         }
26930         
26931         if (this.currentTip.el) {
26932             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26933         }    
26934         //Roo.log(ev);
26935         
26936         if(!el || el.dom == document){
26937             return;
26938         }
26939         
26940         var bindEl = el;
26941         
26942         // you can not look for children, as if el is the body.. then everythign is the child..
26943         if (!el.attr('tooltip')) { //
26944             if (!el.select("[tooltip]").elements.length) {
26945                 return;
26946             }
26947             // is the mouse over this child...?
26948             bindEl = el.select("[tooltip]").first();
26949             var xy = ev.getXY();
26950             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26951                 //Roo.log("not in region.");
26952                 return;
26953             }
26954             //Roo.log("child element over..");
26955             
26956         }
26957         this.currentEl = bindEl;
26958         this.currentTip.bind(bindEl);
26959         this.currentRegion = Roo.lib.Region.getRegion(dom);
26960         this.currentTip.enter();
26961         
26962     },
26963     leave : function(ev)
26964     {
26965         var dom = ev.getTarget();
26966         //Roo.log(['leave',dom]);
26967         if (!this.currentEl) {
26968             return;
26969         }
26970         
26971         
26972         if (dom != this.currentEl.dom) {
26973             return;
26974         }
26975         var xy = ev.getXY();
26976         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26977             return;
26978         }
26979         // only activate leave if mouse cursor is outside... bounding box..
26980         
26981         
26982         
26983         
26984         if (this.currentTip) {
26985             this.currentTip.leave();
26986         }
26987         //Roo.log('clear currentEl');
26988         this.currentEl = false;
26989         
26990         
26991     },
26992     alignment : {
26993         'left' : ['r-l', [-2,0], 'right'],
26994         'right' : ['l-r', [2,0], 'left'],
26995         'bottom' : ['t-b', [0,2], 'top'],
26996         'top' : [ 'b-t', [0,-2], 'bottom']
26997     }
26998     
26999 });
27000
27001
27002 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27003     
27004     
27005     bindEl : false,
27006     
27007     delay : null, // can be { show : 300 , hide: 500}
27008     
27009     timeout : null,
27010     
27011     hoverState : null, //???
27012     
27013     placement : 'bottom', 
27014     
27015     alignment : false,
27016     
27017     getAutoCreate : function(){
27018     
27019         var cfg = {
27020            cls : 'tooltip',
27021            role : 'tooltip',
27022            cn : [
27023                 {
27024                     cls : 'tooltip-arrow'
27025                 },
27026                 {
27027                     cls : 'tooltip-inner'
27028                 }
27029            ]
27030         };
27031         
27032         return cfg;
27033     },
27034     bind : function(el)
27035     {
27036         this.bindEl = el;
27037     },
27038       
27039     
27040     enter : function () {
27041        
27042         if (this.timeout != null) {
27043             clearTimeout(this.timeout);
27044         }
27045         
27046         this.hoverState = 'in';
27047          //Roo.log("enter - show");
27048         if (!this.delay || !this.delay.show) {
27049             this.show();
27050             return;
27051         }
27052         var _t = this;
27053         this.timeout = setTimeout(function () {
27054             if (_t.hoverState == 'in') {
27055                 _t.show();
27056             }
27057         }, this.delay.show);
27058     },
27059     leave : function()
27060     {
27061         clearTimeout(this.timeout);
27062     
27063         this.hoverState = 'out';
27064          if (!this.delay || !this.delay.hide) {
27065             this.hide();
27066             return;
27067         }
27068        
27069         var _t = this;
27070         this.timeout = setTimeout(function () {
27071             //Roo.log("leave - timeout");
27072             
27073             if (_t.hoverState == 'out') {
27074                 _t.hide();
27075                 Roo.bootstrap.Tooltip.currentEl = false;
27076             }
27077         }, delay);
27078     },
27079     
27080     show : function (msg)
27081     {
27082         if (!this.el) {
27083             this.render(document.body);
27084         }
27085         // set content.
27086         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27087         
27088         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27089         
27090         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27091         
27092         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27093         
27094         var placement = typeof this.placement == 'function' ?
27095             this.placement.call(this, this.el, on_el) :
27096             this.placement;
27097             
27098         var autoToken = /\s?auto?\s?/i;
27099         var autoPlace = autoToken.test(placement);
27100         if (autoPlace) {
27101             placement = placement.replace(autoToken, '') || 'top';
27102         }
27103         
27104         //this.el.detach()
27105         //this.el.setXY([0,0]);
27106         this.el.show();
27107         //this.el.dom.style.display='block';
27108         
27109         //this.el.appendTo(on_el);
27110         
27111         var p = this.getPosition();
27112         var box = this.el.getBox();
27113         
27114         if (autoPlace) {
27115             // fixme..
27116         }
27117         
27118         var align = this.alignment[placement];
27119         
27120         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27121         
27122         if(placement == 'top' || placement == 'bottom'){
27123             if(xy[0] < 0){
27124                 placement = 'right';
27125             }
27126             
27127             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27128                 placement = 'left';
27129             }
27130             
27131             var scroll = Roo.select('body', true).first().getScroll();
27132             
27133             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27134                 placement = 'top';
27135             }
27136             
27137             align = this.alignment[placement];
27138         }
27139         
27140         this.el.alignTo(this.bindEl, align[0],align[1]);
27141         //var arrow = this.el.select('.arrow',true).first();
27142         //arrow.set(align[2], 
27143         
27144         this.el.addClass(placement);
27145         
27146         this.el.addClass('in fade');
27147         
27148         this.hoverState = null;
27149         
27150         if (this.el.hasClass('fade')) {
27151             // fade it?
27152         }
27153         
27154     },
27155     hide : function()
27156     {
27157          
27158         if (!this.el) {
27159             return;
27160         }
27161         //this.el.setXY([0,0]);
27162         this.el.removeClass('in');
27163         //this.el.hide();
27164         
27165     }
27166     
27167 });
27168  
27169
27170  /*
27171  * - LGPL
27172  *
27173  * Location Picker
27174  * 
27175  */
27176
27177 /**
27178  * @class Roo.bootstrap.LocationPicker
27179  * @extends Roo.bootstrap.Component
27180  * Bootstrap LocationPicker class
27181  * @cfg {Number} latitude Position when init default 0
27182  * @cfg {Number} longitude Position when init default 0
27183  * @cfg {Number} zoom default 15
27184  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27185  * @cfg {Boolean} mapTypeControl default false
27186  * @cfg {Boolean} disableDoubleClickZoom default false
27187  * @cfg {Boolean} scrollwheel default true
27188  * @cfg {Boolean} streetViewControl default false
27189  * @cfg {Number} radius default 0
27190  * @cfg {String} locationName
27191  * @cfg {Boolean} draggable default true
27192  * @cfg {Boolean} enableAutocomplete default false
27193  * @cfg {Boolean} enableReverseGeocode default true
27194  * @cfg {String} markerTitle
27195  * 
27196  * @constructor
27197  * Create a new LocationPicker
27198  * @param {Object} config The config object
27199  */
27200
27201
27202 Roo.bootstrap.LocationPicker = function(config){
27203     
27204     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27205     
27206     this.addEvents({
27207         /**
27208          * @event initial
27209          * Fires when the picker initialized.
27210          * @param {Roo.bootstrap.LocationPicker} this
27211          * @param {Google Location} location
27212          */
27213         initial : true,
27214         /**
27215          * @event positionchanged
27216          * Fires when the picker position changed.
27217          * @param {Roo.bootstrap.LocationPicker} this
27218          * @param {Google Location} location
27219          */
27220         positionchanged : true,
27221         /**
27222          * @event resize
27223          * Fires when the map resize.
27224          * @param {Roo.bootstrap.LocationPicker} this
27225          */
27226         resize : true,
27227         /**
27228          * @event show
27229          * Fires when the map show.
27230          * @param {Roo.bootstrap.LocationPicker} this
27231          */
27232         show : true,
27233         /**
27234          * @event hide
27235          * Fires when the map hide.
27236          * @param {Roo.bootstrap.LocationPicker} this
27237          */
27238         hide : true,
27239         /**
27240          * @event mapClick
27241          * Fires when click the map.
27242          * @param {Roo.bootstrap.LocationPicker} this
27243          * @param {Map event} e
27244          */
27245         mapClick : true,
27246         /**
27247          * @event mapRightClick
27248          * Fires when right click the map.
27249          * @param {Roo.bootstrap.LocationPicker} this
27250          * @param {Map event} e
27251          */
27252         mapRightClick : true,
27253         /**
27254          * @event markerClick
27255          * Fires when click the marker.
27256          * @param {Roo.bootstrap.LocationPicker} this
27257          * @param {Map event} e
27258          */
27259         markerClick : true,
27260         /**
27261          * @event markerRightClick
27262          * Fires when right click the marker.
27263          * @param {Roo.bootstrap.LocationPicker} this
27264          * @param {Map event} e
27265          */
27266         markerRightClick : true,
27267         /**
27268          * @event OverlayViewDraw
27269          * Fires when OverlayView Draw
27270          * @param {Roo.bootstrap.LocationPicker} this
27271          */
27272         OverlayViewDraw : true,
27273         /**
27274          * @event OverlayViewOnAdd
27275          * Fires when OverlayView Draw
27276          * @param {Roo.bootstrap.LocationPicker} this
27277          */
27278         OverlayViewOnAdd : true,
27279         /**
27280          * @event OverlayViewOnRemove
27281          * Fires when OverlayView Draw
27282          * @param {Roo.bootstrap.LocationPicker} this
27283          */
27284         OverlayViewOnRemove : true,
27285         /**
27286          * @event OverlayViewShow
27287          * Fires when OverlayView Draw
27288          * @param {Roo.bootstrap.LocationPicker} this
27289          * @param {Pixel} cpx
27290          */
27291         OverlayViewShow : true,
27292         /**
27293          * @event OverlayViewHide
27294          * Fires when OverlayView Draw
27295          * @param {Roo.bootstrap.LocationPicker} this
27296          */
27297         OverlayViewHide : true,
27298         /**
27299          * @event loadexception
27300          * Fires when load google lib failed.
27301          * @param {Roo.bootstrap.LocationPicker} this
27302          */
27303         loadexception : true
27304     });
27305         
27306 };
27307
27308 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27309     
27310     gMapContext: false,
27311     
27312     latitude: 0,
27313     longitude: 0,
27314     zoom: 15,
27315     mapTypeId: false,
27316     mapTypeControl: false,
27317     disableDoubleClickZoom: false,
27318     scrollwheel: true,
27319     streetViewControl: false,
27320     radius: 0,
27321     locationName: '',
27322     draggable: true,
27323     enableAutocomplete: false,
27324     enableReverseGeocode: true,
27325     markerTitle: '',
27326     
27327     getAutoCreate: function()
27328     {
27329
27330         var cfg = {
27331             tag: 'div',
27332             cls: 'roo-location-picker'
27333         };
27334         
27335         return cfg
27336     },
27337     
27338     initEvents: function(ct, position)
27339     {       
27340         if(!this.el.getWidth() || this.isApplied()){
27341             return;
27342         }
27343         
27344         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27345         
27346         this.initial();
27347     },
27348     
27349     initial: function()
27350     {
27351         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27352             this.fireEvent('loadexception', this);
27353             return;
27354         }
27355         
27356         if(!this.mapTypeId){
27357             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27358         }
27359         
27360         this.gMapContext = this.GMapContext();
27361         
27362         this.initOverlayView();
27363         
27364         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27365         
27366         var _this = this;
27367                 
27368         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27369             _this.setPosition(_this.gMapContext.marker.position);
27370         });
27371         
27372         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27373             _this.fireEvent('mapClick', this, event);
27374             
27375         });
27376
27377         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27378             _this.fireEvent('mapRightClick', this, event);
27379             
27380         });
27381         
27382         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27383             _this.fireEvent('markerClick', this, event);
27384             
27385         });
27386
27387         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27388             _this.fireEvent('markerRightClick', this, event);
27389             
27390         });
27391         
27392         this.setPosition(this.gMapContext.location);
27393         
27394         this.fireEvent('initial', this, this.gMapContext.location);
27395     },
27396     
27397     initOverlayView: function()
27398     {
27399         var _this = this;
27400         
27401         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27402             
27403             draw: function()
27404             {
27405                 _this.fireEvent('OverlayViewDraw', _this);
27406             },
27407             
27408             onAdd: function()
27409             {
27410                 _this.fireEvent('OverlayViewOnAdd', _this);
27411             },
27412             
27413             onRemove: function()
27414             {
27415                 _this.fireEvent('OverlayViewOnRemove', _this);
27416             },
27417             
27418             show: function(cpx)
27419             {
27420                 _this.fireEvent('OverlayViewShow', _this, cpx);
27421             },
27422             
27423             hide: function()
27424             {
27425                 _this.fireEvent('OverlayViewHide', _this);
27426             }
27427             
27428         });
27429     },
27430     
27431     fromLatLngToContainerPixel: function(event)
27432     {
27433         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27434     },
27435     
27436     isApplied: function() 
27437     {
27438         return this.getGmapContext() == false ? false : true;
27439     },
27440     
27441     getGmapContext: function() 
27442     {
27443         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27444     },
27445     
27446     GMapContext: function() 
27447     {
27448         var position = new google.maps.LatLng(this.latitude, this.longitude);
27449         
27450         var _map = new google.maps.Map(this.el.dom, {
27451             center: position,
27452             zoom: this.zoom,
27453             mapTypeId: this.mapTypeId,
27454             mapTypeControl: this.mapTypeControl,
27455             disableDoubleClickZoom: this.disableDoubleClickZoom,
27456             scrollwheel: this.scrollwheel,
27457             streetViewControl: this.streetViewControl,
27458             locationName: this.locationName,
27459             draggable: this.draggable,
27460             enableAutocomplete: this.enableAutocomplete,
27461             enableReverseGeocode: this.enableReverseGeocode
27462         });
27463         
27464         var _marker = new google.maps.Marker({
27465             position: position,
27466             map: _map,
27467             title: this.markerTitle,
27468             draggable: this.draggable
27469         });
27470         
27471         return {
27472             map: _map,
27473             marker: _marker,
27474             circle: null,
27475             location: position,
27476             radius: this.radius,
27477             locationName: this.locationName,
27478             addressComponents: {
27479                 formatted_address: null,
27480                 addressLine1: null,
27481                 addressLine2: null,
27482                 streetName: null,
27483                 streetNumber: null,
27484                 city: null,
27485                 district: null,
27486                 state: null,
27487                 stateOrProvince: null
27488             },
27489             settings: this,
27490             domContainer: this.el.dom,
27491             geodecoder: new google.maps.Geocoder()
27492         };
27493     },
27494     
27495     drawCircle: function(center, radius, options) 
27496     {
27497         if (this.gMapContext.circle != null) {
27498             this.gMapContext.circle.setMap(null);
27499         }
27500         if (radius > 0) {
27501             radius *= 1;
27502             options = Roo.apply({}, options, {
27503                 strokeColor: "#0000FF",
27504                 strokeOpacity: .35,
27505                 strokeWeight: 2,
27506                 fillColor: "#0000FF",
27507                 fillOpacity: .2
27508             });
27509             
27510             options.map = this.gMapContext.map;
27511             options.radius = radius;
27512             options.center = center;
27513             this.gMapContext.circle = new google.maps.Circle(options);
27514             return this.gMapContext.circle;
27515         }
27516         
27517         return null;
27518     },
27519     
27520     setPosition: function(location) 
27521     {
27522         this.gMapContext.location = location;
27523         this.gMapContext.marker.setPosition(location);
27524         this.gMapContext.map.panTo(location);
27525         this.drawCircle(location, this.gMapContext.radius, {});
27526         
27527         var _this = this;
27528         
27529         if (this.gMapContext.settings.enableReverseGeocode) {
27530             this.gMapContext.geodecoder.geocode({
27531                 latLng: this.gMapContext.location
27532             }, function(results, status) {
27533                 
27534                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27535                     _this.gMapContext.locationName = results[0].formatted_address;
27536                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27537                     
27538                     _this.fireEvent('positionchanged', this, location);
27539                 }
27540             });
27541             
27542             return;
27543         }
27544         
27545         this.fireEvent('positionchanged', this, location);
27546     },
27547     
27548     resize: function()
27549     {
27550         google.maps.event.trigger(this.gMapContext.map, "resize");
27551         
27552         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27553         
27554         this.fireEvent('resize', this);
27555     },
27556     
27557     setPositionByLatLng: function(latitude, longitude)
27558     {
27559         this.setPosition(new google.maps.LatLng(latitude, longitude));
27560     },
27561     
27562     getCurrentPosition: function() 
27563     {
27564         return {
27565             latitude: this.gMapContext.location.lat(),
27566             longitude: this.gMapContext.location.lng()
27567         };
27568     },
27569     
27570     getAddressName: function() 
27571     {
27572         return this.gMapContext.locationName;
27573     },
27574     
27575     getAddressComponents: function() 
27576     {
27577         return this.gMapContext.addressComponents;
27578     },
27579     
27580     address_component_from_google_geocode: function(address_components) 
27581     {
27582         var result = {};
27583         
27584         for (var i = 0; i < address_components.length; i++) {
27585             var component = address_components[i];
27586             if (component.types.indexOf("postal_code") >= 0) {
27587                 result.postalCode = component.short_name;
27588             } else if (component.types.indexOf("street_number") >= 0) {
27589                 result.streetNumber = component.short_name;
27590             } else if (component.types.indexOf("route") >= 0) {
27591                 result.streetName = component.short_name;
27592             } else if (component.types.indexOf("neighborhood") >= 0) {
27593                 result.city = component.short_name;
27594             } else if (component.types.indexOf("locality") >= 0) {
27595                 result.city = component.short_name;
27596             } else if (component.types.indexOf("sublocality") >= 0) {
27597                 result.district = component.short_name;
27598             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27599                 result.stateOrProvince = component.short_name;
27600             } else if (component.types.indexOf("country") >= 0) {
27601                 result.country = component.short_name;
27602             }
27603         }
27604         
27605         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27606         result.addressLine2 = "";
27607         return result;
27608     },
27609     
27610     setZoomLevel: function(zoom)
27611     {
27612         this.gMapContext.map.setZoom(zoom);
27613     },
27614     
27615     show: function()
27616     {
27617         if(!this.el){
27618             return;
27619         }
27620         
27621         this.el.show();
27622         
27623         this.resize();
27624         
27625         this.fireEvent('show', this);
27626     },
27627     
27628     hide: function()
27629     {
27630         if(!this.el){
27631             return;
27632         }
27633         
27634         this.el.hide();
27635         
27636         this.fireEvent('hide', this);
27637     }
27638     
27639 });
27640
27641 Roo.apply(Roo.bootstrap.LocationPicker, {
27642     
27643     OverlayView : function(map, options)
27644     {
27645         options = options || {};
27646         
27647         this.setMap(map);
27648     }
27649     
27650     
27651 });/**
27652  * @class Roo.bootstrap.Alert
27653  * @extends Roo.bootstrap.Component
27654  * Bootstrap Alert class - shows an alert area box
27655  * eg
27656  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27657   Enter a valid email address
27658 </div>
27659  * @licence LGPL
27660  * @cfg {String} title The title of alert
27661  * @cfg {String} html The content of alert
27662  * @cfg {String} weight (  success | info | warning | danger )
27663  * @cfg {String} faicon font-awesomeicon
27664  * 
27665  * @constructor
27666  * Create a new alert
27667  * @param {Object} config The config object
27668  */
27669
27670
27671 Roo.bootstrap.Alert = function(config){
27672     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27673     
27674 };
27675
27676 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27677     
27678     title: '',
27679     html: '',
27680     weight: false,
27681     faicon: false,
27682     
27683     getAutoCreate : function()
27684     {
27685         
27686         var cfg = {
27687             tag : 'div',
27688             cls : 'alert',
27689             cn : [
27690                 {
27691                     tag : 'i',
27692                     cls : 'roo-alert-icon'
27693                     
27694                 },
27695                 {
27696                     tag : 'b',
27697                     cls : 'roo-alert-title',
27698                     html : this.title
27699                 },
27700                 {
27701                     tag : 'span',
27702                     cls : 'roo-alert-text',
27703                     html : this.html
27704                 }
27705             ]
27706         };
27707         
27708         if(this.faicon){
27709             cfg.cn[0].cls += ' fa ' + this.faicon;
27710         }
27711         
27712         if(this.weight){
27713             cfg.cls += ' alert-' + this.weight;
27714         }
27715         
27716         return cfg;
27717     },
27718     
27719     initEvents: function() 
27720     {
27721         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27722     },
27723     
27724     setTitle : function(str)
27725     {
27726         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27727     },
27728     
27729     setText : function(str)
27730     {
27731         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27732     },
27733     
27734     setWeight : function(weight)
27735     {
27736         if(this.weight){
27737             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27738         }
27739         
27740         this.weight = weight;
27741         
27742         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27743     },
27744     
27745     setIcon : function(icon)
27746     {
27747         if(this.faicon){
27748             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27749         }
27750         
27751         this.faicon = icon;
27752         
27753         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27754     },
27755     
27756     hide: function() 
27757     {
27758         this.el.hide();   
27759     },
27760     
27761     show: function() 
27762     {  
27763         this.el.show();   
27764     }
27765     
27766 });
27767
27768  
27769 /*
27770 * Licence: LGPL
27771 */
27772
27773 /**
27774  * @class Roo.bootstrap.UploadCropbox
27775  * @extends Roo.bootstrap.Component
27776  * Bootstrap UploadCropbox class
27777  * @cfg {String} emptyText show when image has been loaded
27778  * @cfg {String} rotateNotify show when image too small to rotate
27779  * @cfg {Number} errorTimeout default 3000
27780  * @cfg {Number} minWidth default 300
27781  * @cfg {Number} minHeight default 300
27782  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27783  * @cfg {Boolean} isDocument (true|false) default false
27784  * @cfg {String} url action url
27785  * @cfg {String} paramName default 'imageUpload'
27786  * @cfg {String} method default POST
27787  * @cfg {Boolean} loadMask (true|false) default true
27788  * @cfg {Boolean} loadingText default 'Loading...'
27789  * 
27790  * @constructor
27791  * Create a new UploadCropbox
27792  * @param {Object} config The config object
27793  */
27794
27795 Roo.bootstrap.UploadCropbox = function(config){
27796     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27797     
27798     this.addEvents({
27799         /**
27800          * @event beforeselectfile
27801          * Fire before select file
27802          * @param {Roo.bootstrap.UploadCropbox} this
27803          */
27804         "beforeselectfile" : true,
27805         /**
27806          * @event initial
27807          * Fire after initEvent
27808          * @param {Roo.bootstrap.UploadCropbox} this
27809          */
27810         "initial" : true,
27811         /**
27812          * @event crop
27813          * Fire after initEvent
27814          * @param {Roo.bootstrap.UploadCropbox} this
27815          * @param {String} data
27816          */
27817         "crop" : true,
27818         /**
27819          * @event prepare
27820          * Fire when preparing the file data
27821          * @param {Roo.bootstrap.UploadCropbox} this
27822          * @param {Object} file
27823          */
27824         "prepare" : true,
27825         /**
27826          * @event exception
27827          * Fire when get exception
27828          * @param {Roo.bootstrap.UploadCropbox} this
27829          * @param {XMLHttpRequest} xhr
27830          */
27831         "exception" : true,
27832         /**
27833          * @event beforeloadcanvas
27834          * Fire before load the canvas
27835          * @param {Roo.bootstrap.UploadCropbox} this
27836          * @param {String} src
27837          */
27838         "beforeloadcanvas" : true,
27839         /**
27840          * @event trash
27841          * Fire when trash image
27842          * @param {Roo.bootstrap.UploadCropbox} this
27843          */
27844         "trash" : true,
27845         /**
27846          * @event download
27847          * Fire when download the image
27848          * @param {Roo.bootstrap.UploadCropbox} this
27849          */
27850         "download" : true,
27851         /**
27852          * @event footerbuttonclick
27853          * Fire when footerbuttonclick
27854          * @param {Roo.bootstrap.UploadCropbox} this
27855          * @param {String} type
27856          */
27857         "footerbuttonclick" : true,
27858         /**
27859          * @event resize
27860          * Fire when resize
27861          * @param {Roo.bootstrap.UploadCropbox} this
27862          */
27863         "resize" : true,
27864         /**
27865          * @event rotate
27866          * Fire when rotate the image
27867          * @param {Roo.bootstrap.UploadCropbox} this
27868          * @param {String} pos
27869          */
27870         "rotate" : true,
27871         /**
27872          * @event inspect
27873          * Fire when inspect the file
27874          * @param {Roo.bootstrap.UploadCropbox} this
27875          * @param {Object} file
27876          */
27877         "inspect" : true,
27878         /**
27879          * @event upload
27880          * Fire when xhr upload the file
27881          * @param {Roo.bootstrap.UploadCropbox} this
27882          * @param {Object} data
27883          */
27884         "upload" : true,
27885         /**
27886          * @event arrange
27887          * Fire when arrange the file data
27888          * @param {Roo.bootstrap.UploadCropbox} this
27889          * @param {Object} formData
27890          */
27891         "arrange" : true
27892     });
27893     
27894     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27895 };
27896
27897 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27898     
27899     emptyText : 'Click to upload image',
27900     rotateNotify : 'Image is too small to rotate',
27901     errorTimeout : 3000,
27902     scale : 0,
27903     baseScale : 1,
27904     rotate : 0,
27905     dragable : false,
27906     pinching : false,
27907     mouseX : 0,
27908     mouseY : 0,
27909     cropData : false,
27910     minWidth : 300,
27911     minHeight : 300,
27912     file : false,
27913     exif : {},
27914     baseRotate : 1,
27915     cropType : 'image/jpeg',
27916     buttons : false,
27917     canvasLoaded : false,
27918     isDocument : false,
27919     method : 'POST',
27920     paramName : 'imageUpload',
27921     loadMask : true,
27922     loadingText : 'Loading...',
27923     maskEl : false,
27924     
27925     getAutoCreate : function()
27926     {
27927         var cfg = {
27928             tag : 'div',
27929             cls : 'roo-upload-cropbox',
27930             cn : [
27931                 {
27932                     tag : 'input',
27933                     cls : 'roo-upload-cropbox-selector',
27934                     type : 'file'
27935                 },
27936                 {
27937                     tag : 'div',
27938                     cls : 'roo-upload-cropbox-body',
27939                     style : 'cursor:pointer',
27940                     cn : [
27941                         {
27942                             tag : 'div',
27943                             cls : 'roo-upload-cropbox-preview'
27944                         },
27945                         {
27946                             tag : 'div',
27947                             cls : 'roo-upload-cropbox-thumb'
27948                         },
27949                         {
27950                             tag : 'div',
27951                             cls : 'roo-upload-cropbox-empty-notify',
27952                             html : this.emptyText
27953                         },
27954                         {
27955                             tag : 'div',
27956                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27957                             html : this.rotateNotify
27958                         }
27959                     ]
27960                 },
27961                 {
27962                     tag : 'div',
27963                     cls : 'roo-upload-cropbox-footer',
27964                     cn : {
27965                         tag : 'div',
27966                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27967                         cn : []
27968                     }
27969                 }
27970             ]
27971         };
27972         
27973         return cfg;
27974     },
27975     
27976     onRender : function(ct, position)
27977     {
27978         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27979         
27980         if (this.buttons.length) {
27981             
27982             Roo.each(this.buttons, function(bb) {
27983                 
27984                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27985                 
27986                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27987                 
27988             }, this);
27989         }
27990         
27991         if(this.loadMask){
27992             this.maskEl = this.el;
27993         }
27994     },
27995     
27996     initEvents : function()
27997     {
27998         this.urlAPI = (window.createObjectURL && window) || 
27999                                 (window.URL && URL.revokeObjectURL && URL) || 
28000                                 (window.webkitURL && webkitURL);
28001                         
28002         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28003         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28004         
28005         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28006         this.selectorEl.hide();
28007         
28008         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28009         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28010         
28011         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28012         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28013         this.thumbEl.hide();
28014         
28015         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28016         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28017         
28018         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28019         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28020         this.errorEl.hide();
28021         
28022         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28023         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28024         this.footerEl.hide();
28025         
28026         this.setThumbBoxSize();
28027         
28028         this.bind();
28029         
28030         this.resize();
28031         
28032         this.fireEvent('initial', this);
28033     },
28034
28035     bind : function()
28036     {
28037         var _this = this;
28038         
28039         window.addEventListener("resize", function() { _this.resize(); } );
28040         
28041         this.bodyEl.on('click', this.beforeSelectFile, this);
28042         
28043         if(Roo.isTouch){
28044             this.bodyEl.on('touchstart', this.onTouchStart, this);
28045             this.bodyEl.on('touchmove', this.onTouchMove, this);
28046             this.bodyEl.on('touchend', this.onTouchEnd, this);
28047         }
28048         
28049         if(!Roo.isTouch){
28050             this.bodyEl.on('mousedown', this.onMouseDown, this);
28051             this.bodyEl.on('mousemove', this.onMouseMove, this);
28052             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28053             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28054             Roo.get(document).on('mouseup', this.onMouseUp, this);
28055         }
28056         
28057         this.selectorEl.on('change', this.onFileSelected, this);
28058     },
28059     
28060     reset : function()
28061     {    
28062         this.scale = 0;
28063         this.baseScale = 1;
28064         this.rotate = 0;
28065         this.baseRotate = 1;
28066         this.dragable = false;
28067         this.pinching = false;
28068         this.mouseX = 0;
28069         this.mouseY = 0;
28070         this.cropData = false;
28071         this.notifyEl.dom.innerHTML = this.emptyText;
28072         
28073         this.selectorEl.dom.value = '';
28074         
28075     },
28076     
28077     resize : function()
28078     {
28079         if(this.fireEvent('resize', this) != false){
28080             this.setThumbBoxPosition();
28081             this.setCanvasPosition();
28082         }
28083     },
28084     
28085     onFooterButtonClick : function(e, el, o, type)
28086     {
28087         switch (type) {
28088             case 'rotate-left' :
28089                 this.onRotateLeft(e);
28090                 break;
28091             case 'rotate-right' :
28092                 this.onRotateRight(e);
28093                 break;
28094             case 'picture' :
28095                 this.beforeSelectFile(e);
28096                 break;
28097             case 'trash' :
28098                 this.trash(e);
28099                 break;
28100             case 'crop' :
28101                 this.crop(e);
28102                 break;
28103             case 'download' :
28104                 this.download(e);
28105                 break;
28106             default :
28107                 break;
28108         }
28109         
28110         this.fireEvent('footerbuttonclick', this, type);
28111     },
28112     
28113     beforeSelectFile : function(e)
28114     {
28115         e.preventDefault();
28116         
28117         if(this.fireEvent('beforeselectfile', this) != false){
28118             this.selectorEl.dom.click();
28119         }
28120     },
28121     
28122     onFileSelected : function(e)
28123     {
28124         e.preventDefault();
28125         
28126         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28127             return;
28128         }
28129         
28130         var file = this.selectorEl.dom.files[0];
28131         
28132         if(this.fireEvent('inspect', this, file) != false){
28133             this.prepare(file);
28134         }
28135         
28136     },
28137     
28138     trash : function(e)
28139     {
28140         this.fireEvent('trash', this);
28141     },
28142     
28143     download : function(e)
28144     {
28145         this.fireEvent('download', this);
28146     },
28147     
28148     loadCanvas : function(src)
28149     {   
28150         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28151             
28152             this.reset();
28153             
28154             this.imageEl = document.createElement('img');
28155             
28156             var _this = this;
28157             
28158             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28159             
28160             this.imageEl.src = src;
28161         }
28162     },
28163     
28164     onLoadCanvas : function()
28165     {   
28166         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28167         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28168         
28169         this.bodyEl.un('click', this.beforeSelectFile, this);
28170         
28171         this.notifyEl.hide();
28172         this.thumbEl.show();
28173         this.footerEl.show();
28174         
28175         this.baseRotateLevel();
28176         
28177         if(this.isDocument){
28178             this.setThumbBoxSize();
28179         }
28180         
28181         this.setThumbBoxPosition();
28182         
28183         this.baseScaleLevel();
28184         
28185         this.draw();
28186         
28187         this.resize();
28188         
28189         this.canvasLoaded = true;
28190         
28191         if(this.loadMask){
28192             this.maskEl.unmask();
28193         }
28194         
28195     },
28196     
28197     setCanvasPosition : function()
28198     {   
28199         if(!this.canvasEl){
28200             return;
28201         }
28202         
28203         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28204         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28205         
28206         this.previewEl.setLeft(pw);
28207         this.previewEl.setTop(ph);
28208         
28209     },
28210     
28211     onMouseDown : function(e)
28212     {   
28213         e.stopEvent();
28214         
28215         this.dragable = true;
28216         this.pinching = false;
28217         
28218         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28219             this.dragable = false;
28220             return;
28221         }
28222         
28223         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28224         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28225         
28226     },
28227     
28228     onMouseMove : function(e)
28229     {   
28230         e.stopEvent();
28231         
28232         if(!this.canvasLoaded){
28233             return;
28234         }
28235         
28236         if (!this.dragable){
28237             return;
28238         }
28239         
28240         var minX = Math.ceil(this.thumbEl.getLeft(true));
28241         var minY = Math.ceil(this.thumbEl.getTop(true));
28242         
28243         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28244         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28245         
28246         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28247         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28248         
28249         x = x - this.mouseX;
28250         y = y - this.mouseY;
28251         
28252         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28253         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28254         
28255         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28256         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28257         
28258         this.previewEl.setLeft(bgX);
28259         this.previewEl.setTop(bgY);
28260         
28261         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28262         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28263     },
28264     
28265     onMouseUp : function(e)
28266     {   
28267         e.stopEvent();
28268         
28269         this.dragable = false;
28270     },
28271     
28272     onMouseWheel : function(e)
28273     {   
28274         e.stopEvent();
28275         
28276         this.startScale = this.scale;
28277         
28278         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28279         
28280         if(!this.zoomable()){
28281             this.scale = this.startScale;
28282             return;
28283         }
28284         
28285         this.draw();
28286         
28287         return;
28288     },
28289     
28290     zoomable : function()
28291     {
28292         var minScale = this.thumbEl.getWidth() / this.minWidth;
28293         
28294         if(this.minWidth < this.minHeight){
28295             minScale = this.thumbEl.getHeight() / this.minHeight;
28296         }
28297         
28298         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28299         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28300         
28301         if(
28302                 this.isDocument &&
28303                 (this.rotate == 0 || this.rotate == 180) && 
28304                 (
28305                     width > this.imageEl.OriginWidth || 
28306                     height > this.imageEl.OriginHeight ||
28307                     (width < this.minWidth && height < this.minHeight)
28308                 )
28309         ){
28310             return false;
28311         }
28312         
28313         if(
28314                 this.isDocument &&
28315                 (this.rotate == 90 || this.rotate == 270) && 
28316                 (
28317                     width > this.imageEl.OriginWidth || 
28318                     height > this.imageEl.OriginHeight ||
28319                     (width < this.minHeight && height < this.minWidth)
28320                 )
28321         ){
28322             return false;
28323         }
28324         
28325         if(
28326                 !this.isDocument &&
28327                 (this.rotate == 0 || this.rotate == 180) && 
28328                 (
28329                     width < this.minWidth || 
28330                     width > this.imageEl.OriginWidth || 
28331                     height < this.minHeight || 
28332                     height > this.imageEl.OriginHeight
28333                 )
28334         ){
28335             return false;
28336         }
28337         
28338         if(
28339                 !this.isDocument &&
28340                 (this.rotate == 90 || this.rotate == 270) && 
28341                 (
28342                     width < this.minHeight || 
28343                     width > this.imageEl.OriginWidth || 
28344                     height < this.minWidth || 
28345                     height > this.imageEl.OriginHeight
28346                 )
28347         ){
28348             return false;
28349         }
28350         
28351         return true;
28352         
28353     },
28354     
28355     onRotateLeft : function(e)
28356     {   
28357         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28358             
28359             var minScale = this.thumbEl.getWidth() / this.minWidth;
28360             
28361             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28362             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28363             
28364             this.startScale = this.scale;
28365             
28366             while (this.getScaleLevel() < minScale){
28367             
28368                 this.scale = this.scale + 1;
28369                 
28370                 if(!this.zoomable()){
28371                     break;
28372                 }
28373                 
28374                 if(
28375                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28376                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28377                 ){
28378                     continue;
28379                 }
28380                 
28381                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28382
28383                 this.draw();
28384                 
28385                 return;
28386             }
28387             
28388             this.scale = this.startScale;
28389             
28390             this.onRotateFail();
28391             
28392             return false;
28393         }
28394         
28395         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28396
28397         if(this.isDocument){
28398             this.setThumbBoxSize();
28399             this.setThumbBoxPosition();
28400             this.setCanvasPosition();
28401         }
28402         
28403         this.draw();
28404         
28405         this.fireEvent('rotate', this, 'left');
28406         
28407     },
28408     
28409     onRotateRight : function(e)
28410     {
28411         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28412             
28413             var minScale = this.thumbEl.getWidth() / this.minWidth;
28414         
28415             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28416             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28417             
28418             this.startScale = this.scale;
28419             
28420             while (this.getScaleLevel() < minScale){
28421             
28422                 this.scale = this.scale + 1;
28423                 
28424                 if(!this.zoomable()){
28425                     break;
28426                 }
28427                 
28428                 if(
28429                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28430                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28431                 ){
28432                     continue;
28433                 }
28434                 
28435                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28436
28437                 this.draw();
28438                 
28439                 return;
28440             }
28441             
28442             this.scale = this.startScale;
28443             
28444             this.onRotateFail();
28445             
28446             return false;
28447         }
28448         
28449         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28450
28451         if(this.isDocument){
28452             this.setThumbBoxSize();
28453             this.setThumbBoxPosition();
28454             this.setCanvasPosition();
28455         }
28456         
28457         this.draw();
28458         
28459         this.fireEvent('rotate', this, 'right');
28460     },
28461     
28462     onRotateFail : function()
28463     {
28464         this.errorEl.show(true);
28465         
28466         var _this = this;
28467         
28468         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28469     },
28470     
28471     draw : function()
28472     {
28473         this.previewEl.dom.innerHTML = '';
28474         
28475         var canvasEl = document.createElement("canvas");
28476         
28477         var contextEl = canvasEl.getContext("2d");
28478         
28479         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28480         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28481         var center = this.imageEl.OriginWidth / 2;
28482         
28483         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28484             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28485             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28486             center = this.imageEl.OriginHeight / 2;
28487         }
28488         
28489         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28490         
28491         contextEl.translate(center, center);
28492         contextEl.rotate(this.rotate * Math.PI / 180);
28493
28494         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28495         
28496         this.canvasEl = document.createElement("canvas");
28497         
28498         this.contextEl = this.canvasEl.getContext("2d");
28499         
28500         switch (this.rotate) {
28501             case 0 :
28502                 
28503                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28504                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28505                 
28506                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28507                 
28508                 break;
28509             case 90 : 
28510                 
28511                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28512                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28513                 
28514                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28515                     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);
28516                     break;
28517                 }
28518                 
28519                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28520                 
28521                 break;
28522             case 180 :
28523                 
28524                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28525                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28526                 
28527                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28528                     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);
28529                     break;
28530                 }
28531                 
28532                 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);
28533                 
28534                 break;
28535             case 270 :
28536                 
28537                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28538                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28539         
28540                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28541                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28542                     break;
28543                 }
28544                 
28545                 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);
28546                 
28547                 break;
28548             default : 
28549                 break;
28550         }
28551         
28552         this.previewEl.appendChild(this.canvasEl);
28553         
28554         this.setCanvasPosition();
28555     },
28556     
28557     crop : function()
28558     {
28559         if(!this.canvasLoaded){
28560             return;
28561         }
28562         
28563         var imageCanvas = document.createElement("canvas");
28564         
28565         var imageContext = imageCanvas.getContext("2d");
28566         
28567         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28568         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28569         
28570         var center = imageCanvas.width / 2;
28571         
28572         imageContext.translate(center, center);
28573         
28574         imageContext.rotate(this.rotate * Math.PI / 180);
28575         
28576         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28577         
28578         var canvas = document.createElement("canvas");
28579         
28580         var context = canvas.getContext("2d");
28581                 
28582         canvas.width = this.minWidth;
28583         canvas.height = this.minHeight;
28584
28585         switch (this.rotate) {
28586             case 0 :
28587                 
28588                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28589                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28590                 
28591                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28592                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28593                 
28594                 var targetWidth = this.minWidth - 2 * x;
28595                 var targetHeight = this.minHeight - 2 * y;
28596                 
28597                 var scale = 1;
28598                 
28599                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28600                     scale = targetWidth / width;
28601                 }
28602                 
28603                 if(x > 0 && y == 0){
28604                     scale = targetHeight / height;
28605                 }
28606                 
28607                 if(x > 0 && y > 0){
28608                     scale = targetWidth / width;
28609                     
28610                     if(width < height){
28611                         scale = targetHeight / height;
28612                     }
28613                 }
28614                 
28615                 context.scale(scale, scale);
28616                 
28617                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28618                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28619
28620                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28621                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28622
28623                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28624                 
28625                 break;
28626             case 90 : 
28627                 
28628                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28629                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28630                 
28631                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28632                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28633                 
28634                 var targetWidth = this.minWidth - 2 * x;
28635                 var targetHeight = this.minHeight - 2 * y;
28636                 
28637                 var scale = 1;
28638                 
28639                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28640                     scale = targetWidth / width;
28641                 }
28642                 
28643                 if(x > 0 && y == 0){
28644                     scale = targetHeight / height;
28645                 }
28646                 
28647                 if(x > 0 && y > 0){
28648                     scale = targetWidth / width;
28649                     
28650                     if(width < height){
28651                         scale = targetHeight / height;
28652                     }
28653                 }
28654                 
28655                 context.scale(scale, scale);
28656                 
28657                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28658                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28659
28660                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28661                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28662                 
28663                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28664                 
28665                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28666                 
28667                 break;
28668             case 180 :
28669                 
28670                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28671                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28672                 
28673                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28674                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28675                 
28676                 var targetWidth = this.minWidth - 2 * x;
28677                 var targetHeight = this.minHeight - 2 * y;
28678                 
28679                 var scale = 1;
28680                 
28681                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28682                     scale = targetWidth / width;
28683                 }
28684                 
28685                 if(x > 0 && y == 0){
28686                     scale = targetHeight / height;
28687                 }
28688                 
28689                 if(x > 0 && y > 0){
28690                     scale = targetWidth / width;
28691                     
28692                     if(width < height){
28693                         scale = targetHeight / height;
28694                     }
28695                 }
28696                 
28697                 context.scale(scale, scale);
28698                 
28699                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28700                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28701
28702                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28703                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28704
28705                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28706                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28707                 
28708                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28709                 
28710                 break;
28711             case 270 :
28712                 
28713                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28714                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28715                 
28716                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28717                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28718                 
28719                 var targetWidth = this.minWidth - 2 * x;
28720                 var targetHeight = this.minHeight - 2 * y;
28721                 
28722                 var scale = 1;
28723                 
28724                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28725                     scale = targetWidth / width;
28726                 }
28727                 
28728                 if(x > 0 && y == 0){
28729                     scale = targetHeight / height;
28730                 }
28731                 
28732                 if(x > 0 && y > 0){
28733                     scale = targetWidth / width;
28734                     
28735                     if(width < height){
28736                         scale = targetHeight / height;
28737                     }
28738                 }
28739                 
28740                 context.scale(scale, scale);
28741                 
28742                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28743                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28744
28745                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28746                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28747                 
28748                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28749                 
28750                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28751                 
28752                 break;
28753             default : 
28754                 break;
28755         }
28756         
28757         this.cropData = canvas.toDataURL(this.cropType);
28758         
28759         if(this.fireEvent('crop', this, this.cropData) !== false){
28760             this.process(this.file, this.cropData);
28761         }
28762         
28763         return;
28764         
28765     },
28766     
28767     setThumbBoxSize : function()
28768     {
28769         var width, height;
28770         
28771         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28772             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28773             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28774             
28775             this.minWidth = width;
28776             this.minHeight = height;
28777             
28778             if(this.rotate == 90 || this.rotate == 270){
28779                 this.minWidth = height;
28780                 this.minHeight = width;
28781             }
28782         }
28783         
28784         height = 300;
28785         width = Math.ceil(this.minWidth * height / this.minHeight);
28786         
28787         if(this.minWidth > this.minHeight){
28788             width = 300;
28789             height = Math.ceil(this.minHeight * width / this.minWidth);
28790         }
28791         
28792         this.thumbEl.setStyle({
28793             width : width + 'px',
28794             height : height + 'px'
28795         });
28796
28797         return;
28798             
28799     },
28800     
28801     setThumbBoxPosition : function()
28802     {
28803         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28804         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28805         
28806         this.thumbEl.setLeft(x);
28807         this.thumbEl.setTop(y);
28808         
28809     },
28810     
28811     baseRotateLevel : function()
28812     {
28813         this.baseRotate = 1;
28814         
28815         if(
28816                 typeof(this.exif) != 'undefined' &&
28817                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28818                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28819         ){
28820             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28821         }
28822         
28823         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28824         
28825     },
28826     
28827     baseScaleLevel : function()
28828     {
28829         var width, height;
28830         
28831         if(this.isDocument){
28832             
28833             if(this.baseRotate == 6 || this.baseRotate == 8){
28834             
28835                 height = this.thumbEl.getHeight();
28836                 this.baseScale = height / this.imageEl.OriginWidth;
28837
28838                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28839                     width = this.thumbEl.getWidth();
28840                     this.baseScale = width / this.imageEl.OriginHeight;
28841                 }
28842
28843                 return;
28844             }
28845
28846             height = this.thumbEl.getHeight();
28847             this.baseScale = height / this.imageEl.OriginHeight;
28848
28849             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28850                 width = this.thumbEl.getWidth();
28851                 this.baseScale = width / this.imageEl.OriginWidth;
28852             }
28853
28854             return;
28855         }
28856         
28857         if(this.baseRotate == 6 || this.baseRotate == 8){
28858             
28859             width = this.thumbEl.getHeight();
28860             this.baseScale = width / this.imageEl.OriginHeight;
28861             
28862             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28863                 height = this.thumbEl.getWidth();
28864                 this.baseScale = height / this.imageEl.OriginHeight;
28865             }
28866             
28867             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28868                 height = this.thumbEl.getWidth();
28869                 this.baseScale = height / this.imageEl.OriginHeight;
28870                 
28871                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28872                     width = this.thumbEl.getHeight();
28873                     this.baseScale = width / this.imageEl.OriginWidth;
28874                 }
28875             }
28876             
28877             return;
28878         }
28879         
28880         width = this.thumbEl.getWidth();
28881         this.baseScale = width / this.imageEl.OriginWidth;
28882         
28883         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28884             height = this.thumbEl.getHeight();
28885             this.baseScale = height / this.imageEl.OriginHeight;
28886         }
28887         
28888         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28889             
28890             height = this.thumbEl.getHeight();
28891             this.baseScale = height / this.imageEl.OriginHeight;
28892             
28893             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28894                 width = this.thumbEl.getWidth();
28895                 this.baseScale = width / this.imageEl.OriginWidth;
28896             }
28897             
28898         }
28899         
28900         return;
28901     },
28902     
28903     getScaleLevel : function()
28904     {
28905         return this.baseScale * Math.pow(1.1, this.scale);
28906     },
28907     
28908     onTouchStart : function(e)
28909     {
28910         if(!this.canvasLoaded){
28911             this.beforeSelectFile(e);
28912             return;
28913         }
28914         
28915         var touches = e.browserEvent.touches;
28916         
28917         if(!touches){
28918             return;
28919         }
28920         
28921         if(touches.length == 1){
28922             this.onMouseDown(e);
28923             return;
28924         }
28925         
28926         if(touches.length != 2){
28927             return;
28928         }
28929         
28930         var coords = [];
28931         
28932         for(var i = 0, finger; finger = touches[i]; i++){
28933             coords.push(finger.pageX, finger.pageY);
28934         }
28935         
28936         var x = Math.pow(coords[0] - coords[2], 2);
28937         var y = Math.pow(coords[1] - coords[3], 2);
28938         
28939         this.startDistance = Math.sqrt(x + y);
28940         
28941         this.startScale = this.scale;
28942         
28943         this.pinching = true;
28944         this.dragable = false;
28945         
28946     },
28947     
28948     onTouchMove : function(e)
28949     {
28950         if(!this.pinching && !this.dragable){
28951             return;
28952         }
28953         
28954         var touches = e.browserEvent.touches;
28955         
28956         if(!touches){
28957             return;
28958         }
28959         
28960         if(this.dragable){
28961             this.onMouseMove(e);
28962             return;
28963         }
28964         
28965         var coords = [];
28966         
28967         for(var i = 0, finger; finger = touches[i]; i++){
28968             coords.push(finger.pageX, finger.pageY);
28969         }
28970         
28971         var x = Math.pow(coords[0] - coords[2], 2);
28972         var y = Math.pow(coords[1] - coords[3], 2);
28973         
28974         this.endDistance = Math.sqrt(x + y);
28975         
28976         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28977         
28978         if(!this.zoomable()){
28979             this.scale = this.startScale;
28980             return;
28981         }
28982         
28983         this.draw();
28984         
28985     },
28986     
28987     onTouchEnd : function(e)
28988     {
28989         this.pinching = false;
28990         this.dragable = false;
28991         
28992     },
28993     
28994     process : function(file, crop)
28995     {
28996         if(this.loadMask){
28997             this.maskEl.mask(this.loadingText);
28998         }
28999         
29000         this.xhr = new XMLHttpRequest();
29001         
29002         file.xhr = this.xhr;
29003
29004         this.xhr.open(this.method, this.url, true);
29005         
29006         var headers = {
29007             "Accept": "application/json",
29008             "Cache-Control": "no-cache",
29009             "X-Requested-With": "XMLHttpRequest"
29010         };
29011         
29012         for (var headerName in headers) {
29013             var headerValue = headers[headerName];
29014             if (headerValue) {
29015                 this.xhr.setRequestHeader(headerName, headerValue);
29016             }
29017         }
29018         
29019         var _this = this;
29020         
29021         this.xhr.onload = function()
29022         {
29023             _this.xhrOnLoad(_this.xhr);
29024         }
29025         
29026         this.xhr.onerror = function()
29027         {
29028             _this.xhrOnError(_this.xhr);
29029         }
29030         
29031         var formData = new FormData();
29032
29033         formData.append('returnHTML', 'NO');
29034         
29035         if(crop){
29036             formData.append('crop', crop);
29037         }
29038         
29039         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29040             formData.append(this.paramName, file, file.name);
29041         }
29042         
29043         if(typeof(file.filename) != 'undefined'){
29044             formData.append('filename', file.filename);
29045         }
29046         
29047         if(typeof(file.mimetype) != 'undefined'){
29048             formData.append('mimetype', file.mimetype);
29049         }
29050         
29051         if(this.fireEvent('arrange', this, formData) != false){
29052             this.xhr.send(formData);
29053         };
29054     },
29055     
29056     xhrOnLoad : function(xhr)
29057     {
29058         if(this.loadMask){
29059             this.maskEl.unmask();
29060         }
29061         
29062         if (xhr.readyState !== 4) {
29063             this.fireEvent('exception', this, xhr);
29064             return;
29065         }
29066
29067         var response = Roo.decode(xhr.responseText);
29068         
29069         if(!response.success){
29070             this.fireEvent('exception', this, xhr);
29071             return;
29072         }
29073         
29074         var response = Roo.decode(xhr.responseText);
29075         
29076         this.fireEvent('upload', this, response);
29077         
29078     },
29079     
29080     xhrOnError : function()
29081     {
29082         if(this.loadMask){
29083             this.maskEl.unmask();
29084         }
29085         
29086         Roo.log('xhr on error');
29087         
29088         var response = Roo.decode(xhr.responseText);
29089           
29090         Roo.log(response);
29091         
29092     },
29093     
29094     prepare : function(file)
29095     {   
29096         if(this.loadMask){
29097             this.maskEl.mask(this.loadingText);
29098         }
29099         
29100         this.file = false;
29101         this.exif = {};
29102         
29103         if(typeof(file) === 'string'){
29104             this.loadCanvas(file);
29105             return;
29106         }
29107         
29108         if(!file || !this.urlAPI){
29109             return;
29110         }
29111         
29112         this.file = file;
29113         this.cropType = file.type;
29114         
29115         var _this = this;
29116         
29117         if(this.fireEvent('prepare', this, this.file) != false){
29118             
29119             var reader = new FileReader();
29120             
29121             reader.onload = function (e) {
29122                 if (e.target.error) {
29123                     Roo.log(e.target.error);
29124                     return;
29125                 }
29126                 
29127                 var buffer = e.target.result,
29128                     dataView = new DataView(buffer),
29129                     offset = 2,
29130                     maxOffset = dataView.byteLength - 4,
29131                     markerBytes,
29132                     markerLength;
29133                 
29134                 if (dataView.getUint16(0) === 0xffd8) {
29135                     while (offset < maxOffset) {
29136                         markerBytes = dataView.getUint16(offset);
29137                         
29138                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29139                             markerLength = dataView.getUint16(offset + 2) + 2;
29140                             if (offset + markerLength > dataView.byteLength) {
29141                                 Roo.log('Invalid meta data: Invalid segment size.');
29142                                 break;
29143                             }
29144                             
29145                             if(markerBytes == 0xffe1){
29146                                 _this.parseExifData(
29147                                     dataView,
29148                                     offset,
29149                                     markerLength
29150                                 );
29151                             }
29152                             
29153                             offset += markerLength;
29154                             
29155                             continue;
29156                         }
29157                         
29158                         break;
29159                     }
29160                     
29161                 }
29162                 
29163                 var url = _this.urlAPI.createObjectURL(_this.file);
29164                 
29165                 _this.loadCanvas(url);
29166                 
29167                 return;
29168             }
29169             
29170             reader.readAsArrayBuffer(this.file);
29171             
29172         }
29173         
29174     },
29175     
29176     parseExifData : function(dataView, offset, length)
29177     {
29178         var tiffOffset = offset + 10,
29179             littleEndian,
29180             dirOffset;
29181     
29182         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29183             // No Exif data, might be XMP data instead
29184             return;
29185         }
29186         
29187         // Check for the ASCII code for "Exif" (0x45786966):
29188         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29189             // No Exif data, might be XMP data instead
29190             return;
29191         }
29192         if (tiffOffset + 8 > dataView.byteLength) {
29193             Roo.log('Invalid Exif data: Invalid segment size.');
29194             return;
29195         }
29196         // Check for the two null bytes:
29197         if (dataView.getUint16(offset + 8) !== 0x0000) {
29198             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29199             return;
29200         }
29201         // Check the byte alignment:
29202         switch (dataView.getUint16(tiffOffset)) {
29203         case 0x4949:
29204             littleEndian = true;
29205             break;
29206         case 0x4D4D:
29207             littleEndian = false;
29208             break;
29209         default:
29210             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29211             return;
29212         }
29213         // Check for the TIFF tag marker (0x002A):
29214         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29215             Roo.log('Invalid Exif data: Missing TIFF marker.');
29216             return;
29217         }
29218         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29219         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29220         
29221         this.parseExifTags(
29222             dataView,
29223             tiffOffset,
29224             tiffOffset + dirOffset,
29225             littleEndian
29226         );
29227     },
29228     
29229     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29230     {
29231         var tagsNumber,
29232             dirEndOffset,
29233             i;
29234         if (dirOffset + 6 > dataView.byteLength) {
29235             Roo.log('Invalid Exif data: Invalid directory offset.');
29236             return;
29237         }
29238         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29239         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29240         if (dirEndOffset + 4 > dataView.byteLength) {
29241             Roo.log('Invalid Exif data: Invalid directory size.');
29242             return;
29243         }
29244         for (i = 0; i < tagsNumber; i += 1) {
29245             this.parseExifTag(
29246                 dataView,
29247                 tiffOffset,
29248                 dirOffset + 2 + 12 * i, // tag offset
29249                 littleEndian
29250             );
29251         }
29252         // Return the offset to the next directory:
29253         return dataView.getUint32(dirEndOffset, littleEndian);
29254     },
29255     
29256     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29257     {
29258         var tag = dataView.getUint16(offset, littleEndian);
29259         
29260         this.exif[tag] = this.getExifValue(
29261             dataView,
29262             tiffOffset,
29263             offset,
29264             dataView.getUint16(offset + 2, littleEndian), // tag type
29265             dataView.getUint32(offset + 4, littleEndian), // tag length
29266             littleEndian
29267         );
29268     },
29269     
29270     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29271     {
29272         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29273             tagSize,
29274             dataOffset,
29275             values,
29276             i,
29277             str,
29278             c;
29279     
29280         if (!tagType) {
29281             Roo.log('Invalid Exif data: Invalid tag type.');
29282             return;
29283         }
29284         
29285         tagSize = tagType.size * length;
29286         // Determine if the value is contained in the dataOffset bytes,
29287         // or if the value at the dataOffset is a pointer to the actual data:
29288         dataOffset = tagSize > 4 ?
29289                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29290         if (dataOffset + tagSize > dataView.byteLength) {
29291             Roo.log('Invalid Exif data: Invalid data offset.');
29292             return;
29293         }
29294         if (length === 1) {
29295             return tagType.getValue(dataView, dataOffset, littleEndian);
29296         }
29297         values = [];
29298         for (i = 0; i < length; i += 1) {
29299             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29300         }
29301         
29302         if (tagType.ascii) {
29303             str = '';
29304             // Concatenate the chars:
29305             for (i = 0; i < values.length; i += 1) {
29306                 c = values[i];
29307                 // Ignore the terminating NULL byte(s):
29308                 if (c === '\u0000') {
29309                     break;
29310                 }
29311                 str += c;
29312             }
29313             return str;
29314         }
29315         return values;
29316     }
29317     
29318 });
29319
29320 Roo.apply(Roo.bootstrap.UploadCropbox, {
29321     tags : {
29322         'Orientation': 0x0112
29323     },
29324     
29325     Orientation: {
29326             1: 0, //'top-left',
29327 //            2: 'top-right',
29328             3: 180, //'bottom-right',
29329 //            4: 'bottom-left',
29330 //            5: 'left-top',
29331             6: 90, //'right-top',
29332 //            7: 'right-bottom',
29333             8: 270 //'left-bottom'
29334     },
29335     
29336     exifTagTypes : {
29337         // byte, 8-bit unsigned int:
29338         1: {
29339             getValue: function (dataView, dataOffset) {
29340                 return dataView.getUint8(dataOffset);
29341             },
29342             size: 1
29343         },
29344         // ascii, 8-bit byte:
29345         2: {
29346             getValue: function (dataView, dataOffset) {
29347                 return String.fromCharCode(dataView.getUint8(dataOffset));
29348             },
29349             size: 1,
29350             ascii: true
29351         },
29352         // short, 16 bit int:
29353         3: {
29354             getValue: function (dataView, dataOffset, littleEndian) {
29355                 return dataView.getUint16(dataOffset, littleEndian);
29356             },
29357             size: 2
29358         },
29359         // long, 32 bit int:
29360         4: {
29361             getValue: function (dataView, dataOffset, littleEndian) {
29362                 return dataView.getUint32(dataOffset, littleEndian);
29363             },
29364             size: 4
29365         },
29366         // rational = two long values, first is numerator, second is denominator:
29367         5: {
29368             getValue: function (dataView, dataOffset, littleEndian) {
29369                 return dataView.getUint32(dataOffset, littleEndian) /
29370                     dataView.getUint32(dataOffset + 4, littleEndian);
29371             },
29372             size: 8
29373         },
29374         // slong, 32 bit signed int:
29375         9: {
29376             getValue: function (dataView, dataOffset, littleEndian) {
29377                 return dataView.getInt32(dataOffset, littleEndian);
29378             },
29379             size: 4
29380         },
29381         // srational, two slongs, first is numerator, second is denominator:
29382         10: {
29383             getValue: function (dataView, dataOffset, littleEndian) {
29384                 return dataView.getInt32(dataOffset, littleEndian) /
29385                     dataView.getInt32(dataOffset + 4, littleEndian);
29386             },
29387             size: 8
29388         }
29389     },
29390     
29391     footer : {
29392         STANDARD : [
29393             {
29394                 tag : 'div',
29395                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29396                 action : 'rotate-left',
29397                 cn : [
29398                     {
29399                         tag : 'button',
29400                         cls : 'btn btn-default',
29401                         html : '<i class="fa fa-undo"></i>'
29402                     }
29403                 ]
29404             },
29405             {
29406                 tag : 'div',
29407                 cls : 'btn-group roo-upload-cropbox-picture',
29408                 action : 'picture',
29409                 cn : [
29410                     {
29411                         tag : 'button',
29412                         cls : 'btn btn-default',
29413                         html : '<i class="fa fa-picture-o"></i>'
29414                     }
29415                 ]
29416             },
29417             {
29418                 tag : 'div',
29419                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29420                 action : 'rotate-right',
29421                 cn : [
29422                     {
29423                         tag : 'button',
29424                         cls : 'btn btn-default',
29425                         html : '<i class="fa fa-repeat"></i>'
29426                     }
29427                 ]
29428             }
29429         ],
29430         DOCUMENT : [
29431             {
29432                 tag : 'div',
29433                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29434                 action : 'rotate-left',
29435                 cn : [
29436                     {
29437                         tag : 'button',
29438                         cls : 'btn btn-default',
29439                         html : '<i class="fa fa-undo"></i>'
29440                     }
29441                 ]
29442             },
29443             {
29444                 tag : 'div',
29445                 cls : 'btn-group roo-upload-cropbox-download',
29446                 action : 'download',
29447                 cn : [
29448                     {
29449                         tag : 'button',
29450                         cls : 'btn btn-default',
29451                         html : '<i class="fa fa-download"></i>'
29452                     }
29453                 ]
29454             },
29455             {
29456                 tag : 'div',
29457                 cls : 'btn-group roo-upload-cropbox-crop',
29458                 action : 'crop',
29459                 cn : [
29460                     {
29461                         tag : 'button',
29462                         cls : 'btn btn-default',
29463                         html : '<i class="fa fa-crop"></i>'
29464                     }
29465                 ]
29466             },
29467             {
29468                 tag : 'div',
29469                 cls : 'btn-group roo-upload-cropbox-trash',
29470                 action : 'trash',
29471                 cn : [
29472                     {
29473                         tag : 'button',
29474                         cls : 'btn btn-default',
29475                         html : '<i class="fa fa-trash"></i>'
29476                     }
29477                 ]
29478             },
29479             {
29480                 tag : 'div',
29481                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29482                 action : 'rotate-right',
29483                 cn : [
29484                     {
29485                         tag : 'button',
29486                         cls : 'btn btn-default',
29487                         html : '<i class="fa fa-repeat"></i>'
29488                     }
29489                 ]
29490             }
29491         ],
29492         ROTATOR : [
29493             {
29494                 tag : 'div',
29495                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29496                 action : 'rotate-left',
29497                 cn : [
29498                     {
29499                         tag : 'button',
29500                         cls : 'btn btn-default',
29501                         html : '<i class="fa fa-undo"></i>'
29502                     }
29503                 ]
29504             },
29505             {
29506                 tag : 'div',
29507                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29508                 action : 'rotate-right',
29509                 cn : [
29510                     {
29511                         tag : 'button',
29512                         cls : 'btn btn-default',
29513                         html : '<i class="fa fa-repeat"></i>'
29514                     }
29515                 ]
29516             }
29517         ]
29518     }
29519 });
29520
29521 /*
29522 * Licence: LGPL
29523 */
29524
29525 /**
29526  * @class Roo.bootstrap.DocumentManager
29527  * @extends Roo.bootstrap.Component
29528  * Bootstrap DocumentManager class
29529  * @cfg {String} paramName default 'imageUpload'
29530  * @cfg {String} toolTipName default 'filename'
29531  * @cfg {String} method default POST
29532  * @cfg {String} url action url
29533  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29534  * @cfg {Boolean} multiple multiple upload default true
29535  * @cfg {Number} thumbSize default 300
29536  * @cfg {String} fieldLabel
29537  * @cfg {Number} labelWidth default 4
29538  * @cfg {String} labelAlign (left|top) default left
29539  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29540 * @cfg {Number} labellg set the width of label (1-12)
29541  * @cfg {Number} labelmd set the width of label (1-12)
29542  * @cfg {Number} labelsm set the width of label (1-12)
29543  * @cfg {Number} labelxs set the width of label (1-12)
29544  * 
29545  * @constructor
29546  * Create a new DocumentManager
29547  * @param {Object} config The config object
29548  */
29549
29550 Roo.bootstrap.DocumentManager = function(config){
29551     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29552     
29553     this.files = [];
29554     this.delegates = [];
29555     
29556     this.addEvents({
29557         /**
29558          * @event initial
29559          * Fire when initial the DocumentManager
29560          * @param {Roo.bootstrap.DocumentManager} this
29561          */
29562         "initial" : true,
29563         /**
29564          * @event inspect
29565          * inspect selected file
29566          * @param {Roo.bootstrap.DocumentManager} this
29567          * @param {File} file
29568          */
29569         "inspect" : true,
29570         /**
29571          * @event exception
29572          * Fire when xhr load exception
29573          * @param {Roo.bootstrap.DocumentManager} this
29574          * @param {XMLHttpRequest} xhr
29575          */
29576         "exception" : true,
29577         /**
29578          * @event afterupload
29579          * Fire when xhr load exception
29580          * @param {Roo.bootstrap.DocumentManager} this
29581          * @param {XMLHttpRequest} xhr
29582          */
29583         "afterupload" : true,
29584         /**
29585          * @event prepare
29586          * prepare the form data
29587          * @param {Roo.bootstrap.DocumentManager} this
29588          * @param {Object} formData
29589          */
29590         "prepare" : true,
29591         /**
29592          * @event remove
29593          * Fire when remove the file
29594          * @param {Roo.bootstrap.DocumentManager} this
29595          * @param {Object} file
29596          */
29597         "remove" : true,
29598         /**
29599          * @event refresh
29600          * Fire after refresh the file
29601          * @param {Roo.bootstrap.DocumentManager} this
29602          */
29603         "refresh" : true,
29604         /**
29605          * @event click
29606          * Fire after click the image
29607          * @param {Roo.bootstrap.DocumentManager} this
29608          * @param {Object} file
29609          */
29610         "click" : true,
29611         /**
29612          * @event edit
29613          * Fire when upload a image and editable set to true
29614          * @param {Roo.bootstrap.DocumentManager} this
29615          * @param {Object} file
29616          */
29617         "edit" : true,
29618         /**
29619          * @event beforeselectfile
29620          * Fire before select file
29621          * @param {Roo.bootstrap.DocumentManager} this
29622          */
29623         "beforeselectfile" : true,
29624         /**
29625          * @event process
29626          * Fire before process file
29627          * @param {Roo.bootstrap.DocumentManager} this
29628          * @param {Object} file
29629          */
29630         "process" : true,
29631         /**
29632          * @event previewrendered
29633          * Fire when preview rendered
29634          * @param {Roo.bootstrap.DocumentManager} this
29635          * @param {Object} file
29636          */
29637         "previewrendered" : true,
29638         /**
29639          */
29640         "previewResize" : true
29641         
29642     });
29643 };
29644
29645 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29646     
29647     boxes : 0,
29648     inputName : '',
29649     thumbSize : 300,
29650     multiple : true,
29651     files : false,
29652     method : 'POST',
29653     url : '',
29654     paramName : 'imageUpload',
29655     toolTipName : 'filename',
29656     fieldLabel : '',
29657     labelWidth : 4,
29658     labelAlign : 'left',
29659     editable : true,
29660     delegates : false,
29661     xhr : false, 
29662     
29663     labellg : 0,
29664     labelmd : 0,
29665     labelsm : 0,
29666     labelxs : 0,
29667     
29668     getAutoCreate : function()
29669     {   
29670         var managerWidget = {
29671             tag : 'div',
29672             cls : 'roo-document-manager',
29673             cn : [
29674                 {
29675                     tag : 'input',
29676                     cls : 'roo-document-manager-selector',
29677                     type : 'file'
29678                 },
29679                 {
29680                     tag : 'div',
29681                     cls : 'roo-document-manager-uploader',
29682                     cn : [
29683                         {
29684                             tag : 'div',
29685                             cls : 'roo-document-manager-upload-btn',
29686                             html : '<i class="fa fa-plus"></i>'
29687                         }
29688                     ]
29689                     
29690                 }
29691             ]
29692         };
29693         
29694         var content = [
29695             {
29696                 tag : 'div',
29697                 cls : 'column col-md-12',
29698                 cn : managerWidget
29699             }
29700         ];
29701         
29702         if(this.fieldLabel.length){
29703             
29704             content = [
29705                 {
29706                     tag : 'div',
29707                     cls : 'column col-md-12',
29708                     html : this.fieldLabel
29709                 },
29710                 {
29711                     tag : 'div',
29712                     cls : 'column col-md-12',
29713                     cn : managerWidget
29714                 }
29715             ];
29716
29717             if(this.labelAlign == 'left'){
29718                 content = [
29719                     {
29720                         tag : 'div',
29721                         cls : 'column',
29722                         html : this.fieldLabel
29723                     },
29724                     {
29725                         tag : 'div',
29726                         cls : 'column',
29727                         cn : managerWidget
29728                     }
29729                 ];
29730                 
29731                 if(this.labelWidth > 12){
29732                     content[0].style = "width: " + this.labelWidth + 'px';
29733                 }
29734
29735                 if(this.labelWidth < 13 && this.labelmd == 0){
29736                     this.labelmd = this.labelWidth;
29737                 }
29738
29739                 if(this.labellg > 0){
29740                     content[0].cls += ' col-lg-' + this.labellg;
29741                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29742                 }
29743
29744                 if(this.labelmd > 0){
29745                     content[0].cls += ' col-md-' + this.labelmd;
29746                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29747                 }
29748
29749                 if(this.labelsm > 0){
29750                     content[0].cls += ' col-sm-' + this.labelsm;
29751                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29752                 }
29753
29754                 if(this.labelxs > 0){
29755                     content[0].cls += ' col-xs-' + this.labelxs;
29756                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29757                 }
29758                 
29759             }
29760         }
29761         
29762         var cfg = {
29763             tag : 'div',
29764             cls : 'row clearfix',
29765             cn : content
29766         };
29767         
29768         return cfg;
29769         
29770     },
29771     
29772     initEvents : function()
29773     {
29774         this.managerEl = this.el.select('.roo-document-manager', true).first();
29775         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29776         
29777         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29778         this.selectorEl.hide();
29779         
29780         if(this.multiple){
29781             this.selectorEl.attr('multiple', 'multiple');
29782         }
29783         
29784         this.selectorEl.on('change', this.onFileSelected, this);
29785         
29786         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29787         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29788         
29789         this.uploader.on('click', this.onUploaderClick, this);
29790         
29791         this.renderProgressDialog();
29792         
29793         var _this = this;
29794         
29795         window.addEventListener("resize", function() { _this.refresh(); } );
29796         
29797         this.fireEvent('initial', this);
29798     },
29799     
29800     renderProgressDialog : function()
29801     {
29802         var _this = this;
29803         
29804         this.progressDialog = new Roo.bootstrap.Modal({
29805             cls : 'roo-document-manager-progress-dialog',
29806             allow_close : false,
29807             animate : false,
29808             title : '',
29809             buttons : [
29810                 {
29811                     name  :'cancel',
29812                     weight : 'danger',
29813                     html : 'Cancel'
29814                 }
29815             ], 
29816             listeners : { 
29817                 btnclick : function() {
29818                     _this.uploadCancel();
29819                     this.hide();
29820                 }
29821             }
29822         });
29823          
29824         this.progressDialog.render(Roo.get(document.body));
29825          
29826         this.progress = new Roo.bootstrap.Progress({
29827             cls : 'roo-document-manager-progress',
29828             active : true,
29829             striped : true
29830         });
29831         
29832         this.progress.render(this.progressDialog.getChildContainer());
29833         
29834         this.progressBar = new Roo.bootstrap.ProgressBar({
29835             cls : 'roo-document-manager-progress-bar',
29836             aria_valuenow : 0,
29837             aria_valuemin : 0,
29838             aria_valuemax : 12,
29839             panel : 'success'
29840         });
29841         
29842         this.progressBar.render(this.progress.getChildContainer());
29843     },
29844     
29845     onUploaderClick : function(e)
29846     {
29847         e.preventDefault();
29848      
29849         if(this.fireEvent('beforeselectfile', this) != false){
29850             this.selectorEl.dom.click();
29851         }
29852         
29853     },
29854     
29855     onFileSelected : function(e)
29856     {
29857         e.preventDefault();
29858         
29859         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29860             return;
29861         }
29862         
29863         Roo.each(this.selectorEl.dom.files, function(file){
29864             if(this.fireEvent('inspect', this, file) != false){
29865                 this.files.push(file);
29866             }
29867         }, this);
29868         
29869         this.queue();
29870         
29871     },
29872     
29873     queue : function()
29874     {
29875         this.selectorEl.dom.value = '';
29876         
29877         if(!this.files || !this.files.length){
29878             return;
29879         }
29880         
29881         if(this.boxes > 0 && this.files.length > this.boxes){
29882             this.files = this.files.slice(0, this.boxes);
29883         }
29884         
29885         this.uploader.show();
29886         
29887         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29888             this.uploader.hide();
29889         }
29890         
29891         var _this = this;
29892         
29893         var files = [];
29894         
29895         var docs = [];
29896         
29897         Roo.each(this.files, function(file){
29898             
29899             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29900                 var f = this.renderPreview(file);
29901                 files.push(f);
29902                 return;
29903             }
29904             
29905             if(file.type.indexOf('image') != -1){
29906                 this.delegates.push(
29907                     (function(){
29908                         _this.process(file);
29909                     }).createDelegate(this)
29910                 );
29911         
29912                 return;
29913             }
29914             
29915             docs.push(
29916                 (function(){
29917                     _this.process(file);
29918                 }).createDelegate(this)
29919             );
29920             
29921         }, this);
29922         
29923         this.files = files;
29924         
29925         this.delegates = this.delegates.concat(docs);
29926         
29927         if(!this.delegates.length){
29928             this.refresh();
29929             return;
29930         }
29931         
29932         this.progressBar.aria_valuemax = this.delegates.length;
29933         
29934         this.arrange();
29935         
29936         return;
29937     },
29938     
29939     arrange : function()
29940     {
29941         if(!this.delegates.length){
29942             this.progressDialog.hide();
29943             this.refresh();
29944             return;
29945         }
29946         
29947         var delegate = this.delegates.shift();
29948         
29949         this.progressDialog.show();
29950         
29951         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29952         
29953         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29954         
29955         delegate();
29956     },
29957     
29958     refresh : function()
29959     {
29960         this.uploader.show();
29961         
29962         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29963             this.uploader.hide();
29964         }
29965         
29966         Roo.isTouch ? this.closable(false) : this.closable(true);
29967         
29968         this.fireEvent('refresh', this);
29969     },
29970     
29971     onRemove : function(e, el, o)
29972     {
29973         e.preventDefault();
29974         
29975         this.fireEvent('remove', this, o);
29976         
29977     },
29978     
29979     remove : function(o)
29980     {
29981         var files = [];
29982         
29983         Roo.each(this.files, function(file){
29984             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29985                 files.push(file);
29986                 return;
29987             }
29988
29989             o.target.remove();
29990
29991         }, this);
29992         
29993         this.files = files;
29994         
29995         this.refresh();
29996     },
29997     
29998     clear : function()
29999     {
30000         Roo.each(this.files, function(file){
30001             if(!file.target){
30002                 return;
30003             }
30004             
30005             file.target.remove();
30006
30007         }, this);
30008         
30009         this.files = [];
30010         
30011         this.refresh();
30012     },
30013     
30014     onClick : function(e, el, o)
30015     {
30016         e.preventDefault();
30017         
30018         this.fireEvent('click', this, o);
30019         
30020     },
30021     
30022     closable : function(closable)
30023     {
30024         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30025             
30026             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30027             
30028             if(closable){
30029                 el.show();
30030                 return;
30031             }
30032             
30033             el.hide();
30034             
30035         }, this);
30036     },
30037     
30038     xhrOnLoad : function(xhr)
30039     {
30040         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30041             el.remove();
30042         }, this);
30043         
30044         if (xhr.readyState !== 4) {
30045             this.arrange();
30046             this.fireEvent('exception', this, xhr);
30047             return;
30048         }
30049
30050         var response = Roo.decode(xhr.responseText);
30051         
30052         if(!response.success){
30053             this.arrange();
30054             this.fireEvent('exception', this, xhr);
30055             return;
30056         }
30057         
30058         var file = this.renderPreview(response.data);
30059         
30060         this.files.push(file);
30061         
30062         this.arrange();
30063         
30064         this.fireEvent('afterupload', this, xhr);
30065         
30066     },
30067     
30068     xhrOnError : function(xhr)
30069     {
30070         Roo.log('xhr on error');
30071         
30072         var response = Roo.decode(xhr.responseText);
30073           
30074         Roo.log(response);
30075         
30076         this.arrange();
30077     },
30078     
30079     process : function(file)
30080     {
30081         if(this.fireEvent('process', this, file) !== false){
30082             if(this.editable && file.type.indexOf('image') != -1){
30083                 this.fireEvent('edit', this, file);
30084                 return;
30085             }
30086
30087             this.uploadStart(file, false);
30088
30089             return;
30090         }
30091         
30092     },
30093     
30094     uploadStart : function(file, crop)
30095     {
30096         this.xhr = new XMLHttpRequest();
30097         
30098         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30099             this.arrange();
30100             return;
30101         }
30102         
30103         file.xhr = this.xhr;
30104             
30105         this.managerEl.createChild({
30106             tag : 'div',
30107             cls : 'roo-document-manager-loading',
30108             cn : [
30109                 {
30110                     tag : 'div',
30111                     tooltip : file.name,
30112                     cls : 'roo-document-manager-thumb',
30113                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30114                 }
30115             ]
30116
30117         });
30118
30119         this.xhr.open(this.method, this.url, true);
30120         
30121         var headers = {
30122             "Accept": "application/json",
30123             "Cache-Control": "no-cache",
30124             "X-Requested-With": "XMLHttpRequest"
30125         };
30126         
30127         for (var headerName in headers) {
30128             var headerValue = headers[headerName];
30129             if (headerValue) {
30130                 this.xhr.setRequestHeader(headerName, headerValue);
30131             }
30132         }
30133         
30134         var _this = this;
30135         
30136         this.xhr.onload = function()
30137         {
30138             _this.xhrOnLoad(_this.xhr);
30139         }
30140         
30141         this.xhr.onerror = function()
30142         {
30143             _this.xhrOnError(_this.xhr);
30144         }
30145         
30146         var formData = new FormData();
30147
30148         formData.append('returnHTML', 'NO');
30149         
30150         if(crop){
30151             formData.append('crop', crop);
30152         }
30153         
30154         formData.append(this.paramName, file, file.name);
30155         
30156         var options = {
30157             file : file, 
30158             manually : false
30159         };
30160         
30161         if(this.fireEvent('prepare', this, formData, options) != false){
30162             
30163             if(options.manually){
30164                 return;
30165             }
30166             
30167             this.xhr.send(formData);
30168             return;
30169         };
30170         
30171         this.uploadCancel();
30172     },
30173     
30174     uploadCancel : function()
30175     {
30176         if (this.xhr) {
30177             this.xhr.abort();
30178         }
30179         
30180         this.delegates = [];
30181         
30182         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30183             el.remove();
30184         }, this);
30185         
30186         this.arrange();
30187     },
30188     
30189     renderPreview : function(file)
30190     {
30191         if(typeof(file.target) != 'undefined' && file.target){
30192             return file;
30193         }
30194         
30195         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30196         
30197         var previewEl = this.managerEl.createChild({
30198             tag : 'div',
30199             cls : 'roo-document-manager-preview',
30200             cn : [
30201                 {
30202                     tag : 'div',
30203                     tooltip : file[this.toolTipName],
30204                     cls : 'roo-document-manager-thumb',
30205                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30206                 },
30207                 {
30208                     tag : 'button',
30209                     cls : 'close',
30210                     html : '<i class="fa fa-times-circle"></i>'
30211                 }
30212             ]
30213         });
30214
30215         var close = previewEl.select('button.close', true).first();
30216
30217         close.on('click', this.onRemove, this, file);
30218
30219         file.target = previewEl;
30220
30221         var image = previewEl.select('img', true).first();
30222         
30223         var _this = this;
30224         
30225         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30226         
30227         image.on('click', this.onClick, this, file);
30228         
30229         this.fireEvent('previewrendered', this, file);
30230         
30231         return file;
30232         
30233     },
30234     
30235     onPreviewLoad : function(file, image)
30236     {
30237         if(typeof(file.target) == 'undefined' || !file.target){
30238             return;
30239         }
30240         
30241         var width = image.dom.naturalWidth || image.dom.width;
30242         var height = image.dom.naturalHeight || image.dom.height;
30243         
30244         if(!this.previewResize) {
30245             return;
30246         }
30247         
30248         if(width > height){
30249             file.target.addClass('wide');
30250             return;
30251         }
30252         
30253         file.target.addClass('tall');
30254         return;
30255         
30256     },
30257     
30258     uploadFromSource : function(file, crop)
30259     {
30260         this.xhr = new XMLHttpRequest();
30261         
30262         this.managerEl.createChild({
30263             tag : 'div',
30264             cls : 'roo-document-manager-loading',
30265             cn : [
30266                 {
30267                     tag : 'div',
30268                     tooltip : file.name,
30269                     cls : 'roo-document-manager-thumb',
30270                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30271                 }
30272             ]
30273
30274         });
30275
30276         this.xhr.open(this.method, this.url, true);
30277         
30278         var headers = {
30279             "Accept": "application/json",
30280             "Cache-Control": "no-cache",
30281             "X-Requested-With": "XMLHttpRequest"
30282         };
30283         
30284         for (var headerName in headers) {
30285             var headerValue = headers[headerName];
30286             if (headerValue) {
30287                 this.xhr.setRequestHeader(headerName, headerValue);
30288             }
30289         }
30290         
30291         var _this = this;
30292         
30293         this.xhr.onload = function()
30294         {
30295             _this.xhrOnLoad(_this.xhr);
30296         }
30297         
30298         this.xhr.onerror = function()
30299         {
30300             _this.xhrOnError(_this.xhr);
30301         }
30302         
30303         var formData = new FormData();
30304
30305         formData.append('returnHTML', 'NO');
30306         
30307         formData.append('crop', crop);
30308         
30309         if(typeof(file.filename) != 'undefined'){
30310             formData.append('filename', file.filename);
30311         }
30312         
30313         if(typeof(file.mimetype) != 'undefined'){
30314             formData.append('mimetype', file.mimetype);
30315         }
30316         
30317         Roo.log(formData);
30318         
30319         if(this.fireEvent('prepare', this, formData) != false){
30320             this.xhr.send(formData);
30321         };
30322     }
30323 });
30324
30325 /*
30326 * Licence: LGPL
30327 */
30328
30329 /**
30330  * @class Roo.bootstrap.DocumentViewer
30331  * @extends Roo.bootstrap.Component
30332  * Bootstrap DocumentViewer class
30333  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30334  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30335  * 
30336  * @constructor
30337  * Create a new DocumentViewer
30338  * @param {Object} config The config object
30339  */
30340
30341 Roo.bootstrap.DocumentViewer = function(config){
30342     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30343     
30344     this.addEvents({
30345         /**
30346          * @event initial
30347          * Fire after initEvent
30348          * @param {Roo.bootstrap.DocumentViewer} this
30349          */
30350         "initial" : true,
30351         /**
30352          * @event click
30353          * Fire after click
30354          * @param {Roo.bootstrap.DocumentViewer} this
30355          */
30356         "click" : true,
30357         /**
30358          * @event download
30359          * Fire after download button
30360          * @param {Roo.bootstrap.DocumentViewer} this
30361          */
30362         "download" : true,
30363         /**
30364          * @event trash
30365          * Fire after trash button
30366          * @param {Roo.bootstrap.DocumentViewer} this
30367          */
30368         "trash" : true
30369         
30370     });
30371 };
30372
30373 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30374     
30375     showDownload : true,
30376     
30377     showTrash : true,
30378     
30379     getAutoCreate : function()
30380     {
30381         var cfg = {
30382             tag : 'div',
30383             cls : 'roo-document-viewer',
30384             cn : [
30385                 {
30386                     tag : 'div',
30387                     cls : 'roo-document-viewer-body',
30388                     cn : [
30389                         {
30390                             tag : 'div',
30391                             cls : 'roo-document-viewer-thumb',
30392                             cn : [
30393                                 {
30394                                     tag : 'img',
30395                                     cls : 'roo-document-viewer-image'
30396                                 }
30397                             ]
30398                         }
30399                     ]
30400                 },
30401                 {
30402                     tag : 'div',
30403                     cls : 'roo-document-viewer-footer',
30404                     cn : {
30405                         tag : 'div',
30406                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30407                         cn : [
30408                             {
30409                                 tag : 'div',
30410                                 cls : 'btn-group roo-document-viewer-download',
30411                                 cn : [
30412                                     {
30413                                         tag : 'button',
30414                                         cls : 'btn btn-default',
30415                                         html : '<i class="fa fa-download"></i>'
30416                                     }
30417                                 ]
30418                             },
30419                             {
30420                                 tag : 'div',
30421                                 cls : 'btn-group roo-document-viewer-trash',
30422                                 cn : [
30423                                     {
30424                                         tag : 'button',
30425                                         cls : 'btn btn-default',
30426                                         html : '<i class="fa fa-trash"></i>'
30427                                     }
30428                                 ]
30429                             }
30430                         ]
30431                     }
30432                 }
30433             ]
30434         };
30435         
30436         return cfg;
30437     },
30438     
30439     initEvents : function()
30440     {
30441         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30442         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30443         
30444         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30445         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30446         
30447         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30448         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30449         
30450         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30451         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30452         
30453         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30454         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30455         
30456         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30457         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30458         
30459         this.bodyEl.on('click', this.onClick, this);
30460         this.downloadBtn.on('click', this.onDownload, this);
30461         this.trashBtn.on('click', this.onTrash, this);
30462         
30463         this.downloadBtn.hide();
30464         this.trashBtn.hide();
30465         
30466         if(this.showDownload){
30467             this.downloadBtn.show();
30468         }
30469         
30470         if(this.showTrash){
30471             this.trashBtn.show();
30472         }
30473         
30474         if(!this.showDownload && !this.showTrash) {
30475             this.footerEl.hide();
30476         }
30477         
30478     },
30479     
30480     initial : function()
30481     {
30482         this.fireEvent('initial', this);
30483         
30484     },
30485     
30486     onClick : function(e)
30487     {
30488         e.preventDefault();
30489         
30490         this.fireEvent('click', this);
30491     },
30492     
30493     onDownload : function(e)
30494     {
30495         e.preventDefault();
30496         
30497         this.fireEvent('download', this);
30498     },
30499     
30500     onTrash : function(e)
30501     {
30502         e.preventDefault();
30503         
30504         this.fireEvent('trash', this);
30505     }
30506     
30507 });
30508 /*
30509  * - LGPL
30510  *
30511  * nav progress bar
30512  * 
30513  */
30514
30515 /**
30516  * @class Roo.bootstrap.NavProgressBar
30517  * @extends Roo.bootstrap.Component
30518  * Bootstrap NavProgressBar class
30519  * 
30520  * @constructor
30521  * Create a new nav progress bar
30522  * @param {Object} config The config object
30523  */
30524
30525 Roo.bootstrap.NavProgressBar = function(config){
30526     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30527
30528     this.bullets = this.bullets || [];
30529    
30530 //    Roo.bootstrap.NavProgressBar.register(this);
30531      this.addEvents({
30532         /**
30533              * @event changed
30534              * Fires when the active item changes
30535              * @param {Roo.bootstrap.NavProgressBar} this
30536              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30537              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30538          */
30539         'changed': true
30540      });
30541     
30542 };
30543
30544 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30545     
30546     bullets : [],
30547     barItems : [],
30548     
30549     getAutoCreate : function()
30550     {
30551         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30552         
30553         cfg = {
30554             tag : 'div',
30555             cls : 'roo-navigation-bar-group',
30556             cn : [
30557                 {
30558                     tag : 'div',
30559                     cls : 'roo-navigation-top-bar'
30560                 },
30561                 {
30562                     tag : 'div',
30563                     cls : 'roo-navigation-bullets-bar',
30564                     cn : [
30565                         {
30566                             tag : 'ul',
30567                             cls : 'roo-navigation-bar'
30568                         }
30569                     ]
30570                 },
30571                 
30572                 {
30573                     tag : 'div',
30574                     cls : 'roo-navigation-bottom-bar'
30575                 }
30576             ]
30577             
30578         };
30579         
30580         return cfg;
30581         
30582     },
30583     
30584     initEvents: function() 
30585     {
30586         
30587     },
30588     
30589     onRender : function(ct, position) 
30590     {
30591         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30592         
30593         if(this.bullets.length){
30594             Roo.each(this.bullets, function(b){
30595                this.addItem(b);
30596             }, this);
30597         }
30598         
30599         this.format();
30600         
30601     },
30602     
30603     addItem : function(cfg)
30604     {
30605         var item = new Roo.bootstrap.NavProgressItem(cfg);
30606         
30607         item.parentId = this.id;
30608         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30609         
30610         if(cfg.html){
30611             var top = new Roo.bootstrap.Element({
30612                 tag : 'div',
30613                 cls : 'roo-navigation-bar-text'
30614             });
30615             
30616             var bottom = new Roo.bootstrap.Element({
30617                 tag : 'div',
30618                 cls : 'roo-navigation-bar-text'
30619             });
30620             
30621             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30622             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30623             
30624             var topText = new Roo.bootstrap.Element({
30625                 tag : 'span',
30626                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30627             });
30628             
30629             var bottomText = new Roo.bootstrap.Element({
30630                 tag : 'span',
30631                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30632             });
30633             
30634             topText.onRender(top.el, null);
30635             bottomText.onRender(bottom.el, null);
30636             
30637             item.topEl = top;
30638             item.bottomEl = bottom;
30639         }
30640         
30641         this.barItems.push(item);
30642         
30643         return item;
30644     },
30645     
30646     getActive : function()
30647     {
30648         var active = false;
30649         
30650         Roo.each(this.barItems, function(v){
30651             
30652             if (!v.isActive()) {
30653                 return;
30654             }
30655             
30656             active = v;
30657             return false;
30658             
30659         });
30660         
30661         return active;
30662     },
30663     
30664     setActiveItem : function(item)
30665     {
30666         var prev = false;
30667         
30668         Roo.each(this.barItems, function(v){
30669             if (v.rid == item.rid) {
30670                 return ;
30671             }
30672             
30673             if (v.isActive()) {
30674                 v.setActive(false);
30675                 prev = v;
30676             }
30677         });
30678
30679         item.setActive(true);
30680         
30681         this.fireEvent('changed', this, item, prev);
30682     },
30683     
30684     getBarItem: function(rid)
30685     {
30686         var ret = false;
30687         
30688         Roo.each(this.barItems, function(e) {
30689             if (e.rid != rid) {
30690                 return;
30691             }
30692             
30693             ret =  e;
30694             return false;
30695         });
30696         
30697         return ret;
30698     },
30699     
30700     indexOfItem : function(item)
30701     {
30702         var index = false;
30703         
30704         Roo.each(this.barItems, function(v, i){
30705             
30706             if (v.rid != item.rid) {
30707                 return;
30708             }
30709             
30710             index = i;
30711             return false
30712         });
30713         
30714         return index;
30715     },
30716     
30717     setActiveNext : function()
30718     {
30719         var i = this.indexOfItem(this.getActive());
30720         
30721         if (i > this.barItems.length) {
30722             return;
30723         }
30724         
30725         this.setActiveItem(this.barItems[i+1]);
30726     },
30727     
30728     setActivePrev : function()
30729     {
30730         var i = this.indexOfItem(this.getActive());
30731         
30732         if (i  < 1) {
30733             return;
30734         }
30735         
30736         this.setActiveItem(this.barItems[i-1]);
30737     },
30738     
30739     format : function()
30740     {
30741         if(!this.barItems.length){
30742             return;
30743         }
30744      
30745         var width = 100 / this.barItems.length;
30746         
30747         Roo.each(this.barItems, function(i){
30748             i.el.setStyle('width', width + '%');
30749             i.topEl.el.setStyle('width', width + '%');
30750             i.bottomEl.el.setStyle('width', width + '%');
30751         }, this);
30752         
30753     }
30754     
30755 });
30756 /*
30757  * - LGPL
30758  *
30759  * Nav Progress Item
30760  * 
30761  */
30762
30763 /**
30764  * @class Roo.bootstrap.NavProgressItem
30765  * @extends Roo.bootstrap.Component
30766  * Bootstrap NavProgressItem class
30767  * @cfg {String} rid the reference id
30768  * @cfg {Boolean} active (true|false) Is item active default false
30769  * @cfg {Boolean} disabled (true|false) Is item active default false
30770  * @cfg {String} html
30771  * @cfg {String} position (top|bottom) text position default bottom
30772  * @cfg {String} icon show icon instead of number
30773  * 
30774  * @constructor
30775  * Create a new NavProgressItem
30776  * @param {Object} config The config object
30777  */
30778 Roo.bootstrap.NavProgressItem = function(config){
30779     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30780     this.addEvents({
30781         // raw events
30782         /**
30783          * @event click
30784          * The raw click event for the entire grid.
30785          * @param {Roo.bootstrap.NavProgressItem} this
30786          * @param {Roo.EventObject} e
30787          */
30788         "click" : true
30789     });
30790    
30791 };
30792
30793 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30794     
30795     rid : '',
30796     active : false,
30797     disabled : false,
30798     html : '',
30799     position : 'bottom',
30800     icon : false,
30801     
30802     getAutoCreate : function()
30803     {
30804         var iconCls = 'roo-navigation-bar-item-icon';
30805         
30806         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30807         
30808         var cfg = {
30809             tag: 'li',
30810             cls: 'roo-navigation-bar-item',
30811             cn : [
30812                 {
30813                     tag : 'i',
30814                     cls : iconCls
30815                 }
30816             ]
30817         };
30818         
30819         if(this.active){
30820             cfg.cls += ' active';
30821         }
30822         if(this.disabled){
30823             cfg.cls += ' disabled';
30824         }
30825         
30826         return cfg;
30827     },
30828     
30829     disable : function()
30830     {
30831         this.setDisabled(true);
30832     },
30833     
30834     enable : function()
30835     {
30836         this.setDisabled(false);
30837     },
30838     
30839     initEvents: function() 
30840     {
30841         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30842         
30843         this.iconEl.on('click', this.onClick, this);
30844     },
30845     
30846     onClick : function(e)
30847     {
30848         e.preventDefault();
30849         
30850         if(this.disabled){
30851             return;
30852         }
30853         
30854         if(this.fireEvent('click', this, e) === false){
30855             return;
30856         };
30857         
30858         this.parent().setActiveItem(this);
30859     },
30860     
30861     isActive: function () 
30862     {
30863         return this.active;
30864     },
30865     
30866     setActive : function(state)
30867     {
30868         if(this.active == state){
30869             return;
30870         }
30871         
30872         this.active = state;
30873         
30874         if (state) {
30875             this.el.addClass('active');
30876             return;
30877         }
30878         
30879         this.el.removeClass('active');
30880         
30881         return;
30882     },
30883     
30884     setDisabled : function(state)
30885     {
30886         if(this.disabled == state){
30887             return;
30888         }
30889         
30890         this.disabled = state;
30891         
30892         if (state) {
30893             this.el.addClass('disabled');
30894             return;
30895         }
30896         
30897         this.el.removeClass('disabled');
30898     },
30899     
30900     tooltipEl : function()
30901     {
30902         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30903     }
30904 });
30905  
30906
30907  /*
30908  * - LGPL
30909  *
30910  * FieldLabel
30911  * 
30912  */
30913
30914 /**
30915  * @class Roo.bootstrap.FieldLabel
30916  * @extends Roo.bootstrap.Component
30917  * Bootstrap FieldLabel class
30918  * @cfg {String} html contents of the element
30919  * @cfg {String} tag tag of the element default label
30920  * @cfg {String} cls class of the element
30921  * @cfg {String} target label target 
30922  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30923  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30924  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30925  * @cfg {String} iconTooltip default "This field is required"
30926  * @cfg {String} indicatorpos (left|right) default left
30927  * 
30928  * @constructor
30929  * Create a new FieldLabel
30930  * @param {Object} config The config object
30931  */
30932
30933 Roo.bootstrap.FieldLabel = function(config){
30934     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30935     
30936     this.addEvents({
30937             /**
30938              * @event invalid
30939              * Fires after the field has been marked as invalid.
30940              * @param {Roo.form.FieldLabel} this
30941              * @param {String} msg The validation message
30942              */
30943             invalid : true,
30944             /**
30945              * @event valid
30946              * Fires after the field has been validated with no errors.
30947              * @param {Roo.form.FieldLabel} this
30948              */
30949             valid : true
30950         });
30951 };
30952
30953 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30954     
30955     tag: 'label',
30956     cls: '',
30957     html: '',
30958     target: '',
30959     allowBlank : true,
30960     invalidClass : 'has-warning',
30961     validClass : 'has-success',
30962     iconTooltip : 'This field is required',
30963     indicatorpos : 'left',
30964     
30965     getAutoCreate : function(){
30966         
30967         var cls = "";
30968         if (!this.allowBlank) {
30969             cls  = "visible";
30970         }
30971         
30972         var cfg = {
30973             tag : this.tag,
30974             cls : 'roo-bootstrap-field-label ' + this.cls,
30975             for : this.target,
30976             cn : [
30977                 {
30978                     tag : 'i',
30979                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30980                     tooltip : this.iconTooltip
30981                 },
30982                 {
30983                     tag : 'span',
30984                     html : this.html
30985                 }
30986             ] 
30987         };
30988         
30989         if(this.indicatorpos == 'right'){
30990             var cfg = {
30991                 tag : this.tag,
30992                 cls : 'roo-bootstrap-field-label ' + this.cls,
30993                 for : this.target,
30994                 cn : [
30995                     {
30996                         tag : 'span',
30997                         html : this.html
30998                     },
30999                     {
31000                         tag : 'i',
31001                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31002                         tooltip : this.iconTooltip
31003                     }
31004                 ] 
31005             };
31006         }
31007         
31008         return cfg;
31009     },
31010     
31011     initEvents: function() 
31012     {
31013         Roo.bootstrap.Element.superclass.initEvents.call(this);
31014         
31015         this.indicator = this.indicatorEl();
31016         
31017         if(this.indicator){
31018             this.indicator.removeClass('visible');
31019             this.indicator.addClass('invisible');
31020         }
31021         
31022         Roo.bootstrap.FieldLabel.register(this);
31023     },
31024     
31025     indicatorEl : function()
31026     {
31027         var indicator = this.el.select('i.roo-required-indicator',true).first();
31028         
31029         if(!indicator){
31030             return false;
31031         }
31032         
31033         return indicator;
31034         
31035     },
31036     
31037     /**
31038      * Mark this field as valid
31039      */
31040     markValid : function()
31041     {
31042         if(this.indicator){
31043             this.indicator.removeClass('visible');
31044             this.indicator.addClass('invisible');
31045         }
31046         if (Roo.bootstrap.version == 3) {
31047             this.el.removeClass(this.invalidClass);
31048             this.el.addClass(this.validClass);
31049         } else {
31050             this.el.removeClass('is-invalid');
31051             this.el.addClass('is-valid');
31052         }
31053         
31054         
31055         this.fireEvent('valid', this);
31056     },
31057     
31058     /**
31059      * Mark this field as invalid
31060      * @param {String} msg The validation message
31061      */
31062     markInvalid : function(msg)
31063     {
31064         if(this.indicator){
31065             this.indicator.removeClass('invisible');
31066             this.indicator.addClass('visible');
31067         }
31068           if (Roo.bootstrap.version == 3) {
31069             this.el.removeClass(this.validClass);
31070             this.el.addClass(this.invalidClass);
31071         } else {
31072             this.el.removeClass('is-valid');
31073             this.el.addClass('is-invalid');
31074         }
31075         
31076         
31077         this.fireEvent('invalid', this, msg);
31078     }
31079     
31080    
31081 });
31082
31083 Roo.apply(Roo.bootstrap.FieldLabel, {
31084     
31085     groups: {},
31086     
31087      /**
31088     * register a FieldLabel Group
31089     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31090     */
31091     register : function(label)
31092     {
31093         if(this.groups.hasOwnProperty(label.target)){
31094             return;
31095         }
31096      
31097         this.groups[label.target] = label;
31098         
31099     },
31100     /**
31101     * fetch a FieldLabel Group based on the target
31102     * @param {string} target
31103     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31104     */
31105     get: function(target) {
31106         if (typeof(this.groups[target]) == 'undefined') {
31107             return false;
31108         }
31109         
31110         return this.groups[target] ;
31111     }
31112 });
31113
31114  
31115
31116  /*
31117  * - LGPL
31118  *
31119  * page DateSplitField.
31120  * 
31121  */
31122
31123
31124 /**
31125  * @class Roo.bootstrap.DateSplitField
31126  * @extends Roo.bootstrap.Component
31127  * Bootstrap DateSplitField class
31128  * @cfg {string} fieldLabel - the label associated
31129  * @cfg {Number} labelWidth set the width of label (0-12)
31130  * @cfg {String} labelAlign (top|left)
31131  * @cfg {Boolean} dayAllowBlank (true|false) default false
31132  * @cfg {Boolean} monthAllowBlank (true|false) default false
31133  * @cfg {Boolean} yearAllowBlank (true|false) default false
31134  * @cfg {string} dayPlaceholder 
31135  * @cfg {string} monthPlaceholder
31136  * @cfg {string} yearPlaceholder
31137  * @cfg {string} dayFormat default 'd'
31138  * @cfg {string} monthFormat default 'm'
31139  * @cfg {string} yearFormat default 'Y'
31140  * @cfg {Number} labellg set the width of label (1-12)
31141  * @cfg {Number} labelmd set the width of label (1-12)
31142  * @cfg {Number} labelsm set the width of label (1-12)
31143  * @cfg {Number} labelxs set the width of label (1-12)
31144
31145  *     
31146  * @constructor
31147  * Create a new DateSplitField
31148  * @param {Object} config The config object
31149  */
31150
31151 Roo.bootstrap.DateSplitField = function(config){
31152     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31153     
31154     this.addEvents({
31155         // raw events
31156          /**
31157          * @event years
31158          * getting the data of years
31159          * @param {Roo.bootstrap.DateSplitField} this
31160          * @param {Object} years
31161          */
31162         "years" : true,
31163         /**
31164          * @event days
31165          * getting the data of days
31166          * @param {Roo.bootstrap.DateSplitField} this
31167          * @param {Object} days
31168          */
31169         "days" : true,
31170         /**
31171          * @event invalid
31172          * Fires after the field has been marked as invalid.
31173          * @param {Roo.form.Field} this
31174          * @param {String} msg The validation message
31175          */
31176         invalid : true,
31177        /**
31178          * @event valid
31179          * Fires after the field has been validated with no errors.
31180          * @param {Roo.form.Field} this
31181          */
31182         valid : true
31183     });
31184 };
31185
31186 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31187     
31188     fieldLabel : '',
31189     labelAlign : 'top',
31190     labelWidth : 3,
31191     dayAllowBlank : false,
31192     monthAllowBlank : false,
31193     yearAllowBlank : false,
31194     dayPlaceholder : '',
31195     monthPlaceholder : '',
31196     yearPlaceholder : '',
31197     dayFormat : 'd',
31198     monthFormat : 'm',
31199     yearFormat : 'Y',
31200     isFormField : true,
31201     labellg : 0,
31202     labelmd : 0,
31203     labelsm : 0,
31204     labelxs : 0,
31205     
31206     getAutoCreate : function()
31207     {
31208         var cfg = {
31209             tag : 'div',
31210             cls : 'row roo-date-split-field-group',
31211             cn : [
31212                 {
31213                     tag : 'input',
31214                     type : 'hidden',
31215                     cls : 'form-hidden-field roo-date-split-field-group-value',
31216                     name : this.name
31217                 }
31218             ]
31219         };
31220         
31221         var labelCls = 'col-md-12';
31222         var contentCls = 'col-md-4';
31223         
31224         if(this.fieldLabel){
31225             
31226             var label = {
31227                 tag : 'div',
31228                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31229                 cn : [
31230                     {
31231                         tag : 'label',
31232                         html : this.fieldLabel
31233                     }
31234                 ]
31235             };
31236             
31237             if(this.labelAlign == 'left'){
31238             
31239                 if(this.labelWidth > 12){
31240                     label.style = "width: " + this.labelWidth + 'px';
31241                 }
31242
31243                 if(this.labelWidth < 13 && this.labelmd == 0){
31244                     this.labelmd = this.labelWidth;
31245                 }
31246
31247                 if(this.labellg > 0){
31248                     labelCls = ' col-lg-' + this.labellg;
31249                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31250                 }
31251
31252                 if(this.labelmd > 0){
31253                     labelCls = ' col-md-' + this.labelmd;
31254                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31255                 }
31256
31257                 if(this.labelsm > 0){
31258                     labelCls = ' col-sm-' + this.labelsm;
31259                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31260                 }
31261
31262                 if(this.labelxs > 0){
31263                     labelCls = ' col-xs-' + this.labelxs;
31264                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31265                 }
31266             }
31267             
31268             label.cls += ' ' + labelCls;
31269             
31270             cfg.cn.push(label);
31271         }
31272         
31273         Roo.each(['day', 'month', 'year'], function(t){
31274             cfg.cn.push({
31275                 tag : 'div',
31276                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31277             });
31278         }, this);
31279         
31280         return cfg;
31281     },
31282     
31283     inputEl: function ()
31284     {
31285         return this.el.select('.roo-date-split-field-group-value', true).first();
31286     },
31287     
31288     onRender : function(ct, position) 
31289     {
31290         var _this = this;
31291         
31292         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31293         
31294         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31295         
31296         this.dayField = new Roo.bootstrap.ComboBox({
31297             allowBlank : this.dayAllowBlank,
31298             alwaysQuery : true,
31299             displayField : 'value',
31300             editable : false,
31301             fieldLabel : '',
31302             forceSelection : true,
31303             mode : 'local',
31304             placeholder : this.dayPlaceholder,
31305             selectOnFocus : true,
31306             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31307             triggerAction : 'all',
31308             typeAhead : true,
31309             valueField : 'value',
31310             store : new Roo.data.SimpleStore({
31311                 data : (function() {    
31312                     var days = [];
31313                     _this.fireEvent('days', _this, days);
31314                     return days;
31315                 })(),
31316                 fields : [ 'value' ]
31317             }),
31318             listeners : {
31319                 select : function (_self, record, index)
31320                 {
31321                     _this.setValue(_this.getValue());
31322                 }
31323             }
31324         });
31325
31326         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31327         
31328         this.monthField = new Roo.bootstrap.MonthField({
31329             after : '<i class=\"fa fa-calendar\"></i>',
31330             allowBlank : this.monthAllowBlank,
31331             placeholder : this.monthPlaceholder,
31332             readOnly : true,
31333             listeners : {
31334                 render : function (_self)
31335                 {
31336                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31337                         e.preventDefault();
31338                         _self.focus();
31339                     });
31340                 },
31341                 select : function (_self, oldvalue, newvalue)
31342                 {
31343                     _this.setValue(_this.getValue());
31344                 }
31345             }
31346         });
31347         
31348         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31349         
31350         this.yearField = new Roo.bootstrap.ComboBox({
31351             allowBlank : this.yearAllowBlank,
31352             alwaysQuery : true,
31353             displayField : 'value',
31354             editable : false,
31355             fieldLabel : '',
31356             forceSelection : true,
31357             mode : 'local',
31358             placeholder : this.yearPlaceholder,
31359             selectOnFocus : true,
31360             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31361             triggerAction : 'all',
31362             typeAhead : true,
31363             valueField : 'value',
31364             store : new Roo.data.SimpleStore({
31365                 data : (function() {
31366                     var years = [];
31367                     _this.fireEvent('years', _this, years);
31368                     return years;
31369                 })(),
31370                 fields : [ 'value' ]
31371             }),
31372             listeners : {
31373                 select : function (_self, record, index)
31374                 {
31375                     _this.setValue(_this.getValue());
31376                 }
31377             }
31378         });
31379
31380         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31381     },
31382     
31383     setValue : function(v, format)
31384     {
31385         this.inputEl.dom.value = v;
31386         
31387         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31388         
31389         var d = Date.parseDate(v, f);
31390         
31391         if(!d){
31392             this.validate();
31393             return;
31394         }
31395         
31396         this.setDay(d.format(this.dayFormat));
31397         this.setMonth(d.format(this.monthFormat));
31398         this.setYear(d.format(this.yearFormat));
31399         
31400         this.validate();
31401         
31402         return;
31403     },
31404     
31405     setDay : function(v)
31406     {
31407         this.dayField.setValue(v);
31408         this.inputEl.dom.value = this.getValue();
31409         this.validate();
31410         return;
31411     },
31412     
31413     setMonth : function(v)
31414     {
31415         this.monthField.setValue(v, true);
31416         this.inputEl.dom.value = this.getValue();
31417         this.validate();
31418         return;
31419     },
31420     
31421     setYear : function(v)
31422     {
31423         this.yearField.setValue(v);
31424         this.inputEl.dom.value = this.getValue();
31425         this.validate();
31426         return;
31427     },
31428     
31429     getDay : function()
31430     {
31431         return this.dayField.getValue();
31432     },
31433     
31434     getMonth : function()
31435     {
31436         return this.monthField.getValue();
31437     },
31438     
31439     getYear : function()
31440     {
31441         return this.yearField.getValue();
31442     },
31443     
31444     getValue : function()
31445     {
31446         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31447         
31448         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31449         
31450         return date;
31451     },
31452     
31453     reset : function()
31454     {
31455         this.setDay('');
31456         this.setMonth('');
31457         this.setYear('');
31458         this.inputEl.dom.value = '';
31459         this.validate();
31460         return;
31461     },
31462     
31463     validate : function()
31464     {
31465         var d = this.dayField.validate();
31466         var m = this.monthField.validate();
31467         var y = this.yearField.validate();
31468         
31469         var valid = true;
31470         
31471         if(
31472                 (!this.dayAllowBlank && !d) ||
31473                 (!this.monthAllowBlank && !m) ||
31474                 (!this.yearAllowBlank && !y)
31475         ){
31476             valid = false;
31477         }
31478         
31479         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31480             return valid;
31481         }
31482         
31483         if(valid){
31484             this.markValid();
31485             return valid;
31486         }
31487         
31488         this.markInvalid();
31489         
31490         return valid;
31491     },
31492     
31493     markValid : function()
31494     {
31495         
31496         var label = this.el.select('label', true).first();
31497         var icon = this.el.select('i.fa-star', true).first();
31498
31499         if(label && icon){
31500             icon.remove();
31501         }
31502         
31503         this.fireEvent('valid', this);
31504     },
31505     
31506      /**
31507      * Mark this field as invalid
31508      * @param {String} msg The validation message
31509      */
31510     markInvalid : function(msg)
31511     {
31512         
31513         var label = this.el.select('label', true).first();
31514         var icon = this.el.select('i.fa-star', true).first();
31515
31516         if(label && !icon){
31517             this.el.select('.roo-date-split-field-label', true).createChild({
31518                 tag : 'i',
31519                 cls : 'text-danger fa fa-lg fa-star',
31520                 tooltip : 'This field is required',
31521                 style : 'margin-right:5px;'
31522             }, label, true);
31523         }
31524         
31525         this.fireEvent('invalid', this, msg);
31526     },
31527     
31528     clearInvalid : function()
31529     {
31530         var label = this.el.select('label', true).first();
31531         var icon = this.el.select('i.fa-star', true).first();
31532
31533         if(label && icon){
31534             icon.remove();
31535         }
31536         
31537         this.fireEvent('valid', this);
31538     },
31539     
31540     getName: function()
31541     {
31542         return this.name;
31543     }
31544     
31545 });
31546
31547  /**
31548  *
31549  * This is based on 
31550  * http://masonry.desandro.com
31551  *
31552  * The idea is to render all the bricks based on vertical width...
31553  *
31554  * The original code extends 'outlayer' - we might need to use that....
31555  * 
31556  */
31557
31558
31559 /**
31560  * @class Roo.bootstrap.LayoutMasonry
31561  * @extends Roo.bootstrap.Component
31562  * Bootstrap Layout Masonry class
31563  * 
31564  * @constructor
31565  * Create a new Element
31566  * @param {Object} config The config object
31567  */
31568
31569 Roo.bootstrap.LayoutMasonry = function(config){
31570     
31571     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31572     
31573     this.bricks = [];
31574     
31575     Roo.bootstrap.LayoutMasonry.register(this);
31576     
31577     this.addEvents({
31578         // raw events
31579         /**
31580          * @event layout
31581          * Fire after layout the items
31582          * @param {Roo.bootstrap.LayoutMasonry} this
31583          * @param {Roo.EventObject} e
31584          */
31585         "layout" : true
31586     });
31587     
31588 };
31589
31590 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31591     
31592     /**
31593      * @cfg {Boolean} isLayoutInstant = no animation?
31594      */   
31595     isLayoutInstant : false, // needed?
31596    
31597     /**
31598      * @cfg {Number} boxWidth  width of the columns
31599      */   
31600     boxWidth : 450,
31601     
31602       /**
31603      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31604      */   
31605     boxHeight : 0,
31606     
31607     /**
31608      * @cfg {Number} padWidth padding below box..
31609      */   
31610     padWidth : 10, 
31611     
31612     /**
31613      * @cfg {Number} gutter gutter width..
31614      */   
31615     gutter : 10,
31616     
31617      /**
31618      * @cfg {Number} maxCols maximum number of columns
31619      */   
31620     
31621     maxCols: 0,
31622     
31623     /**
31624      * @cfg {Boolean} isAutoInitial defalut true
31625      */   
31626     isAutoInitial : true, 
31627     
31628     containerWidth: 0,
31629     
31630     /**
31631      * @cfg {Boolean} isHorizontal defalut false
31632      */   
31633     isHorizontal : false, 
31634
31635     currentSize : null,
31636     
31637     tag: 'div',
31638     
31639     cls: '',
31640     
31641     bricks: null, //CompositeElement
31642     
31643     cols : 1,
31644     
31645     _isLayoutInited : false,
31646     
31647 //    isAlternative : false, // only use for vertical layout...
31648     
31649     /**
31650      * @cfg {Number} alternativePadWidth padding below box..
31651      */   
31652     alternativePadWidth : 50,
31653     
31654     selectedBrick : [],
31655     
31656     getAutoCreate : function(){
31657         
31658         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31659         
31660         var cfg = {
31661             tag: this.tag,
31662             cls: 'blog-masonary-wrapper ' + this.cls,
31663             cn : {
31664                 cls : 'mas-boxes masonary'
31665             }
31666         };
31667         
31668         return cfg;
31669     },
31670     
31671     getChildContainer: function( )
31672     {
31673         if (this.boxesEl) {
31674             return this.boxesEl;
31675         }
31676         
31677         this.boxesEl = this.el.select('.mas-boxes').first();
31678         
31679         return this.boxesEl;
31680     },
31681     
31682     
31683     initEvents : function()
31684     {
31685         var _this = this;
31686         
31687         if(this.isAutoInitial){
31688             Roo.log('hook children rendered');
31689             this.on('childrenrendered', function() {
31690                 Roo.log('children rendered');
31691                 _this.initial();
31692             } ,this);
31693         }
31694     },
31695     
31696     initial : function()
31697     {
31698         this.selectedBrick = [];
31699         
31700         this.currentSize = this.el.getBox(true);
31701         
31702         Roo.EventManager.onWindowResize(this.resize, this); 
31703
31704         if(!this.isAutoInitial){
31705             this.layout();
31706             return;
31707         }
31708         
31709         this.layout();
31710         
31711         return;
31712         //this.layout.defer(500,this);
31713         
31714     },
31715     
31716     resize : function()
31717     {
31718         var cs = this.el.getBox(true);
31719         
31720         if (
31721                 this.currentSize.width == cs.width && 
31722                 this.currentSize.x == cs.x && 
31723                 this.currentSize.height == cs.height && 
31724                 this.currentSize.y == cs.y 
31725         ) {
31726             Roo.log("no change in with or X or Y");
31727             return;
31728         }
31729         
31730         this.currentSize = cs;
31731         
31732         this.layout();
31733         
31734     },
31735     
31736     layout : function()
31737     {   
31738         this._resetLayout();
31739         
31740         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31741         
31742         this.layoutItems( isInstant );
31743       
31744         this._isLayoutInited = true;
31745         
31746         this.fireEvent('layout', this);
31747         
31748     },
31749     
31750     _resetLayout : function()
31751     {
31752         if(this.isHorizontal){
31753             this.horizontalMeasureColumns();
31754             return;
31755         }
31756         
31757         this.verticalMeasureColumns();
31758         
31759     },
31760     
31761     verticalMeasureColumns : function()
31762     {
31763         this.getContainerWidth();
31764         
31765 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31766 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31767 //            return;
31768 //        }
31769         
31770         var boxWidth = this.boxWidth + this.padWidth;
31771         
31772         if(this.containerWidth < this.boxWidth){
31773             boxWidth = this.containerWidth
31774         }
31775         
31776         var containerWidth = this.containerWidth;
31777         
31778         var cols = Math.floor(containerWidth / boxWidth);
31779         
31780         this.cols = Math.max( cols, 1 );
31781         
31782         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31783         
31784         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31785         
31786         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31787         
31788         this.colWidth = boxWidth + avail - this.padWidth;
31789         
31790         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31791         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31792     },
31793     
31794     horizontalMeasureColumns : function()
31795     {
31796         this.getContainerWidth();
31797         
31798         var boxWidth = this.boxWidth;
31799         
31800         if(this.containerWidth < boxWidth){
31801             boxWidth = this.containerWidth;
31802         }
31803         
31804         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31805         
31806         this.el.setHeight(boxWidth);
31807         
31808     },
31809     
31810     getContainerWidth : function()
31811     {
31812         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31813     },
31814     
31815     layoutItems : function( isInstant )
31816     {
31817         Roo.log(this.bricks);
31818         
31819         var items = Roo.apply([], this.bricks);
31820         
31821         if(this.isHorizontal){
31822             this._horizontalLayoutItems( items , isInstant );
31823             return;
31824         }
31825         
31826 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31827 //            this._verticalAlternativeLayoutItems( items , isInstant );
31828 //            return;
31829 //        }
31830         
31831         this._verticalLayoutItems( items , isInstant );
31832         
31833     },
31834     
31835     _verticalLayoutItems : function ( items , isInstant)
31836     {
31837         if ( !items || !items.length ) {
31838             return;
31839         }
31840         
31841         var standard = [
31842             ['xs', 'xs', 'xs', 'tall'],
31843             ['xs', 'xs', 'tall'],
31844             ['xs', 'xs', 'sm'],
31845             ['xs', 'xs', 'xs'],
31846             ['xs', 'tall'],
31847             ['xs', 'sm'],
31848             ['xs', 'xs'],
31849             ['xs'],
31850             
31851             ['sm', 'xs', 'xs'],
31852             ['sm', 'xs'],
31853             ['sm'],
31854             
31855             ['tall', 'xs', 'xs', 'xs'],
31856             ['tall', 'xs', 'xs'],
31857             ['tall', 'xs'],
31858             ['tall']
31859             
31860         ];
31861         
31862         var queue = [];
31863         
31864         var boxes = [];
31865         
31866         var box = [];
31867         
31868         Roo.each(items, function(item, k){
31869             
31870             switch (item.size) {
31871                 // these layouts take up a full box,
31872                 case 'md' :
31873                 case 'md-left' :
31874                 case 'md-right' :
31875                 case 'wide' :
31876                     
31877                     if(box.length){
31878                         boxes.push(box);
31879                         box = [];
31880                     }
31881                     
31882                     boxes.push([item]);
31883                     
31884                     break;
31885                     
31886                 case 'xs' :
31887                 case 'sm' :
31888                 case 'tall' :
31889                     
31890                     box.push(item);
31891                     
31892                     break;
31893                 default :
31894                     break;
31895                     
31896             }
31897             
31898         }, this);
31899         
31900         if(box.length){
31901             boxes.push(box);
31902             box = [];
31903         }
31904         
31905         var filterPattern = function(box, length)
31906         {
31907             if(!box.length){
31908                 return;
31909             }
31910             
31911             var match = false;
31912             
31913             var pattern = box.slice(0, length);
31914             
31915             var format = [];
31916             
31917             Roo.each(pattern, function(i){
31918                 format.push(i.size);
31919             }, this);
31920             
31921             Roo.each(standard, function(s){
31922                 
31923                 if(String(s) != String(format)){
31924                     return;
31925                 }
31926                 
31927                 match = true;
31928                 return false;
31929                 
31930             }, this);
31931             
31932             if(!match && length == 1){
31933                 return;
31934             }
31935             
31936             if(!match){
31937                 filterPattern(box, length - 1);
31938                 return;
31939             }
31940                 
31941             queue.push(pattern);
31942
31943             box = box.slice(length, box.length);
31944
31945             filterPattern(box, 4);
31946
31947             return;
31948             
31949         }
31950         
31951         Roo.each(boxes, function(box, k){
31952             
31953             if(!box.length){
31954                 return;
31955             }
31956             
31957             if(box.length == 1){
31958                 queue.push(box);
31959                 return;
31960             }
31961             
31962             filterPattern(box, 4);
31963             
31964         }, this);
31965         
31966         this._processVerticalLayoutQueue( queue, isInstant );
31967         
31968     },
31969     
31970 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31971 //    {
31972 //        if ( !items || !items.length ) {
31973 //            return;
31974 //        }
31975 //
31976 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31977 //        
31978 //    },
31979     
31980     _horizontalLayoutItems : function ( items , isInstant)
31981     {
31982         if ( !items || !items.length || items.length < 3) {
31983             return;
31984         }
31985         
31986         items.reverse();
31987         
31988         var eItems = items.slice(0, 3);
31989         
31990         items = items.slice(3, items.length);
31991         
31992         var standard = [
31993             ['xs', 'xs', 'xs', 'wide'],
31994             ['xs', 'xs', 'wide'],
31995             ['xs', 'xs', 'sm'],
31996             ['xs', 'xs', 'xs'],
31997             ['xs', 'wide'],
31998             ['xs', 'sm'],
31999             ['xs', 'xs'],
32000             ['xs'],
32001             
32002             ['sm', 'xs', 'xs'],
32003             ['sm', 'xs'],
32004             ['sm'],
32005             
32006             ['wide', 'xs', 'xs', 'xs'],
32007             ['wide', 'xs', 'xs'],
32008             ['wide', 'xs'],
32009             ['wide'],
32010             
32011             ['wide-thin']
32012         ];
32013         
32014         var queue = [];
32015         
32016         var boxes = [];
32017         
32018         var box = [];
32019         
32020         Roo.each(items, function(item, k){
32021             
32022             switch (item.size) {
32023                 case 'md' :
32024                 case 'md-left' :
32025                 case 'md-right' :
32026                 case 'tall' :
32027                     
32028                     if(box.length){
32029                         boxes.push(box);
32030                         box = [];
32031                     }
32032                     
32033                     boxes.push([item]);
32034                     
32035                     break;
32036                     
32037                 case 'xs' :
32038                 case 'sm' :
32039                 case 'wide' :
32040                 case 'wide-thin' :
32041                     
32042                     box.push(item);
32043                     
32044                     break;
32045                 default :
32046                     break;
32047                     
32048             }
32049             
32050         }, this);
32051         
32052         if(box.length){
32053             boxes.push(box);
32054             box = [];
32055         }
32056         
32057         var filterPattern = function(box, length)
32058         {
32059             if(!box.length){
32060                 return;
32061             }
32062             
32063             var match = false;
32064             
32065             var pattern = box.slice(0, length);
32066             
32067             var format = [];
32068             
32069             Roo.each(pattern, function(i){
32070                 format.push(i.size);
32071             }, this);
32072             
32073             Roo.each(standard, function(s){
32074                 
32075                 if(String(s) != String(format)){
32076                     return;
32077                 }
32078                 
32079                 match = true;
32080                 return false;
32081                 
32082             }, this);
32083             
32084             if(!match && length == 1){
32085                 return;
32086             }
32087             
32088             if(!match){
32089                 filterPattern(box, length - 1);
32090                 return;
32091             }
32092                 
32093             queue.push(pattern);
32094
32095             box = box.slice(length, box.length);
32096
32097             filterPattern(box, 4);
32098
32099             return;
32100             
32101         }
32102         
32103         Roo.each(boxes, function(box, k){
32104             
32105             if(!box.length){
32106                 return;
32107             }
32108             
32109             if(box.length == 1){
32110                 queue.push(box);
32111                 return;
32112             }
32113             
32114             filterPattern(box, 4);
32115             
32116         }, this);
32117         
32118         
32119         var prune = [];
32120         
32121         var pos = this.el.getBox(true);
32122         
32123         var minX = pos.x;
32124         
32125         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32126         
32127         var hit_end = false;
32128         
32129         Roo.each(queue, function(box){
32130             
32131             if(hit_end){
32132                 
32133                 Roo.each(box, function(b){
32134                 
32135                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32136                     b.el.hide();
32137
32138                 }, this);
32139
32140                 return;
32141             }
32142             
32143             var mx = 0;
32144             
32145             Roo.each(box, function(b){
32146                 
32147                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32148                 b.el.show();
32149
32150                 mx = Math.max(mx, b.x);
32151                 
32152             }, this);
32153             
32154             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32155             
32156             if(maxX < minX){
32157                 
32158                 Roo.each(box, function(b){
32159                 
32160                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32161                     b.el.hide();
32162                     
32163                 }, this);
32164                 
32165                 hit_end = true;
32166                 
32167                 return;
32168             }
32169             
32170             prune.push(box);
32171             
32172         }, this);
32173         
32174         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32175     },
32176     
32177     /** Sets position of item in DOM
32178     * @param {Element} item
32179     * @param {Number} x - horizontal position
32180     * @param {Number} y - vertical position
32181     * @param {Boolean} isInstant - disables transitions
32182     */
32183     _processVerticalLayoutQueue : function( queue, isInstant )
32184     {
32185         var pos = this.el.getBox(true);
32186         var x = pos.x;
32187         var y = pos.y;
32188         var maxY = [];
32189         
32190         for (var i = 0; i < this.cols; i++){
32191             maxY[i] = pos.y;
32192         }
32193         
32194         Roo.each(queue, function(box, k){
32195             
32196             var col = k % this.cols;
32197             
32198             Roo.each(box, function(b,kk){
32199                 
32200                 b.el.position('absolute');
32201                 
32202                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32203                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32204                 
32205                 if(b.size == 'md-left' || b.size == 'md-right'){
32206                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32207                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32208                 }
32209                 
32210                 b.el.setWidth(width);
32211                 b.el.setHeight(height);
32212                 // iframe?
32213                 b.el.select('iframe',true).setSize(width,height);
32214                 
32215             }, this);
32216             
32217             for (var i = 0; i < this.cols; i++){
32218                 
32219                 if(maxY[i] < maxY[col]){
32220                     col = i;
32221                     continue;
32222                 }
32223                 
32224                 col = Math.min(col, i);
32225                 
32226             }
32227             
32228             x = pos.x + col * (this.colWidth + this.padWidth);
32229             
32230             y = maxY[col];
32231             
32232             var positions = [];
32233             
32234             switch (box.length){
32235                 case 1 :
32236                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32237                     break;
32238                 case 2 :
32239                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32240                     break;
32241                 case 3 :
32242                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32243                     break;
32244                 case 4 :
32245                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32246                     break;
32247                 default :
32248                     break;
32249             }
32250             
32251             Roo.each(box, function(b,kk){
32252                 
32253                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32254                 
32255                 var sz = b.el.getSize();
32256                 
32257                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32258                 
32259             }, this);
32260             
32261         }, this);
32262         
32263         var mY = 0;
32264         
32265         for (var i = 0; i < this.cols; i++){
32266             mY = Math.max(mY, maxY[i]);
32267         }
32268         
32269         this.el.setHeight(mY - pos.y);
32270         
32271     },
32272     
32273 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32274 //    {
32275 //        var pos = this.el.getBox(true);
32276 //        var x = pos.x;
32277 //        var y = pos.y;
32278 //        var maxX = pos.right;
32279 //        
32280 //        var maxHeight = 0;
32281 //        
32282 //        Roo.each(items, function(item, k){
32283 //            
32284 //            var c = k % 2;
32285 //            
32286 //            item.el.position('absolute');
32287 //                
32288 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32289 //
32290 //            item.el.setWidth(width);
32291 //
32292 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32293 //
32294 //            item.el.setHeight(height);
32295 //            
32296 //            if(c == 0){
32297 //                item.el.setXY([x, y], isInstant ? false : true);
32298 //            } else {
32299 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32300 //            }
32301 //            
32302 //            y = y + height + this.alternativePadWidth;
32303 //            
32304 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32305 //            
32306 //        }, this);
32307 //        
32308 //        this.el.setHeight(maxHeight);
32309 //        
32310 //    },
32311     
32312     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32313     {
32314         var pos = this.el.getBox(true);
32315         
32316         var minX = pos.x;
32317         var minY = pos.y;
32318         
32319         var maxX = pos.right;
32320         
32321         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32322         
32323         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32324         
32325         Roo.each(queue, function(box, k){
32326             
32327             Roo.each(box, function(b, kk){
32328                 
32329                 b.el.position('absolute');
32330                 
32331                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32332                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32333                 
32334                 if(b.size == 'md-left' || b.size == 'md-right'){
32335                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32336                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32337                 }
32338                 
32339                 b.el.setWidth(width);
32340                 b.el.setHeight(height);
32341                 
32342             }, this);
32343             
32344             if(!box.length){
32345                 return;
32346             }
32347             
32348             var positions = [];
32349             
32350             switch (box.length){
32351                 case 1 :
32352                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32353                     break;
32354                 case 2 :
32355                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32356                     break;
32357                 case 3 :
32358                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32359                     break;
32360                 case 4 :
32361                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32362                     break;
32363                 default :
32364                     break;
32365             }
32366             
32367             Roo.each(box, function(b,kk){
32368                 
32369                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32370                 
32371                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32372                 
32373             }, this);
32374             
32375         }, this);
32376         
32377     },
32378     
32379     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32380     {
32381         Roo.each(eItems, function(b,k){
32382             
32383             b.size = (k == 0) ? 'sm' : 'xs';
32384             b.x = (k == 0) ? 2 : 1;
32385             b.y = (k == 0) ? 2 : 1;
32386             
32387             b.el.position('absolute');
32388             
32389             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32390                 
32391             b.el.setWidth(width);
32392             
32393             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32394             
32395             b.el.setHeight(height);
32396             
32397         }, this);
32398
32399         var positions = [];
32400         
32401         positions.push({
32402             x : maxX - this.unitWidth * 2 - this.gutter,
32403             y : minY
32404         });
32405         
32406         positions.push({
32407             x : maxX - this.unitWidth,
32408             y : minY + (this.unitWidth + this.gutter) * 2
32409         });
32410         
32411         positions.push({
32412             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32413             y : minY
32414         });
32415         
32416         Roo.each(eItems, function(b,k){
32417             
32418             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32419
32420         }, this);
32421         
32422     },
32423     
32424     getVerticalOneBoxColPositions : function(x, y, box)
32425     {
32426         var pos = [];
32427         
32428         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32429         
32430         if(box[0].size == 'md-left'){
32431             rand = 0;
32432         }
32433         
32434         if(box[0].size == 'md-right'){
32435             rand = 1;
32436         }
32437         
32438         pos.push({
32439             x : x + (this.unitWidth + this.gutter) * rand,
32440             y : y
32441         });
32442         
32443         return pos;
32444     },
32445     
32446     getVerticalTwoBoxColPositions : function(x, y, box)
32447     {
32448         var pos = [];
32449         
32450         if(box[0].size == 'xs'){
32451             
32452             pos.push({
32453                 x : x,
32454                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32455             });
32456
32457             pos.push({
32458                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32459                 y : y
32460             });
32461             
32462             return pos;
32463             
32464         }
32465         
32466         pos.push({
32467             x : x,
32468             y : y
32469         });
32470
32471         pos.push({
32472             x : x + (this.unitWidth + this.gutter) * 2,
32473             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32474         });
32475         
32476         return pos;
32477         
32478     },
32479     
32480     getVerticalThreeBoxColPositions : function(x, y, box)
32481     {
32482         var pos = [];
32483         
32484         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32485             
32486             pos.push({
32487                 x : x,
32488                 y : y
32489             });
32490
32491             pos.push({
32492                 x : x + (this.unitWidth + this.gutter) * 1,
32493                 y : y
32494             });
32495             
32496             pos.push({
32497                 x : x + (this.unitWidth + this.gutter) * 2,
32498                 y : y
32499             });
32500             
32501             return pos;
32502             
32503         }
32504         
32505         if(box[0].size == 'xs' && box[1].size == 'xs'){
32506             
32507             pos.push({
32508                 x : x,
32509                 y : y
32510             });
32511
32512             pos.push({
32513                 x : x,
32514                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32515             });
32516             
32517             pos.push({
32518                 x : x + (this.unitWidth + this.gutter) * 1,
32519                 y : y
32520             });
32521             
32522             return pos;
32523             
32524         }
32525         
32526         pos.push({
32527             x : x,
32528             y : y
32529         });
32530
32531         pos.push({
32532             x : x + (this.unitWidth + this.gutter) * 2,
32533             y : y
32534         });
32535
32536         pos.push({
32537             x : x + (this.unitWidth + this.gutter) * 2,
32538             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32539         });
32540             
32541         return pos;
32542         
32543     },
32544     
32545     getVerticalFourBoxColPositions : function(x, y, box)
32546     {
32547         var pos = [];
32548         
32549         if(box[0].size == 'xs'){
32550             
32551             pos.push({
32552                 x : x,
32553                 y : y
32554             });
32555
32556             pos.push({
32557                 x : x,
32558                 y : y + (this.unitHeight + this.gutter) * 1
32559             });
32560             
32561             pos.push({
32562                 x : x,
32563                 y : y + (this.unitHeight + this.gutter) * 2
32564             });
32565             
32566             pos.push({
32567                 x : x + (this.unitWidth + this.gutter) * 1,
32568                 y : y
32569             });
32570             
32571             return pos;
32572             
32573         }
32574         
32575         pos.push({
32576             x : x,
32577             y : y
32578         });
32579
32580         pos.push({
32581             x : x + (this.unitWidth + this.gutter) * 2,
32582             y : y
32583         });
32584
32585         pos.push({
32586             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32587             y : y + (this.unitHeight + this.gutter) * 1
32588         });
32589
32590         pos.push({
32591             x : x + (this.unitWidth + this.gutter) * 2,
32592             y : y + (this.unitWidth + this.gutter) * 2
32593         });
32594
32595         return pos;
32596         
32597     },
32598     
32599     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32600     {
32601         var pos = [];
32602         
32603         if(box[0].size == 'md-left'){
32604             pos.push({
32605                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32606                 y : minY
32607             });
32608             
32609             return pos;
32610         }
32611         
32612         if(box[0].size == 'md-right'){
32613             pos.push({
32614                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32615                 y : minY + (this.unitWidth + this.gutter) * 1
32616             });
32617             
32618             return pos;
32619         }
32620         
32621         var rand = Math.floor(Math.random() * (4 - box[0].y));
32622         
32623         pos.push({
32624             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32625             y : minY + (this.unitWidth + this.gutter) * rand
32626         });
32627         
32628         return pos;
32629         
32630     },
32631     
32632     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32633     {
32634         var pos = [];
32635         
32636         if(box[0].size == 'xs'){
32637             
32638             pos.push({
32639                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32640                 y : minY
32641             });
32642
32643             pos.push({
32644                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32645                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32646             });
32647             
32648             return pos;
32649             
32650         }
32651         
32652         pos.push({
32653             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32654             y : minY
32655         });
32656
32657         pos.push({
32658             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32659             y : minY + (this.unitWidth + this.gutter) * 2
32660         });
32661         
32662         return pos;
32663         
32664     },
32665     
32666     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32667     {
32668         var pos = [];
32669         
32670         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32671             
32672             pos.push({
32673                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32674                 y : minY
32675             });
32676
32677             pos.push({
32678                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32679                 y : minY + (this.unitWidth + this.gutter) * 1
32680             });
32681             
32682             pos.push({
32683                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32684                 y : minY + (this.unitWidth + this.gutter) * 2
32685             });
32686             
32687             return pos;
32688             
32689         }
32690         
32691         if(box[0].size == 'xs' && box[1].size == 'xs'){
32692             
32693             pos.push({
32694                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32695                 y : minY
32696             });
32697
32698             pos.push({
32699                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32700                 y : minY
32701             });
32702             
32703             pos.push({
32704                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32705                 y : minY + (this.unitWidth + this.gutter) * 1
32706             });
32707             
32708             return pos;
32709             
32710         }
32711         
32712         pos.push({
32713             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32714             y : minY
32715         });
32716
32717         pos.push({
32718             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32719             y : minY + (this.unitWidth + this.gutter) * 2
32720         });
32721
32722         pos.push({
32723             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32724             y : minY + (this.unitWidth + this.gutter) * 2
32725         });
32726             
32727         return pos;
32728         
32729     },
32730     
32731     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32732     {
32733         var pos = [];
32734         
32735         if(box[0].size == 'xs'){
32736             
32737             pos.push({
32738                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32739                 y : minY
32740             });
32741
32742             pos.push({
32743                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32744                 y : minY
32745             });
32746             
32747             pos.push({
32748                 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),
32749                 y : minY
32750             });
32751             
32752             pos.push({
32753                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32754                 y : minY + (this.unitWidth + this.gutter) * 1
32755             });
32756             
32757             return pos;
32758             
32759         }
32760         
32761         pos.push({
32762             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32763             y : minY
32764         });
32765         
32766         pos.push({
32767             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32768             y : minY + (this.unitWidth + this.gutter) * 2
32769         });
32770         
32771         pos.push({
32772             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32773             y : minY + (this.unitWidth + this.gutter) * 2
32774         });
32775         
32776         pos.push({
32777             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),
32778             y : minY + (this.unitWidth + this.gutter) * 2
32779         });
32780
32781         return pos;
32782         
32783     },
32784     
32785     /**
32786     * remove a Masonry Brick
32787     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32788     */
32789     removeBrick : function(brick_id)
32790     {
32791         if (!brick_id) {
32792             return;
32793         }
32794         
32795         for (var i = 0; i<this.bricks.length; i++) {
32796             if (this.bricks[i].id == brick_id) {
32797                 this.bricks.splice(i,1);
32798                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32799                 this.initial();
32800             }
32801         }
32802     },
32803     
32804     /**
32805     * adds a Masonry Brick
32806     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32807     */
32808     addBrick : function(cfg)
32809     {
32810         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32811         //this.register(cn);
32812         cn.parentId = this.id;
32813         cn.render(this.el);
32814         return cn;
32815     },
32816     
32817     /**
32818     * register a Masonry Brick
32819     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32820     */
32821     
32822     register : function(brick)
32823     {
32824         this.bricks.push(brick);
32825         brick.masonryId = this.id;
32826     },
32827     
32828     /**
32829     * clear all the Masonry Brick
32830     */
32831     clearAll : function()
32832     {
32833         this.bricks = [];
32834         //this.getChildContainer().dom.innerHTML = "";
32835         this.el.dom.innerHTML = '';
32836     },
32837     
32838     getSelected : function()
32839     {
32840         if (!this.selectedBrick) {
32841             return false;
32842         }
32843         
32844         return this.selectedBrick;
32845     }
32846 });
32847
32848 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32849     
32850     groups: {},
32851      /**
32852     * register a Masonry Layout
32853     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32854     */
32855     
32856     register : function(layout)
32857     {
32858         this.groups[layout.id] = layout;
32859     },
32860     /**
32861     * fetch a  Masonry Layout based on the masonry layout ID
32862     * @param {string} the masonry layout to add
32863     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32864     */
32865     
32866     get: function(layout_id) {
32867         if (typeof(this.groups[layout_id]) == 'undefined') {
32868             return false;
32869         }
32870         return this.groups[layout_id] ;
32871     }
32872     
32873     
32874     
32875 });
32876
32877  
32878
32879  /**
32880  *
32881  * This is based on 
32882  * http://masonry.desandro.com
32883  *
32884  * The idea is to render all the bricks based on vertical width...
32885  *
32886  * The original code extends 'outlayer' - we might need to use that....
32887  * 
32888  */
32889
32890
32891 /**
32892  * @class Roo.bootstrap.LayoutMasonryAuto
32893  * @extends Roo.bootstrap.Component
32894  * Bootstrap Layout Masonry class
32895  * 
32896  * @constructor
32897  * Create a new Element
32898  * @param {Object} config The config object
32899  */
32900
32901 Roo.bootstrap.LayoutMasonryAuto = function(config){
32902     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32903 };
32904
32905 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32906     
32907       /**
32908      * @cfg {Boolean} isFitWidth  - resize the width..
32909      */   
32910     isFitWidth : false,  // options..
32911     /**
32912      * @cfg {Boolean} isOriginLeft = left align?
32913      */   
32914     isOriginLeft : true,
32915     /**
32916      * @cfg {Boolean} isOriginTop = top align?
32917      */   
32918     isOriginTop : false,
32919     /**
32920      * @cfg {Boolean} isLayoutInstant = no animation?
32921      */   
32922     isLayoutInstant : false, // needed?
32923     /**
32924      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32925      */   
32926     isResizingContainer : true,
32927     /**
32928      * @cfg {Number} columnWidth  width of the columns 
32929      */   
32930     
32931     columnWidth : 0,
32932     
32933     /**
32934      * @cfg {Number} maxCols maximum number of columns
32935      */   
32936     
32937     maxCols: 0,
32938     /**
32939      * @cfg {Number} padHeight padding below box..
32940      */   
32941     
32942     padHeight : 10, 
32943     
32944     /**
32945      * @cfg {Boolean} isAutoInitial defalut true
32946      */   
32947     
32948     isAutoInitial : true, 
32949     
32950     // private?
32951     gutter : 0,
32952     
32953     containerWidth: 0,
32954     initialColumnWidth : 0,
32955     currentSize : null,
32956     
32957     colYs : null, // array.
32958     maxY : 0,
32959     padWidth: 10,
32960     
32961     
32962     tag: 'div',
32963     cls: '',
32964     bricks: null, //CompositeElement
32965     cols : 0, // array?
32966     // element : null, // wrapped now this.el
32967     _isLayoutInited : null, 
32968     
32969     
32970     getAutoCreate : function(){
32971         
32972         var cfg = {
32973             tag: this.tag,
32974             cls: 'blog-masonary-wrapper ' + this.cls,
32975             cn : {
32976                 cls : 'mas-boxes masonary'
32977             }
32978         };
32979         
32980         return cfg;
32981     },
32982     
32983     getChildContainer: function( )
32984     {
32985         if (this.boxesEl) {
32986             return this.boxesEl;
32987         }
32988         
32989         this.boxesEl = this.el.select('.mas-boxes').first();
32990         
32991         return this.boxesEl;
32992     },
32993     
32994     
32995     initEvents : function()
32996     {
32997         var _this = this;
32998         
32999         if(this.isAutoInitial){
33000             Roo.log('hook children rendered');
33001             this.on('childrenrendered', function() {
33002                 Roo.log('children rendered');
33003                 _this.initial();
33004             } ,this);
33005         }
33006         
33007     },
33008     
33009     initial : function()
33010     {
33011         this.reloadItems();
33012
33013         this.currentSize = this.el.getBox(true);
33014
33015         /// was window resize... - let's see if this works..
33016         Roo.EventManager.onWindowResize(this.resize, this); 
33017
33018         if(!this.isAutoInitial){
33019             this.layout();
33020             return;
33021         }
33022         
33023         this.layout.defer(500,this);
33024     },
33025     
33026     reloadItems: function()
33027     {
33028         this.bricks = this.el.select('.masonry-brick', true);
33029         
33030         this.bricks.each(function(b) {
33031             //Roo.log(b.getSize());
33032             if (!b.attr('originalwidth')) {
33033                 b.attr('originalwidth',  b.getSize().width);
33034             }
33035             
33036         });
33037         
33038         Roo.log(this.bricks.elements.length);
33039     },
33040     
33041     resize : function()
33042     {
33043         Roo.log('resize');
33044         var cs = this.el.getBox(true);
33045         
33046         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33047             Roo.log("no change in with or X");
33048             return;
33049         }
33050         this.currentSize = cs;
33051         this.layout();
33052     },
33053     
33054     layout : function()
33055     {
33056          Roo.log('layout');
33057         this._resetLayout();
33058         //this._manageStamps();
33059       
33060         // don't animate first layout
33061         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33062         this.layoutItems( isInstant );
33063       
33064         // flag for initalized
33065         this._isLayoutInited = true;
33066     },
33067     
33068     layoutItems : function( isInstant )
33069     {
33070         //var items = this._getItemsForLayout( this.items );
33071         // original code supports filtering layout items.. we just ignore it..
33072         
33073         this._layoutItems( this.bricks , isInstant );
33074       
33075         this._postLayout();
33076     },
33077     _layoutItems : function ( items , isInstant)
33078     {
33079        //this.fireEvent( 'layout', this, items );
33080     
33081
33082         if ( !items || !items.elements.length ) {
33083           // no items, emit event with empty array
33084             return;
33085         }
33086
33087         var queue = [];
33088         items.each(function(item) {
33089             Roo.log("layout item");
33090             Roo.log(item);
33091             // get x/y object from method
33092             var position = this._getItemLayoutPosition( item );
33093             // enqueue
33094             position.item = item;
33095             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33096             queue.push( position );
33097         }, this);
33098       
33099         this._processLayoutQueue( queue );
33100     },
33101     /** Sets position of item in DOM
33102     * @param {Element} item
33103     * @param {Number} x - horizontal position
33104     * @param {Number} y - vertical position
33105     * @param {Boolean} isInstant - disables transitions
33106     */
33107     _processLayoutQueue : function( queue )
33108     {
33109         for ( var i=0, len = queue.length; i < len; i++ ) {
33110             var obj = queue[i];
33111             obj.item.position('absolute');
33112             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33113         }
33114     },
33115       
33116     
33117     /**
33118     * Any logic you want to do after each layout,
33119     * i.e. size the container
33120     */
33121     _postLayout : function()
33122     {
33123         this.resizeContainer();
33124     },
33125     
33126     resizeContainer : function()
33127     {
33128         if ( !this.isResizingContainer ) {
33129             return;
33130         }
33131         var size = this._getContainerSize();
33132         if ( size ) {
33133             this.el.setSize(size.width,size.height);
33134             this.boxesEl.setSize(size.width,size.height);
33135         }
33136     },
33137     
33138     
33139     
33140     _resetLayout : function()
33141     {
33142         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33143         this.colWidth = this.el.getWidth();
33144         //this.gutter = this.el.getWidth(); 
33145         
33146         this.measureColumns();
33147
33148         // reset column Y
33149         var i = this.cols;
33150         this.colYs = [];
33151         while (i--) {
33152             this.colYs.push( 0 );
33153         }
33154     
33155         this.maxY = 0;
33156     },
33157
33158     measureColumns : function()
33159     {
33160         this.getContainerWidth();
33161       // if columnWidth is 0, default to outerWidth of first item
33162         if ( !this.columnWidth ) {
33163             var firstItem = this.bricks.first();
33164             Roo.log(firstItem);
33165             this.columnWidth  = this.containerWidth;
33166             if (firstItem && firstItem.attr('originalwidth') ) {
33167                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33168             }
33169             // columnWidth fall back to item of first element
33170             Roo.log("set column width?");
33171                         this.initialColumnWidth = this.columnWidth  ;
33172
33173             // if first elem has no width, default to size of container
33174             
33175         }
33176         
33177         
33178         if (this.initialColumnWidth) {
33179             this.columnWidth = this.initialColumnWidth;
33180         }
33181         
33182         
33183             
33184         // column width is fixed at the top - however if container width get's smaller we should
33185         // reduce it...
33186         
33187         // this bit calcs how man columns..
33188             
33189         var columnWidth = this.columnWidth += this.gutter;
33190       
33191         // calculate columns
33192         var containerWidth = this.containerWidth + this.gutter;
33193         
33194         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33195         // fix rounding errors, typically with gutters
33196         var excess = columnWidth - containerWidth % columnWidth;
33197         
33198         
33199         // if overshoot is less than a pixel, round up, otherwise floor it
33200         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33201         cols = Math[ mathMethod ]( cols );
33202         this.cols = Math.max( cols, 1 );
33203         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33204         
33205          // padding positioning..
33206         var totalColWidth = this.cols * this.columnWidth;
33207         var padavail = this.containerWidth - totalColWidth;
33208         // so for 2 columns - we need 3 'pads'
33209         
33210         var padNeeded = (1+this.cols) * this.padWidth;
33211         
33212         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33213         
33214         this.columnWidth += padExtra
33215         //this.padWidth = Math.floor(padavail /  ( this.cols));
33216         
33217         // adjust colum width so that padding is fixed??
33218         
33219         // we have 3 columns ... total = width * 3
33220         // we have X left over... that should be used by 
33221         
33222         //if (this.expandC) {
33223             
33224         //}
33225         
33226         
33227         
33228     },
33229     
33230     getContainerWidth : function()
33231     {
33232        /* // container is parent if fit width
33233         var container = this.isFitWidth ? this.element.parentNode : this.element;
33234         // check that this.size and size are there
33235         // IE8 triggers resize on body size change, so they might not be
33236         
33237         var size = getSize( container );  //FIXME
33238         this.containerWidth = size && size.innerWidth; //FIXME
33239         */
33240          
33241         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33242         
33243     },
33244     
33245     _getItemLayoutPosition : function( item )  // what is item?
33246     {
33247         // we resize the item to our columnWidth..
33248       
33249         item.setWidth(this.columnWidth);
33250         item.autoBoxAdjust  = false;
33251         
33252         var sz = item.getSize();
33253  
33254         // how many columns does this brick span
33255         var remainder = this.containerWidth % this.columnWidth;
33256         
33257         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33258         // round if off by 1 pixel, otherwise use ceil
33259         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33260         colSpan = Math.min( colSpan, this.cols );
33261         
33262         // normally this should be '1' as we dont' currently allow multi width columns..
33263         
33264         var colGroup = this._getColGroup( colSpan );
33265         // get the minimum Y value from the columns
33266         var minimumY = Math.min.apply( Math, colGroup );
33267         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33268         
33269         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33270          
33271         // position the brick
33272         var position = {
33273             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33274             y: this.currentSize.y + minimumY + this.padHeight
33275         };
33276         
33277         Roo.log(position);
33278         // apply setHeight to necessary columns
33279         var setHeight = minimumY + sz.height + this.padHeight;
33280         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33281         
33282         var setSpan = this.cols + 1 - colGroup.length;
33283         for ( var i = 0; i < setSpan; i++ ) {
33284           this.colYs[ shortColIndex + i ] = setHeight ;
33285         }
33286       
33287         return position;
33288     },
33289     
33290     /**
33291      * @param {Number} colSpan - number of columns the element spans
33292      * @returns {Array} colGroup
33293      */
33294     _getColGroup : function( colSpan )
33295     {
33296         if ( colSpan < 2 ) {
33297           // if brick spans only one column, use all the column Ys
33298           return this.colYs;
33299         }
33300       
33301         var colGroup = [];
33302         // how many different places could this brick fit horizontally
33303         var groupCount = this.cols + 1 - colSpan;
33304         // for each group potential horizontal position
33305         for ( var i = 0; i < groupCount; i++ ) {
33306           // make an array of colY values for that one group
33307           var groupColYs = this.colYs.slice( i, i + colSpan );
33308           // and get the max value of the array
33309           colGroup[i] = Math.max.apply( Math, groupColYs );
33310         }
33311         return colGroup;
33312     },
33313     /*
33314     _manageStamp : function( stamp )
33315     {
33316         var stampSize =  stamp.getSize();
33317         var offset = stamp.getBox();
33318         // get the columns that this stamp affects
33319         var firstX = this.isOriginLeft ? offset.x : offset.right;
33320         var lastX = firstX + stampSize.width;
33321         var firstCol = Math.floor( firstX / this.columnWidth );
33322         firstCol = Math.max( 0, firstCol );
33323         
33324         var lastCol = Math.floor( lastX / this.columnWidth );
33325         // lastCol should not go over if multiple of columnWidth #425
33326         lastCol -= lastX % this.columnWidth ? 0 : 1;
33327         lastCol = Math.min( this.cols - 1, lastCol );
33328         
33329         // set colYs to bottom of the stamp
33330         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33331             stampSize.height;
33332             
33333         for ( var i = firstCol; i <= lastCol; i++ ) {
33334           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33335         }
33336     },
33337     */
33338     
33339     _getContainerSize : function()
33340     {
33341         this.maxY = Math.max.apply( Math, this.colYs );
33342         var size = {
33343             height: this.maxY
33344         };
33345       
33346         if ( this.isFitWidth ) {
33347             size.width = this._getContainerFitWidth();
33348         }
33349       
33350         return size;
33351     },
33352     
33353     _getContainerFitWidth : function()
33354     {
33355         var unusedCols = 0;
33356         // count unused columns
33357         var i = this.cols;
33358         while ( --i ) {
33359           if ( this.colYs[i] !== 0 ) {
33360             break;
33361           }
33362           unusedCols++;
33363         }
33364         // fit container to columns that have been used
33365         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33366     },
33367     
33368     needsResizeLayout : function()
33369     {
33370         var previousWidth = this.containerWidth;
33371         this.getContainerWidth();
33372         return previousWidth !== this.containerWidth;
33373     }
33374  
33375 });
33376
33377  
33378
33379  /*
33380  * - LGPL
33381  *
33382  * element
33383  * 
33384  */
33385
33386 /**
33387  * @class Roo.bootstrap.MasonryBrick
33388  * @extends Roo.bootstrap.Component
33389  * Bootstrap MasonryBrick class
33390  * 
33391  * @constructor
33392  * Create a new MasonryBrick
33393  * @param {Object} config The config object
33394  */
33395
33396 Roo.bootstrap.MasonryBrick = function(config){
33397     
33398     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33399     
33400     Roo.bootstrap.MasonryBrick.register(this);
33401     
33402     this.addEvents({
33403         // raw events
33404         /**
33405          * @event click
33406          * When a MasonryBrick is clcik
33407          * @param {Roo.bootstrap.MasonryBrick} this
33408          * @param {Roo.EventObject} e
33409          */
33410         "click" : true
33411     });
33412 };
33413
33414 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33415     
33416     /**
33417      * @cfg {String} title
33418      */   
33419     title : '',
33420     /**
33421      * @cfg {String} html
33422      */   
33423     html : '',
33424     /**
33425      * @cfg {String} bgimage
33426      */   
33427     bgimage : '',
33428     /**
33429      * @cfg {String} videourl
33430      */   
33431     videourl : '',
33432     /**
33433      * @cfg {String} cls
33434      */   
33435     cls : '',
33436     /**
33437      * @cfg {String} href
33438      */   
33439     href : '',
33440     /**
33441      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33442      */   
33443     size : 'xs',
33444     
33445     /**
33446      * @cfg {String} placetitle (center|bottom)
33447      */   
33448     placetitle : '',
33449     
33450     /**
33451      * @cfg {Boolean} isFitContainer defalut true
33452      */   
33453     isFitContainer : true, 
33454     
33455     /**
33456      * @cfg {Boolean} preventDefault defalut false
33457      */   
33458     preventDefault : false, 
33459     
33460     /**
33461      * @cfg {Boolean} inverse defalut false
33462      */   
33463     maskInverse : false, 
33464     
33465     getAutoCreate : function()
33466     {
33467         if(!this.isFitContainer){
33468             return this.getSplitAutoCreate();
33469         }
33470         
33471         var cls = 'masonry-brick masonry-brick-full';
33472         
33473         if(this.href.length){
33474             cls += ' masonry-brick-link';
33475         }
33476         
33477         if(this.bgimage.length){
33478             cls += ' masonry-brick-image';
33479         }
33480         
33481         if(this.maskInverse){
33482             cls += ' mask-inverse';
33483         }
33484         
33485         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33486             cls += ' enable-mask';
33487         }
33488         
33489         if(this.size){
33490             cls += ' masonry-' + this.size + '-brick';
33491         }
33492         
33493         if(this.placetitle.length){
33494             
33495             switch (this.placetitle) {
33496                 case 'center' :
33497                     cls += ' masonry-center-title';
33498                     break;
33499                 case 'bottom' :
33500                     cls += ' masonry-bottom-title';
33501                     break;
33502                 default:
33503                     break;
33504             }
33505             
33506         } else {
33507             if(!this.html.length && !this.bgimage.length){
33508                 cls += ' masonry-center-title';
33509             }
33510
33511             if(!this.html.length && this.bgimage.length){
33512                 cls += ' masonry-bottom-title';
33513             }
33514         }
33515         
33516         if(this.cls){
33517             cls += ' ' + this.cls;
33518         }
33519         
33520         var cfg = {
33521             tag: (this.href.length) ? 'a' : 'div',
33522             cls: cls,
33523             cn: [
33524                 {
33525                     tag: 'div',
33526                     cls: 'masonry-brick-mask'
33527                 },
33528                 {
33529                     tag: 'div',
33530                     cls: 'masonry-brick-paragraph',
33531                     cn: []
33532                 }
33533             ]
33534         };
33535         
33536         if(this.href.length){
33537             cfg.href = this.href;
33538         }
33539         
33540         var cn = cfg.cn[1].cn;
33541         
33542         if(this.title.length){
33543             cn.push({
33544                 tag: 'h4',
33545                 cls: 'masonry-brick-title',
33546                 html: this.title
33547             });
33548         }
33549         
33550         if(this.html.length){
33551             cn.push({
33552                 tag: 'p',
33553                 cls: 'masonry-brick-text',
33554                 html: this.html
33555             });
33556         }
33557         
33558         if (!this.title.length && !this.html.length) {
33559             cfg.cn[1].cls += ' hide';
33560         }
33561         
33562         if(this.bgimage.length){
33563             cfg.cn.push({
33564                 tag: 'img',
33565                 cls: 'masonry-brick-image-view',
33566                 src: this.bgimage
33567             });
33568         }
33569         
33570         if(this.videourl.length){
33571             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33572             // youtube support only?
33573             cfg.cn.push({
33574                 tag: 'iframe',
33575                 cls: 'masonry-brick-image-view',
33576                 src: vurl,
33577                 frameborder : 0,
33578                 allowfullscreen : true
33579             });
33580         }
33581         
33582         return cfg;
33583         
33584     },
33585     
33586     getSplitAutoCreate : function()
33587     {
33588         var cls = 'masonry-brick masonry-brick-split';
33589         
33590         if(this.href.length){
33591             cls += ' masonry-brick-link';
33592         }
33593         
33594         if(this.bgimage.length){
33595             cls += ' masonry-brick-image';
33596         }
33597         
33598         if(this.size){
33599             cls += ' masonry-' + this.size + '-brick';
33600         }
33601         
33602         switch (this.placetitle) {
33603             case 'center' :
33604                 cls += ' masonry-center-title';
33605                 break;
33606             case 'bottom' :
33607                 cls += ' masonry-bottom-title';
33608                 break;
33609             default:
33610                 if(!this.bgimage.length){
33611                     cls += ' masonry-center-title';
33612                 }
33613
33614                 if(this.bgimage.length){
33615                     cls += ' masonry-bottom-title';
33616                 }
33617                 break;
33618         }
33619         
33620         if(this.cls){
33621             cls += ' ' + this.cls;
33622         }
33623         
33624         var cfg = {
33625             tag: (this.href.length) ? 'a' : 'div',
33626             cls: cls,
33627             cn: [
33628                 {
33629                     tag: 'div',
33630                     cls: 'masonry-brick-split-head',
33631                     cn: [
33632                         {
33633                             tag: 'div',
33634                             cls: 'masonry-brick-paragraph',
33635                             cn: []
33636                         }
33637                     ]
33638                 },
33639                 {
33640                     tag: 'div',
33641                     cls: 'masonry-brick-split-body',
33642                     cn: []
33643                 }
33644             ]
33645         };
33646         
33647         if(this.href.length){
33648             cfg.href = this.href;
33649         }
33650         
33651         if(this.title.length){
33652             cfg.cn[0].cn[0].cn.push({
33653                 tag: 'h4',
33654                 cls: 'masonry-brick-title',
33655                 html: this.title
33656             });
33657         }
33658         
33659         if(this.html.length){
33660             cfg.cn[1].cn.push({
33661                 tag: 'p',
33662                 cls: 'masonry-brick-text',
33663                 html: this.html
33664             });
33665         }
33666
33667         if(this.bgimage.length){
33668             cfg.cn[0].cn.push({
33669                 tag: 'img',
33670                 cls: 'masonry-brick-image-view',
33671                 src: this.bgimage
33672             });
33673         }
33674         
33675         if(this.videourl.length){
33676             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33677             // youtube support only?
33678             cfg.cn[0].cn.cn.push({
33679                 tag: 'iframe',
33680                 cls: 'masonry-brick-image-view',
33681                 src: vurl,
33682                 frameborder : 0,
33683                 allowfullscreen : true
33684             });
33685         }
33686         
33687         return cfg;
33688     },
33689     
33690     initEvents: function() 
33691     {
33692         switch (this.size) {
33693             case 'xs' :
33694                 this.x = 1;
33695                 this.y = 1;
33696                 break;
33697             case 'sm' :
33698                 this.x = 2;
33699                 this.y = 2;
33700                 break;
33701             case 'md' :
33702             case 'md-left' :
33703             case 'md-right' :
33704                 this.x = 3;
33705                 this.y = 3;
33706                 break;
33707             case 'tall' :
33708                 this.x = 2;
33709                 this.y = 3;
33710                 break;
33711             case 'wide' :
33712                 this.x = 3;
33713                 this.y = 2;
33714                 break;
33715             case 'wide-thin' :
33716                 this.x = 3;
33717                 this.y = 1;
33718                 break;
33719                         
33720             default :
33721                 break;
33722         }
33723         
33724         if(Roo.isTouch){
33725             this.el.on('touchstart', this.onTouchStart, this);
33726             this.el.on('touchmove', this.onTouchMove, this);
33727             this.el.on('touchend', this.onTouchEnd, this);
33728             this.el.on('contextmenu', this.onContextMenu, this);
33729         } else {
33730             this.el.on('mouseenter'  ,this.enter, this);
33731             this.el.on('mouseleave', this.leave, this);
33732             this.el.on('click', this.onClick, this);
33733         }
33734         
33735         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33736             this.parent().bricks.push(this);   
33737         }
33738         
33739     },
33740     
33741     onClick: function(e, el)
33742     {
33743         var time = this.endTimer - this.startTimer;
33744         // Roo.log(e.preventDefault());
33745         if(Roo.isTouch){
33746             if(time > 1000){
33747                 e.preventDefault();
33748                 return;
33749             }
33750         }
33751         
33752         if(!this.preventDefault){
33753             return;
33754         }
33755         
33756         e.preventDefault();
33757         
33758         if (this.activeClass != '') {
33759             this.selectBrick();
33760         }
33761         
33762         this.fireEvent('click', this, e);
33763     },
33764     
33765     enter: function(e, el)
33766     {
33767         e.preventDefault();
33768         
33769         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33770             return;
33771         }
33772         
33773         if(this.bgimage.length && this.html.length){
33774             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33775         }
33776     },
33777     
33778     leave: function(e, el)
33779     {
33780         e.preventDefault();
33781         
33782         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33783             return;
33784         }
33785         
33786         if(this.bgimage.length && this.html.length){
33787             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33788         }
33789     },
33790     
33791     onTouchStart: function(e, el)
33792     {
33793 //        e.preventDefault();
33794         
33795         this.touchmoved = false;
33796         
33797         if(!this.isFitContainer){
33798             return;
33799         }
33800         
33801         if(!this.bgimage.length || !this.html.length){
33802             return;
33803         }
33804         
33805         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33806         
33807         this.timer = new Date().getTime();
33808         
33809     },
33810     
33811     onTouchMove: function(e, el)
33812     {
33813         this.touchmoved = true;
33814     },
33815     
33816     onContextMenu : function(e,el)
33817     {
33818         e.preventDefault();
33819         e.stopPropagation();
33820         return false;
33821     },
33822     
33823     onTouchEnd: function(e, el)
33824     {
33825 //        e.preventDefault();
33826         
33827         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33828         
33829             this.leave(e,el);
33830             
33831             return;
33832         }
33833         
33834         if(!this.bgimage.length || !this.html.length){
33835             
33836             if(this.href.length){
33837                 window.location.href = this.href;
33838             }
33839             
33840             return;
33841         }
33842         
33843         if(!this.isFitContainer){
33844             return;
33845         }
33846         
33847         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33848         
33849         window.location.href = this.href;
33850     },
33851     
33852     //selection on single brick only
33853     selectBrick : function() {
33854         
33855         if (!this.parentId) {
33856             return;
33857         }
33858         
33859         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33860         var index = m.selectedBrick.indexOf(this.id);
33861         
33862         if ( index > -1) {
33863             m.selectedBrick.splice(index,1);
33864             this.el.removeClass(this.activeClass);
33865             return;
33866         }
33867         
33868         for(var i = 0; i < m.selectedBrick.length; i++) {
33869             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33870             b.el.removeClass(b.activeClass);
33871         }
33872         
33873         m.selectedBrick = [];
33874         
33875         m.selectedBrick.push(this.id);
33876         this.el.addClass(this.activeClass);
33877         return;
33878     },
33879     
33880     isSelected : function(){
33881         return this.el.hasClass(this.activeClass);
33882         
33883     }
33884 });
33885
33886 Roo.apply(Roo.bootstrap.MasonryBrick, {
33887     
33888     //groups: {},
33889     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33890      /**
33891     * register a Masonry Brick
33892     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33893     */
33894     
33895     register : function(brick)
33896     {
33897         //this.groups[brick.id] = brick;
33898         this.groups.add(brick.id, brick);
33899     },
33900     /**
33901     * fetch a  masonry brick based on the masonry brick ID
33902     * @param {string} the masonry brick to add
33903     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33904     */
33905     
33906     get: function(brick_id) 
33907     {
33908         // if (typeof(this.groups[brick_id]) == 'undefined') {
33909         //     return false;
33910         // }
33911         // return this.groups[brick_id] ;
33912         
33913         if(this.groups.key(brick_id)) {
33914             return this.groups.key(brick_id);
33915         }
33916         
33917         return false;
33918     }
33919     
33920     
33921     
33922 });
33923
33924  /*
33925  * - LGPL
33926  *
33927  * element
33928  * 
33929  */
33930
33931 /**
33932  * @class Roo.bootstrap.Brick
33933  * @extends Roo.bootstrap.Component
33934  * Bootstrap Brick class
33935  * 
33936  * @constructor
33937  * Create a new Brick
33938  * @param {Object} config The config object
33939  */
33940
33941 Roo.bootstrap.Brick = function(config){
33942     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33943     
33944     this.addEvents({
33945         // raw events
33946         /**
33947          * @event click
33948          * When a Brick is click
33949          * @param {Roo.bootstrap.Brick} this
33950          * @param {Roo.EventObject} e
33951          */
33952         "click" : true
33953     });
33954 };
33955
33956 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33957     
33958     /**
33959      * @cfg {String} title
33960      */   
33961     title : '',
33962     /**
33963      * @cfg {String} html
33964      */   
33965     html : '',
33966     /**
33967      * @cfg {String} bgimage
33968      */   
33969     bgimage : '',
33970     /**
33971      * @cfg {String} cls
33972      */   
33973     cls : '',
33974     /**
33975      * @cfg {String} href
33976      */   
33977     href : '',
33978     /**
33979      * @cfg {String} video
33980      */   
33981     video : '',
33982     /**
33983      * @cfg {Boolean} square
33984      */   
33985     square : true,
33986     
33987     getAutoCreate : function()
33988     {
33989         var cls = 'roo-brick';
33990         
33991         if(this.href.length){
33992             cls += ' roo-brick-link';
33993         }
33994         
33995         if(this.bgimage.length){
33996             cls += ' roo-brick-image';
33997         }
33998         
33999         if(!this.html.length && !this.bgimage.length){
34000             cls += ' roo-brick-center-title';
34001         }
34002         
34003         if(!this.html.length && this.bgimage.length){
34004             cls += ' roo-brick-bottom-title';
34005         }
34006         
34007         if(this.cls){
34008             cls += ' ' + this.cls;
34009         }
34010         
34011         var cfg = {
34012             tag: (this.href.length) ? 'a' : 'div',
34013             cls: cls,
34014             cn: [
34015                 {
34016                     tag: 'div',
34017                     cls: 'roo-brick-paragraph',
34018                     cn: []
34019                 }
34020             ]
34021         };
34022         
34023         if(this.href.length){
34024             cfg.href = this.href;
34025         }
34026         
34027         var cn = cfg.cn[0].cn;
34028         
34029         if(this.title.length){
34030             cn.push({
34031                 tag: 'h4',
34032                 cls: 'roo-brick-title',
34033                 html: this.title
34034             });
34035         }
34036         
34037         if(this.html.length){
34038             cn.push({
34039                 tag: 'p',
34040                 cls: 'roo-brick-text',
34041                 html: this.html
34042             });
34043         } else {
34044             cn.cls += ' hide';
34045         }
34046         
34047         if(this.bgimage.length){
34048             cfg.cn.push({
34049                 tag: 'img',
34050                 cls: 'roo-brick-image-view',
34051                 src: this.bgimage
34052             });
34053         }
34054         
34055         return cfg;
34056     },
34057     
34058     initEvents: function() 
34059     {
34060         if(this.title.length || this.html.length){
34061             this.el.on('mouseenter'  ,this.enter, this);
34062             this.el.on('mouseleave', this.leave, this);
34063         }
34064         
34065         Roo.EventManager.onWindowResize(this.resize, this); 
34066         
34067         if(this.bgimage.length){
34068             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34069             this.imageEl.on('load', this.onImageLoad, this);
34070             return;
34071         }
34072         
34073         this.resize();
34074     },
34075     
34076     onImageLoad : function()
34077     {
34078         this.resize();
34079     },
34080     
34081     resize : function()
34082     {
34083         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34084         
34085         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34086         
34087         if(this.bgimage.length){
34088             var image = this.el.select('.roo-brick-image-view', true).first();
34089             
34090             image.setWidth(paragraph.getWidth());
34091             
34092             if(this.square){
34093                 image.setHeight(paragraph.getWidth());
34094             }
34095             
34096             this.el.setHeight(image.getHeight());
34097             paragraph.setHeight(image.getHeight());
34098             
34099         }
34100         
34101     },
34102     
34103     enter: function(e, el)
34104     {
34105         e.preventDefault();
34106         
34107         if(this.bgimage.length){
34108             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34109             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34110         }
34111     },
34112     
34113     leave: function(e, el)
34114     {
34115         e.preventDefault();
34116         
34117         if(this.bgimage.length){
34118             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34119             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34120         }
34121     }
34122     
34123 });
34124
34125  
34126
34127  /*
34128  * - LGPL
34129  *
34130  * Number field 
34131  */
34132
34133 /**
34134  * @class Roo.bootstrap.NumberField
34135  * @extends Roo.bootstrap.Input
34136  * Bootstrap NumberField class
34137  * 
34138  * 
34139  * 
34140  * 
34141  * @constructor
34142  * Create a new NumberField
34143  * @param {Object} config The config object
34144  */
34145
34146 Roo.bootstrap.NumberField = function(config){
34147     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34148 };
34149
34150 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34151     
34152     /**
34153      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34154      */
34155     allowDecimals : true,
34156     /**
34157      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34158      */
34159     decimalSeparator : ".",
34160     /**
34161      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34162      */
34163     decimalPrecision : 2,
34164     /**
34165      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34166      */
34167     allowNegative : true,
34168     
34169     /**
34170      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34171      */
34172     allowZero: true,
34173     /**
34174      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34175      */
34176     minValue : Number.NEGATIVE_INFINITY,
34177     /**
34178      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34179      */
34180     maxValue : Number.MAX_VALUE,
34181     /**
34182      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34183      */
34184     minText : "The minimum value for this field is {0}",
34185     /**
34186      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34187      */
34188     maxText : "The maximum value for this field is {0}",
34189     /**
34190      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34191      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34192      */
34193     nanText : "{0} is not a valid number",
34194     /**
34195      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34196      */
34197     thousandsDelimiter : false,
34198     /**
34199      * @cfg {String} valueAlign alignment of value
34200      */
34201     valueAlign : "left",
34202
34203     getAutoCreate : function()
34204     {
34205         var hiddenInput = {
34206             tag: 'input',
34207             type: 'hidden',
34208             id: Roo.id(),
34209             cls: 'hidden-number-input'
34210         };
34211         
34212         if (this.name) {
34213             hiddenInput.name = this.name;
34214         }
34215         
34216         this.name = '';
34217         
34218         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34219         
34220         this.name = hiddenInput.name;
34221         
34222         if(cfg.cn.length > 0) {
34223             cfg.cn.push(hiddenInput);
34224         }
34225         
34226         return cfg;
34227     },
34228
34229     // private
34230     initEvents : function()
34231     {   
34232         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34233         
34234         var allowed = "0123456789";
34235         
34236         if(this.allowDecimals){
34237             allowed += this.decimalSeparator;
34238         }
34239         
34240         if(this.allowNegative){
34241             allowed += "-";
34242         }
34243         
34244         if(this.thousandsDelimiter) {
34245             allowed += ",";
34246         }
34247         
34248         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34249         
34250         var keyPress = function(e){
34251             
34252             var k = e.getKey();
34253             
34254             var c = e.getCharCode();
34255             
34256             if(
34257                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34258                     allowed.indexOf(String.fromCharCode(c)) === -1
34259             ){
34260                 e.stopEvent();
34261                 return;
34262             }
34263             
34264             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34265                 return;
34266             }
34267             
34268             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34269                 e.stopEvent();
34270             }
34271         };
34272         
34273         this.el.on("keypress", keyPress, this);
34274     },
34275     
34276     validateValue : function(value)
34277     {
34278         
34279         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34280             return false;
34281         }
34282         
34283         var num = this.parseValue(value);
34284         
34285         if(isNaN(num)){
34286             this.markInvalid(String.format(this.nanText, value));
34287             return false;
34288         }
34289         
34290         if(num < this.minValue){
34291             this.markInvalid(String.format(this.minText, this.minValue));
34292             return false;
34293         }
34294         
34295         if(num > this.maxValue){
34296             this.markInvalid(String.format(this.maxText, this.maxValue));
34297             return false;
34298         }
34299         
34300         return true;
34301     },
34302
34303     getValue : function()
34304     {
34305         var v = this.hiddenEl().getValue();
34306         
34307         return this.fixPrecision(this.parseValue(v));
34308     },
34309
34310     parseValue : function(value)
34311     {
34312         if(this.thousandsDelimiter) {
34313             value += "";
34314             r = new RegExp(",", "g");
34315             value = value.replace(r, "");
34316         }
34317         
34318         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34319         return isNaN(value) ? '' : value;
34320     },
34321
34322     fixPrecision : function(value)
34323     {
34324         if(this.thousandsDelimiter) {
34325             value += "";
34326             r = new RegExp(",", "g");
34327             value = value.replace(r, "");
34328         }
34329         
34330         var nan = isNaN(value);
34331         
34332         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34333             return nan ? '' : value;
34334         }
34335         return parseFloat(value).toFixed(this.decimalPrecision);
34336     },
34337
34338     setValue : function(v)
34339     {
34340         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34341         
34342         this.value = v;
34343         
34344         if(this.rendered){
34345             
34346             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34347             
34348             this.inputEl().dom.value = (v == '') ? '' :
34349                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34350             
34351             if(!this.allowZero && v === '0') {
34352                 this.hiddenEl().dom.value = '';
34353                 this.inputEl().dom.value = '';
34354             }
34355             
34356             this.validate();
34357         }
34358     },
34359
34360     decimalPrecisionFcn : function(v)
34361     {
34362         return Math.floor(v);
34363     },
34364
34365     beforeBlur : function()
34366     {
34367         var v = this.parseValue(this.getRawValue());
34368         
34369         if(v || v === 0 || v === ''){
34370             this.setValue(v);
34371         }
34372     },
34373     
34374     hiddenEl : function()
34375     {
34376         return this.el.select('input.hidden-number-input',true).first();
34377     }
34378     
34379 });
34380
34381  
34382
34383 /*
34384 * Licence: LGPL
34385 */
34386
34387 /**
34388  * @class Roo.bootstrap.DocumentSlider
34389  * @extends Roo.bootstrap.Component
34390  * Bootstrap DocumentSlider class
34391  * 
34392  * @constructor
34393  * Create a new DocumentViewer
34394  * @param {Object} config The config object
34395  */
34396
34397 Roo.bootstrap.DocumentSlider = function(config){
34398     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34399     
34400     this.files = [];
34401     
34402     this.addEvents({
34403         /**
34404          * @event initial
34405          * Fire after initEvent
34406          * @param {Roo.bootstrap.DocumentSlider} this
34407          */
34408         "initial" : true,
34409         /**
34410          * @event update
34411          * Fire after update
34412          * @param {Roo.bootstrap.DocumentSlider} this
34413          */
34414         "update" : true,
34415         /**
34416          * @event click
34417          * Fire after click
34418          * @param {Roo.bootstrap.DocumentSlider} this
34419          */
34420         "click" : true
34421     });
34422 };
34423
34424 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34425     
34426     files : false,
34427     
34428     indicator : 0,
34429     
34430     getAutoCreate : function()
34431     {
34432         var cfg = {
34433             tag : 'div',
34434             cls : 'roo-document-slider',
34435             cn : [
34436                 {
34437                     tag : 'div',
34438                     cls : 'roo-document-slider-header',
34439                     cn : [
34440                         {
34441                             tag : 'div',
34442                             cls : 'roo-document-slider-header-title'
34443                         }
34444                     ]
34445                 },
34446                 {
34447                     tag : 'div',
34448                     cls : 'roo-document-slider-body',
34449                     cn : [
34450                         {
34451                             tag : 'div',
34452                             cls : 'roo-document-slider-prev',
34453                             cn : [
34454                                 {
34455                                     tag : 'i',
34456                                     cls : 'fa fa-chevron-left'
34457                                 }
34458                             ]
34459                         },
34460                         {
34461                             tag : 'div',
34462                             cls : 'roo-document-slider-thumb',
34463                             cn : [
34464                                 {
34465                                     tag : 'img',
34466                                     cls : 'roo-document-slider-image'
34467                                 }
34468                             ]
34469                         },
34470                         {
34471                             tag : 'div',
34472                             cls : 'roo-document-slider-next',
34473                             cn : [
34474                                 {
34475                                     tag : 'i',
34476                                     cls : 'fa fa-chevron-right'
34477                                 }
34478                             ]
34479                         }
34480                     ]
34481                 }
34482             ]
34483         };
34484         
34485         return cfg;
34486     },
34487     
34488     initEvents : function()
34489     {
34490         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34491         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34492         
34493         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34494         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34495         
34496         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34497         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34498         
34499         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34500         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34501         
34502         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34503         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34504         
34505         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34506         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34507         
34508         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34509         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34510         
34511         this.thumbEl.on('click', this.onClick, this);
34512         
34513         this.prevIndicator.on('click', this.prev, this);
34514         
34515         this.nextIndicator.on('click', this.next, this);
34516         
34517     },
34518     
34519     initial : function()
34520     {
34521         if(this.files.length){
34522             this.indicator = 1;
34523             this.update()
34524         }
34525         
34526         this.fireEvent('initial', this);
34527     },
34528     
34529     update : function()
34530     {
34531         this.imageEl.attr('src', this.files[this.indicator - 1]);
34532         
34533         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34534         
34535         this.prevIndicator.show();
34536         
34537         if(this.indicator == 1){
34538             this.prevIndicator.hide();
34539         }
34540         
34541         this.nextIndicator.show();
34542         
34543         if(this.indicator == this.files.length){
34544             this.nextIndicator.hide();
34545         }
34546         
34547         this.thumbEl.scrollTo('top');
34548         
34549         this.fireEvent('update', this);
34550     },
34551     
34552     onClick : function(e)
34553     {
34554         e.preventDefault();
34555         
34556         this.fireEvent('click', this);
34557     },
34558     
34559     prev : function(e)
34560     {
34561         e.preventDefault();
34562         
34563         this.indicator = Math.max(1, this.indicator - 1);
34564         
34565         this.update();
34566     },
34567     
34568     next : function(e)
34569     {
34570         e.preventDefault();
34571         
34572         this.indicator = Math.min(this.files.length, this.indicator + 1);
34573         
34574         this.update();
34575     }
34576 });
34577 /*
34578  * - LGPL
34579  *
34580  * RadioSet
34581  *
34582  *
34583  */
34584
34585 /**
34586  * @class Roo.bootstrap.RadioSet
34587  * @extends Roo.bootstrap.Input
34588  * Bootstrap RadioSet class
34589  * @cfg {String} indicatorpos (left|right) default left
34590  * @cfg {Boolean} inline (true|false) inline the element (default true)
34591  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34592  * @constructor
34593  * Create a new RadioSet
34594  * @param {Object} config The config object
34595  */
34596
34597 Roo.bootstrap.RadioSet = function(config){
34598     
34599     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34600     
34601     this.radioes = [];
34602     
34603     Roo.bootstrap.RadioSet.register(this);
34604     
34605     this.addEvents({
34606         /**
34607         * @event check
34608         * Fires when the element is checked or unchecked.
34609         * @param {Roo.bootstrap.RadioSet} this This radio
34610         * @param {Roo.bootstrap.Radio} item The checked item
34611         */
34612        check : true,
34613        /**
34614         * @event click
34615         * Fires when the element is click.
34616         * @param {Roo.bootstrap.RadioSet} this This radio set
34617         * @param {Roo.bootstrap.Radio} item The checked item
34618         * @param {Roo.EventObject} e The event object
34619         */
34620        click : true
34621     });
34622     
34623 };
34624
34625 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34626
34627     radioes : false,
34628     
34629     inline : true,
34630     
34631     weight : '',
34632     
34633     indicatorpos : 'left',
34634     
34635     getAutoCreate : function()
34636     {
34637         var label = {
34638             tag : 'label',
34639             cls : 'roo-radio-set-label',
34640             cn : [
34641                 {
34642                     tag : 'span',
34643                     html : this.fieldLabel
34644                 }
34645             ]
34646         };
34647         if (Roo.bootstrap.version == 3) {
34648             
34649             
34650             if(this.indicatorpos == 'left'){
34651                 label.cn.unshift({
34652                     tag : 'i',
34653                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34654                     tooltip : 'This field is required'
34655                 });
34656             } else {
34657                 label.cn.push({
34658                     tag : 'i',
34659                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34660                     tooltip : 'This field is required'
34661                 });
34662             }
34663         }
34664         var items = {
34665             tag : 'div',
34666             cls : 'roo-radio-set-items'
34667         };
34668         
34669         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34670         
34671         if (align === 'left' && this.fieldLabel.length) {
34672             
34673             items = {
34674                 cls : "roo-radio-set-right", 
34675                 cn: [
34676                     items
34677                 ]
34678             };
34679             
34680             if(this.labelWidth > 12){
34681                 label.style = "width: " + this.labelWidth + 'px';
34682             }
34683             
34684             if(this.labelWidth < 13 && this.labelmd == 0){
34685                 this.labelmd = this.labelWidth;
34686             }
34687             
34688             if(this.labellg > 0){
34689                 label.cls += ' col-lg-' + this.labellg;
34690                 items.cls += ' col-lg-' + (12 - this.labellg);
34691             }
34692             
34693             if(this.labelmd > 0){
34694                 label.cls += ' col-md-' + this.labelmd;
34695                 items.cls += ' col-md-' + (12 - this.labelmd);
34696             }
34697             
34698             if(this.labelsm > 0){
34699                 label.cls += ' col-sm-' + this.labelsm;
34700                 items.cls += ' col-sm-' + (12 - this.labelsm);
34701             }
34702             
34703             if(this.labelxs > 0){
34704                 label.cls += ' col-xs-' + this.labelxs;
34705                 items.cls += ' col-xs-' + (12 - this.labelxs);
34706             }
34707         }
34708         
34709         var cfg = {
34710             tag : 'div',
34711             cls : 'roo-radio-set',
34712             cn : [
34713                 {
34714                     tag : 'input',
34715                     cls : 'roo-radio-set-input',
34716                     type : 'hidden',
34717                     name : this.name,
34718                     value : this.value ? this.value :  ''
34719                 },
34720                 label,
34721                 items
34722             ]
34723         };
34724         
34725         if(this.weight.length){
34726             cfg.cls += ' roo-radio-' + this.weight;
34727         }
34728         
34729         if(this.inline) {
34730             cfg.cls += ' roo-radio-set-inline';
34731         }
34732         
34733         var settings=this;
34734         ['xs','sm','md','lg'].map(function(size){
34735             if (settings[size]) {
34736                 cfg.cls += ' col-' + size + '-' + settings[size];
34737             }
34738         });
34739         
34740         return cfg;
34741         
34742     },
34743
34744     initEvents : function()
34745     {
34746         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34747         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34748         
34749         if(!this.fieldLabel.length){
34750             this.labelEl.hide();
34751         }
34752         
34753         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34754         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34755         
34756         this.indicator = this.indicatorEl();
34757         
34758         if(this.indicator){
34759             this.indicator.addClass('invisible');
34760         }
34761         
34762         this.originalValue = this.getValue();
34763         
34764     },
34765     
34766     inputEl: function ()
34767     {
34768         return this.el.select('.roo-radio-set-input', true).first();
34769     },
34770     
34771     getChildContainer : function()
34772     {
34773         return this.itemsEl;
34774     },
34775     
34776     register : function(item)
34777     {
34778         this.radioes.push(item);
34779         
34780     },
34781     
34782     validate : function()
34783     {   
34784         if(this.getVisibilityEl().hasClass('hidden')){
34785             return true;
34786         }
34787         
34788         var valid = false;
34789         
34790         Roo.each(this.radioes, function(i){
34791             if(!i.checked){
34792                 return;
34793             }
34794             
34795             valid = true;
34796             return false;
34797         });
34798         
34799         if(this.allowBlank) {
34800             return true;
34801         }
34802         
34803         if(this.disabled || valid){
34804             this.markValid();
34805             return true;
34806         }
34807         
34808         this.markInvalid();
34809         return false;
34810         
34811     },
34812     
34813     markValid : function()
34814     {
34815         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34816             this.indicatorEl().removeClass('visible');
34817             this.indicatorEl().addClass('invisible');
34818         }
34819         
34820         
34821         if (Roo.bootstrap.version == 3) {
34822             this.el.removeClass([this.invalidClass, this.validClass]);
34823             this.el.addClass(this.validClass);
34824         } else {
34825             this.el.removeClass(['is-invalid','is-valid']);
34826             this.el.addClass(['is-valid']);
34827         }
34828         this.fireEvent('valid', this);
34829     },
34830     
34831     markInvalid : function(msg)
34832     {
34833         if(this.allowBlank || this.disabled){
34834             return;
34835         }
34836         
34837         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34838             this.indicatorEl().removeClass('invisible');
34839             this.indicatorEl().addClass('visible');
34840         }
34841         if (Roo.bootstrap.version == 3) {
34842             this.el.removeClass([this.invalidClass, this.validClass]);
34843             this.el.addClass(this.invalidClass);
34844         } else {
34845             this.el.removeClass(['is-invalid','is-valid']);
34846             this.el.addClass(['is-invalid']);
34847         }
34848         
34849         this.fireEvent('invalid', this, msg);
34850         
34851     },
34852     
34853     setValue : function(v, suppressEvent)
34854     {   
34855         if(this.value === v){
34856             return;
34857         }
34858         
34859         this.value = v;
34860         
34861         if(this.rendered){
34862             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34863         }
34864         
34865         Roo.each(this.radioes, function(i){
34866             i.checked = false;
34867             i.el.removeClass('checked');
34868         });
34869         
34870         Roo.each(this.radioes, function(i){
34871             
34872             if(i.value === v || i.value.toString() === v.toString()){
34873                 i.checked = true;
34874                 i.el.addClass('checked');
34875                 
34876                 if(suppressEvent !== true){
34877                     this.fireEvent('check', this, i);
34878                 }
34879                 
34880                 return false;
34881             }
34882             
34883         }, this);
34884         
34885         this.validate();
34886     },
34887     
34888     clearInvalid : function(){
34889         
34890         if(!this.el || this.preventMark){
34891             return;
34892         }
34893         
34894         this.el.removeClass([this.invalidClass]);
34895         
34896         this.fireEvent('valid', this);
34897     }
34898     
34899 });
34900
34901 Roo.apply(Roo.bootstrap.RadioSet, {
34902     
34903     groups: {},
34904     
34905     register : function(set)
34906     {
34907         this.groups[set.name] = set;
34908     },
34909     
34910     get: function(name) 
34911     {
34912         if (typeof(this.groups[name]) == 'undefined') {
34913             return false;
34914         }
34915         
34916         return this.groups[name] ;
34917     }
34918     
34919 });
34920 /*
34921  * Based on:
34922  * Ext JS Library 1.1.1
34923  * Copyright(c) 2006-2007, Ext JS, LLC.
34924  *
34925  * Originally Released Under LGPL - original licence link has changed is not relivant.
34926  *
34927  * Fork - LGPL
34928  * <script type="text/javascript">
34929  */
34930
34931
34932 /**
34933  * @class Roo.bootstrap.SplitBar
34934  * @extends Roo.util.Observable
34935  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34936  * <br><br>
34937  * Usage:
34938  * <pre><code>
34939 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34940                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34941 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34942 split.minSize = 100;
34943 split.maxSize = 600;
34944 split.animate = true;
34945 split.on('moved', splitterMoved);
34946 </code></pre>
34947  * @constructor
34948  * Create a new SplitBar
34949  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34950  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34951  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34952  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34953                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34954                         position of the SplitBar).
34955  */
34956 Roo.bootstrap.SplitBar = function(cfg){
34957     
34958     /** @private */
34959     
34960     //{
34961     //  dragElement : elm
34962     //  resizingElement: el,
34963         // optional..
34964     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34965     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34966         // existingProxy ???
34967     //}
34968     
34969     this.el = Roo.get(cfg.dragElement, true);
34970     this.el.dom.unselectable = "on";
34971     /** @private */
34972     this.resizingEl = Roo.get(cfg.resizingElement, true);
34973
34974     /**
34975      * @private
34976      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34977      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34978      * @type Number
34979      */
34980     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34981     
34982     /**
34983      * The minimum size of the resizing element. (Defaults to 0)
34984      * @type Number
34985      */
34986     this.minSize = 0;
34987     
34988     /**
34989      * The maximum size of the resizing element. (Defaults to 2000)
34990      * @type Number
34991      */
34992     this.maxSize = 2000;
34993     
34994     /**
34995      * Whether to animate the transition to the new size
34996      * @type Boolean
34997      */
34998     this.animate = false;
34999     
35000     /**
35001      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35002      * @type Boolean
35003      */
35004     this.useShim = false;
35005     
35006     /** @private */
35007     this.shim = null;
35008     
35009     if(!cfg.existingProxy){
35010         /** @private */
35011         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35012     }else{
35013         this.proxy = Roo.get(cfg.existingProxy).dom;
35014     }
35015     /** @private */
35016     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35017     
35018     /** @private */
35019     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35020     
35021     /** @private */
35022     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35023     
35024     /** @private */
35025     this.dragSpecs = {};
35026     
35027     /**
35028      * @private The adapter to use to positon and resize elements
35029      */
35030     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35031     this.adapter.init(this);
35032     
35033     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35034         /** @private */
35035         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35036         this.el.addClass("roo-splitbar-h");
35037     }else{
35038         /** @private */
35039         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35040         this.el.addClass("roo-splitbar-v");
35041     }
35042     
35043     this.addEvents({
35044         /**
35045          * @event resize
35046          * Fires when the splitter is moved (alias for {@link #event-moved})
35047          * @param {Roo.bootstrap.SplitBar} this
35048          * @param {Number} newSize the new width or height
35049          */
35050         "resize" : true,
35051         /**
35052          * @event moved
35053          * Fires when the splitter is moved
35054          * @param {Roo.bootstrap.SplitBar} this
35055          * @param {Number} newSize the new width or height
35056          */
35057         "moved" : true,
35058         /**
35059          * @event beforeresize
35060          * Fires before the splitter is dragged
35061          * @param {Roo.bootstrap.SplitBar} this
35062          */
35063         "beforeresize" : true,
35064
35065         "beforeapply" : true
35066     });
35067
35068     Roo.util.Observable.call(this);
35069 };
35070
35071 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35072     onStartProxyDrag : function(x, y){
35073         this.fireEvent("beforeresize", this);
35074         if(!this.overlay){
35075             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35076             o.unselectable();
35077             o.enableDisplayMode("block");
35078             // all splitbars share the same overlay
35079             Roo.bootstrap.SplitBar.prototype.overlay = o;
35080         }
35081         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35082         this.overlay.show();
35083         Roo.get(this.proxy).setDisplayed("block");
35084         var size = this.adapter.getElementSize(this);
35085         this.activeMinSize = this.getMinimumSize();;
35086         this.activeMaxSize = this.getMaximumSize();;
35087         var c1 = size - this.activeMinSize;
35088         var c2 = Math.max(this.activeMaxSize - size, 0);
35089         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35090             this.dd.resetConstraints();
35091             this.dd.setXConstraint(
35092                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35093                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35094             );
35095             this.dd.setYConstraint(0, 0);
35096         }else{
35097             this.dd.resetConstraints();
35098             this.dd.setXConstraint(0, 0);
35099             this.dd.setYConstraint(
35100                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35101                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35102             );
35103          }
35104         this.dragSpecs.startSize = size;
35105         this.dragSpecs.startPoint = [x, y];
35106         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35107     },
35108     
35109     /** 
35110      * @private Called after the drag operation by the DDProxy
35111      */
35112     onEndProxyDrag : function(e){
35113         Roo.get(this.proxy).setDisplayed(false);
35114         var endPoint = Roo.lib.Event.getXY(e);
35115         if(this.overlay){
35116             this.overlay.hide();
35117         }
35118         var newSize;
35119         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35120             newSize = this.dragSpecs.startSize + 
35121                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35122                     endPoint[0] - this.dragSpecs.startPoint[0] :
35123                     this.dragSpecs.startPoint[0] - endPoint[0]
35124                 );
35125         }else{
35126             newSize = this.dragSpecs.startSize + 
35127                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35128                     endPoint[1] - this.dragSpecs.startPoint[1] :
35129                     this.dragSpecs.startPoint[1] - endPoint[1]
35130                 );
35131         }
35132         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35133         if(newSize != this.dragSpecs.startSize){
35134             if(this.fireEvent('beforeapply', this, newSize) !== false){
35135                 this.adapter.setElementSize(this, newSize);
35136                 this.fireEvent("moved", this, newSize);
35137                 this.fireEvent("resize", this, newSize);
35138             }
35139         }
35140     },
35141     
35142     /**
35143      * Get the adapter this SplitBar uses
35144      * @return The adapter object
35145      */
35146     getAdapter : function(){
35147         return this.adapter;
35148     },
35149     
35150     /**
35151      * Set the adapter this SplitBar uses
35152      * @param {Object} adapter A SplitBar adapter object
35153      */
35154     setAdapter : function(adapter){
35155         this.adapter = adapter;
35156         this.adapter.init(this);
35157     },
35158     
35159     /**
35160      * Gets the minimum size for the resizing element
35161      * @return {Number} The minimum size
35162      */
35163     getMinimumSize : function(){
35164         return this.minSize;
35165     },
35166     
35167     /**
35168      * Sets the minimum size for the resizing element
35169      * @param {Number} minSize The minimum size
35170      */
35171     setMinimumSize : function(minSize){
35172         this.minSize = minSize;
35173     },
35174     
35175     /**
35176      * Gets the maximum size for the resizing element
35177      * @return {Number} The maximum size
35178      */
35179     getMaximumSize : function(){
35180         return this.maxSize;
35181     },
35182     
35183     /**
35184      * Sets the maximum size for the resizing element
35185      * @param {Number} maxSize The maximum size
35186      */
35187     setMaximumSize : function(maxSize){
35188         this.maxSize = maxSize;
35189     },
35190     
35191     /**
35192      * Sets the initialize size for the resizing element
35193      * @param {Number} size The initial size
35194      */
35195     setCurrentSize : function(size){
35196         var oldAnimate = this.animate;
35197         this.animate = false;
35198         this.adapter.setElementSize(this, size);
35199         this.animate = oldAnimate;
35200     },
35201     
35202     /**
35203      * Destroy this splitbar. 
35204      * @param {Boolean} removeEl True to remove the element
35205      */
35206     destroy : function(removeEl){
35207         if(this.shim){
35208             this.shim.remove();
35209         }
35210         this.dd.unreg();
35211         this.proxy.parentNode.removeChild(this.proxy);
35212         if(removeEl){
35213             this.el.remove();
35214         }
35215     }
35216 });
35217
35218 /**
35219  * @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.
35220  */
35221 Roo.bootstrap.SplitBar.createProxy = function(dir){
35222     var proxy = new Roo.Element(document.createElement("div"));
35223     proxy.unselectable();
35224     var cls = 'roo-splitbar-proxy';
35225     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35226     document.body.appendChild(proxy.dom);
35227     return proxy.dom;
35228 };
35229
35230 /** 
35231  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35232  * Default Adapter. It assumes the splitter and resizing element are not positioned
35233  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35234  */
35235 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35236 };
35237
35238 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35239     // do nothing for now
35240     init : function(s){
35241     
35242     },
35243     /**
35244      * Called before drag operations to get the current size of the resizing element. 
35245      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35246      */
35247      getElementSize : function(s){
35248         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35249             return s.resizingEl.getWidth();
35250         }else{
35251             return s.resizingEl.getHeight();
35252         }
35253     },
35254     
35255     /**
35256      * Called after drag operations to set the size of the resizing element.
35257      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35258      * @param {Number} newSize The new size to set
35259      * @param {Function} onComplete A function to be invoked when resizing is complete
35260      */
35261     setElementSize : function(s, newSize, onComplete){
35262         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35263             if(!s.animate){
35264                 s.resizingEl.setWidth(newSize);
35265                 if(onComplete){
35266                     onComplete(s, newSize);
35267                 }
35268             }else{
35269                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35270             }
35271         }else{
35272             
35273             if(!s.animate){
35274                 s.resizingEl.setHeight(newSize);
35275                 if(onComplete){
35276                     onComplete(s, newSize);
35277                 }
35278             }else{
35279                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35280             }
35281         }
35282     }
35283 };
35284
35285 /** 
35286  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35287  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35288  * Adapter that  moves the splitter element to align with the resized sizing element. 
35289  * Used with an absolute positioned SplitBar.
35290  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35291  * document.body, make sure you assign an id to the body element.
35292  */
35293 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35294     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35295     this.container = Roo.get(container);
35296 };
35297
35298 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35299     init : function(s){
35300         this.basic.init(s);
35301     },
35302     
35303     getElementSize : function(s){
35304         return this.basic.getElementSize(s);
35305     },
35306     
35307     setElementSize : function(s, newSize, onComplete){
35308         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35309     },
35310     
35311     moveSplitter : function(s){
35312         var yes = Roo.bootstrap.SplitBar;
35313         switch(s.placement){
35314             case yes.LEFT:
35315                 s.el.setX(s.resizingEl.getRight());
35316                 break;
35317             case yes.RIGHT:
35318                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35319                 break;
35320             case yes.TOP:
35321                 s.el.setY(s.resizingEl.getBottom());
35322                 break;
35323             case yes.BOTTOM:
35324                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35325                 break;
35326         }
35327     }
35328 };
35329
35330 /**
35331  * Orientation constant - Create a vertical SplitBar
35332  * @static
35333  * @type Number
35334  */
35335 Roo.bootstrap.SplitBar.VERTICAL = 1;
35336
35337 /**
35338  * Orientation constant - Create a horizontal SplitBar
35339  * @static
35340  * @type Number
35341  */
35342 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35343
35344 /**
35345  * Placement constant - The resizing element is to the left of the splitter element
35346  * @static
35347  * @type Number
35348  */
35349 Roo.bootstrap.SplitBar.LEFT = 1;
35350
35351 /**
35352  * Placement constant - The resizing element is to the right of the splitter element
35353  * @static
35354  * @type Number
35355  */
35356 Roo.bootstrap.SplitBar.RIGHT = 2;
35357
35358 /**
35359  * Placement constant - The resizing element is positioned above the splitter element
35360  * @static
35361  * @type Number
35362  */
35363 Roo.bootstrap.SplitBar.TOP = 3;
35364
35365 /**
35366  * Placement constant - The resizing element is positioned under splitter element
35367  * @static
35368  * @type Number
35369  */
35370 Roo.bootstrap.SplitBar.BOTTOM = 4;
35371 Roo.namespace("Roo.bootstrap.layout");/*
35372  * Based on:
35373  * Ext JS Library 1.1.1
35374  * Copyright(c) 2006-2007, Ext JS, LLC.
35375  *
35376  * Originally Released Under LGPL - original licence link has changed is not relivant.
35377  *
35378  * Fork - LGPL
35379  * <script type="text/javascript">
35380  */
35381
35382 /**
35383  * @class Roo.bootstrap.layout.Manager
35384  * @extends Roo.bootstrap.Component
35385  * Base class for layout managers.
35386  */
35387 Roo.bootstrap.layout.Manager = function(config)
35388 {
35389     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35390
35391
35392
35393
35394
35395     /** false to disable window resize monitoring @type Boolean */
35396     this.monitorWindowResize = true;
35397     this.regions = {};
35398     this.addEvents({
35399         /**
35400          * @event layout
35401          * Fires when a layout is performed.
35402          * @param {Roo.LayoutManager} this
35403          */
35404         "layout" : true,
35405         /**
35406          * @event regionresized
35407          * Fires when the user resizes a region.
35408          * @param {Roo.LayoutRegion} region The resized region
35409          * @param {Number} newSize The new size (width for east/west, height for north/south)
35410          */
35411         "regionresized" : true,
35412         /**
35413          * @event regioncollapsed
35414          * Fires when a region is collapsed.
35415          * @param {Roo.LayoutRegion} region The collapsed region
35416          */
35417         "regioncollapsed" : true,
35418         /**
35419          * @event regionexpanded
35420          * Fires when a region is expanded.
35421          * @param {Roo.LayoutRegion} region The expanded region
35422          */
35423         "regionexpanded" : true
35424     });
35425     this.updating = false;
35426
35427     if (config.el) {
35428         this.el = Roo.get(config.el);
35429         this.initEvents();
35430     }
35431
35432 };
35433
35434 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35435
35436
35437     regions : null,
35438
35439     monitorWindowResize : true,
35440
35441
35442     updating : false,
35443
35444
35445     onRender : function(ct, position)
35446     {
35447         if(!this.el){
35448             this.el = Roo.get(ct);
35449             this.initEvents();
35450         }
35451         //this.fireEvent('render',this);
35452     },
35453
35454
35455     initEvents: function()
35456     {
35457
35458
35459         // ie scrollbar fix
35460         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35461             document.body.scroll = "no";
35462         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35463             this.el.position('relative');
35464         }
35465         this.id = this.el.id;
35466         this.el.addClass("roo-layout-container");
35467         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35468         if(this.el.dom != document.body ) {
35469             this.el.on('resize', this.layout,this);
35470             this.el.on('show', this.layout,this);
35471         }
35472
35473     },
35474
35475     /**
35476      * Returns true if this layout is currently being updated
35477      * @return {Boolean}
35478      */
35479     isUpdating : function(){
35480         return this.updating;
35481     },
35482
35483     /**
35484      * Suspend the LayoutManager from doing auto-layouts while
35485      * making multiple add or remove calls
35486      */
35487     beginUpdate : function(){
35488         this.updating = true;
35489     },
35490
35491     /**
35492      * Restore auto-layouts and optionally disable the manager from performing a layout
35493      * @param {Boolean} noLayout true to disable a layout update
35494      */
35495     endUpdate : function(noLayout){
35496         this.updating = false;
35497         if(!noLayout){
35498             this.layout();
35499         }
35500     },
35501
35502     layout: function(){
35503         // abstract...
35504     },
35505
35506     onRegionResized : function(region, newSize){
35507         this.fireEvent("regionresized", region, newSize);
35508         this.layout();
35509     },
35510
35511     onRegionCollapsed : function(region){
35512         this.fireEvent("regioncollapsed", region);
35513     },
35514
35515     onRegionExpanded : function(region){
35516         this.fireEvent("regionexpanded", region);
35517     },
35518
35519     /**
35520      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35521      * performs box-model adjustments.
35522      * @return {Object} The size as an object {width: (the width), height: (the height)}
35523      */
35524     getViewSize : function()
35525     {
35526         var size;
35527         if(this.el.dom != document.body){
35528             size = this.el.getSize();
35529         }else{
35530             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35531         }
35532         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35533         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35534         return size;
35535     },
35536
35537     /**
35538      * Returns the Element this layout is bound to.
35539      * @return {Roo.Element}
35540      */
35541     getEl : function(){
35542         return this.el;
35543     },
35544
35545     /**
35546      * Returns the specified region.
35547      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35548      * @return {Roo.LayoutRegion}
35549      */
35550     getRegion : function(target){
35551         return this.regions[target.toLowerCase()];
35552     },
35553
35554     onWindowResize : function(){
35555         if(this.monitorWindowResize){
35556             this.layout();
35557         }
35558     }
35559 });
35560 /*
35561  * Based on:
35562  * Ext JS Library 1.1.1
35563  * Copyright(c) 2006-2007, Ext JS, LLC.
35564  *
35565  * Originally Released Under LGPL - original licence link has changed is not relivant.
35566  *
35567  * Fork - LGPL
35568  * <script type="text/javascript">
35569  */
35570 /**
35571  * @class Roo.bootstrap.layout.Border
35572  * @extends Roo.bootstrap.layout.Manager
35573  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35574  * please see: examples/bootstrap/nested.html<br><br>
35575  
35576 <b>The container the layout is rendered into can be either the body element or any other element.
35577 If it is not the body element, the container needs to either be an absolute positioned element,
35578 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35579 the container size if it is not the body element.</b>
35580
35581 * @constructor
35582 * Create a new Border
35583 * @param {Object} config Configuration options
35584  */
35585 Roo.bootstrap.layout.Border = function(config){
35586     config = config || {};
35587     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35588     
35589     
35590     
35591     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35592         if(config[region]){
35593             config[region].region = region;
35594             this.addRegion(config[region]);
35595         }
35596     },this);
35597     
35598 };
35599
35600 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35601
35602 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35603     
35604     parent : false, // this might point to a 'nest' or a ???
35605     
35606     /**
35607      * Creates and adds a new region if it doesn't already exist.
35608      * @param {String} target The target region key (north, south, east, west or center).
35609      * @param {Object} config The regions config object
35610      * @return {BorderLayoutRegion} The new region
35611      */
35612     addRegion : function(config)
35613     {
35614         if(!this.regions[config.region]){
35615             var r = this.factory(config);
35616             this.bindRegion(r);
35617         }
35618         return this.regions[config.region];
35619     },
35620
35621     // private (kinda)
35622     bindRegion : function(r){
35623         this.regions[r.config.region] = r;
35624         
35625         r.on("visibilitychange",    this.layout, this);
35626         r.on("paneladded",          this.layout, this);
35627         r.on("panelremoved",        this.layout, this);
35628         r.on("invalidated",         this.layout, this);
35629         r.on("resized",             this.onRegionResized, this);
35630         r.on("collapsed",           this.onRegionCollapsed, this);
35631         r.on("expanded",            this.onRegionExpanded, this);
35632     },
35633
35634     /**
35635      * Performs a layout update.
35636      */
35637     layout : function()
35638     {
35639         if(this.updating) {
35640             return;
35641         }
35642         
35643         // render all the rebions if they have not been done alreayd?
35644         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35645             if(this.regions[region] && !this.regions[region].bodyEl){
35646                 this.regions[region].onRender(this.el)
35647             }
35648         },this);
35649         
35650         var size = this.getViewSize();
35651         var w = size.width;
35652         var h = size.height;
35653         var centerW = w;
35654         var centerH = h;
35655         var centerY = 0;
35656         var centerX = 0;
35657         //var x = 0, y = 0;
35658
35659         var rs = this.regions;
35660         var north = rs["north"];
35661         var south = rs["south"]; 
35662         var west = rs["west"];
35663         var east = rs["east"];
35664         var center = rs["center"];
35665         //if(this.hideOnLayout){ // not supported anymore
35666             //c.el.setStyle("display", "none");
35667         //}
35668         if(north && north.isVisible()){
35669             var b = north.getBox();
35670             var m = north.getMargins();
35671             b.width = w - (m.left+m.right);
35672             b.x = m.left;
35673             b.y = m.top;
35674             centerY = b.height + b.y + m.bottom;
35675             centerH -= centerY;
35676             north.updateBox(this.safeBox(b));
35677         }
35678         if(south && south.isVisible()){
35679             var b = south.getBox();
35680             var m = south.getMargins();
35681             b.width = w - (m.left+m.right);
35682             b.x = m.left;
35683             var totalHeight = (b.height + m.top + m.bottom);
35684             b.y = h - totalHeight + m.top;
35685             centerH -= totalHeight;
35686             south.updateBox(this.safeBox(b));
35687         }
35688         if(west && west.isVisible()){
35689             var b = west.getBox();
35690             var m = west.getMargins();
35691             b.height = centerH - (m.top+m.bottom);
35692             b.x = m.left;
35693             b.y = centerY + m.top;
35694             var totalWidth = (b.width + m.left + m.right);
35695             centerX += totalWidth;
35696             centerW -= totalWidth;
35697             west.updateBox(this.safeBox(b));
35698         }
35699         if(east && east.isVisible()){
35700             var b = east.getBox();
35701             var m = east.getMargins();
35702             b.height = centerH - (m.top+m.bottom);
35703             var totalWidth = (b.width + m.left + m.right);
35704             b.x = w - totalWidth + m.left;
35705             b.y = centerY + m.top;
35706             centerW -= totalWidth;
35707             east.updateBox(this.safeBox(b));
35708         }
35709         if(center){
35710             var m = center.getMargins();
35711             var centerBox = {
35712                 x: centerX + m.left,
35713                 y: centerY + m.top,
35714                 width: centerW - (m.left+m.right),
35715                 height: centerH - (m.top+m.bottom)
35716             };
35717             //if(this.hideOnLayout){
35718                 //center.el.setStyle("display", "block");
35719             //}
35720             center.updateBox(this.safeBox(centerBox));
35721         }
35722         this.el.repaint();
35723         this.fireEvent("layout", this);
35724     },
35725
35726     // private
35727     safeBox : function(box){
35728         box.width = Math.max(0, box.width);
35729         box.height = Math.max(0, box.height);
35730         return box;
35731     },
35732
35733     /**
35734      * Adds a ContentPanel (or subclass) to this layout.
35735      * @param {String} target The target region key (north, south, east, west or center).
35736      * @param {Roo.ContentPanel} panel The panel to add
35737      * @return {Roo.ContentPanel} The added panel
35738      */
35739     add : function(target, panel){
35740          
35741         target = target.toLowerCase();
35742         return this.regions[target].add(panel);
35743     },
35744
35745     /**
35746      * Remove a ContentPanel (or subclass) to this layout.
35747      * @param {String} target The target region key (north, south, east, west or center).
35748      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35749      * @return {Roo.ContentPanel} The removed panel
35750      */
35751     remove : function(target, panel){
35752         target = target.toLowerCase();
35753         return this.regions[target].remove(panel);
35754     },
35755
35756     /**
35757      * Searches all regions for a panel with the specified id
35758      * @param {String} panelId
35759      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35760      */
35761     findPanel : function(panelId){
35762         var rs = this.regions;
35763         for(var target in rs){
35764             if(typeof rs[target] != "function"){
35765                 var p = rs[target].getPanel(panelId);
35766                 if(p){
35767                     return p;
35768                 }
35769             }
35770         }
35771         return null;
35772     },
35773
35774     /**
35775      * Searches all regions for a panel with the specified id and activates (shows) it.
35776      * @param {String/ContentPanel} panelId The panels id or the panel itself
35777      * @return {Roo.ContentPanel} The shown panel or null
35778      */
35779     showPanel : function(panelId) {
35780       var rs = this.regions;
35781       for(var target in rs){
35782          var r = rs[target];
35783          if(typeof r != "function"){
35784             if(r.hasPanel(panelId)){
35785                return r.showPanel(panelId);
35786             }
35787          }
35788       }
35789       return null;
35790    },
35791
35792    /**
35793      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35794      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35795      */
35796    /*
35797     restoreState : function(provider){
35798         if(!provider){
35799             provider = Roo.state.Manager;
35800         }
35801         var sm = new Roo.LayoutStateManager();
35802         sm.init(this, provider);
35803     },
35804 */
35805  
35806  
35807     /**
35808      * Adds a xtype elements to the layout.
35809      * <pre><code>
35810
35811 layout.addxtype({
35812        xtype : 'ContentPanel',
35813        region: 'west',
35814        items: [ .... ]
35815    }
35816 );
35817
35818 layout.addxtype({
35819         xtype : 'NestedLayoutPanel',
35820         region: 'west',
35821         layout: {
35822            center: { },
35823            west: { }   
35824         },
35825         items : [ ... list of content panels or nested layout panels.. ]
35826    }
35827 );
35828 </code></pre>
35829      * @param {Object} cfg Xtype definition of item to add.
35830      */
35831     addxtype : function(cfg)
35832     {
35833         // basically accepts a pannel...
35834         // can accept a layout region..!?!?
35835         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35836         
35837         
35838         // theory?  children can only be panels??
35839         
35840         //if (!cfg.xtype.match(/Panel$/)) {
35841         //    return false;
35842         //}
35843         var ret = false;
35844         
35845         if (typeof(cfg.region) == 'undefined') {
35846             Roo.log("Failed to add Panel, region was not set");
35847             Roo.log(cfg);
35848             return false;
35849         }
35850         var region = cfg.region;
35851         delete cfg.region;
35852         
35853           
35854         var xitems = [];
35855         if (cfg.items) {
35856             xitems = cfg.items;
35857             delete cfg.items;
35858         }
35859         var nb = false;
35860         
35861         if ( region == 'center') {
35862             Roo.log("Center: " + cfg.title);
35863         }
35864         
35865         
35866         switch(cfg.xtype) 
35867         {
35868             case 'Content':  // ContentPanel (el, cfg)
35869             case 'Scroll':  // ContentPanel (el, cfg)
35870             case 'View': 
35871                 cfg.autoCreate = cfg.autoCreate || true;
35872                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35873                 //} else {
35874                 //    var el = this.el.createChild();
35875                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35876                 //}
35877                 
35878                 this.add(region, ret);
35879                 break;
35880             
35881             /*
35882             case 'TreePanel': // our new panel!
35883                 cfg.el = this.el.createChild();
35884                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35885                 this.add(region, ret);
35886                 break;
35887             */
35888             
35889             case 'Nest': 
35890                 // create a new Layout (which is  a Border Layout...
35891                 
35892                 var clayout = cfg.layout;
35893                 clayout.el  = this.el.createChild();
35894                 clayout.items   = clayout.items  || [];
35895                 
35896                 delete cfg.layout;
35897                 
35898                 // replace this exitems with the clayout ones..
35899                 xitems = clayout.items;
35900                  
35901                 // force background off if it's in center...
35902                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35903                     cfg.background = false;
35904                 }
35905                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35906                 
35907                 
35908                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35909                 //console.log('adding nested layout panel '  + cfg.toSource());
35910                 this.add(region, ret);
35911                 nb = {}; /// find first...
35912                 break;
35913             
35914             case 'Grid':
35915                 
35916                 // needs grid and region
35917                 
35918                 //var el = this.getRegion(region).el.createChild();
35919                 /*
35920                  *var el = this.el.createChild();
35921                 // create the grid first...
35922                 cfg.grid.container = el;
35923                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35924                 */
35925                 
35926                 if (region == 'center' && this.active ) {
35927                     cfg.background = false;
35928                 }
35929                 
35930                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35931                 
35932                 this.add(region, ret);
35933                 /*
35934                 if (cfg.background) {
35935                     // render grid on panel activation (if panel background)
35936                     ret.on('activate', function(gp) {
35937                         if (!gp.grid.rendered) {
35938                     //        gp.grid.render(el);
35939                         }
35940                     });
35941                 } else {
35942                   //  cfg.grid.render(el);
35943                 }
35944                 */
35945                 break;
35946            
35947            
35948             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35949                 // it was the old xcomponent building that caused this before.
35950                 // espeically if border is the top element in the tree.
35951                 ret = this;
35952                 break; 
35953                 
35954                     
35955                 
35956                 
35957                 
35958             default:
35959                 /*
35960                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35961                     
35962                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35963                     this.add(region, ret);
35964                 } else {
35965                 */
35966                     Roo.log(cfg);
35967                     throw "Can not add '" + cfg.xtype + "' to Border";
35968                     return null;
35969              
35970                                 
35971              
35972         }
35973         this.beginUpdate();
35974         // add children..
35975         var region = '';
35976         var abn = {};
35977         Roo.each(xitems, function(i)  {
35978             region = nb && i.region ? i.region : false;
35979             
35980             var add = ret.addxtype(i);
35981            
35982             if (region) {
35983                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35984                 if (!i.background) {
35985                     abn[region] = nb[region] ;
35986                 }
35987             }
35988             
35989         });
35990         this.endUpdate();
35991
35992         // make the last non-background panel active..
35993         //if (nb) { Roo.log(abn); }
35994         if (nb) {
35995             
35996             for(var r in abn) {
35997                 region = this.getRegion(r);
35998                 if (region) {
35999                     // tried using nb[r], but it does not work..
36000                      
36001                     region.showPanel(abn[r]);
36002                    
36003                 }
36004             }
36005         }
36006         return ret;
36007         
36008     },
36009     
36010     
36011 // private
36012     factory : function(cfg)
36013     {
36014         
36015         var validRegions = Roo.bootstrap.layout.Border.regions;
36016
36017         var target = cfg.region;
36018         cfg.mgr = this;
36019         
36020         var r = Roo.bootstrap.layout;
36021         Roo.log(target);
36022         switch(target){
36023             case "north":
36024                 return new r.North(cfg);
36025             case "south":
36026                 return new r.South(cfg);
36027             case "east":
36028                 return new r.East(cfg);
36029             case "west":
36030                 return new r.West(cfg);
36031             case "center":
36032                 return new r.Center(cfg);
36033         }
36034         throw 'Layout region "'+target+'" not supported.';
36035     }
36036     
36037     
36038 });
36039  /*
36040  * Based on:
36041  * Ext JS Library 1.1.1
36042  * Copyright(c) 2006-2007, Ext JS, LLC.
36043  *
36044  * Originally Released Under LGPL - original licence link has changed is not relivant.
36045  *
36046  * Fork - LGPL
36047  * <script type="text/javascript">
36048  */
36049  
36050 /**
36051  * @class Roo.bootstrap.layout.Basic
36052  * @extends Roo.util.Observable
36053  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36054  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36055  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36056  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36057  * @cfg {string}   region  the region that it inhabits..
36058  * @cfg {bool}   skipConfig skip config?
36059  * 
36060
36061  */
36062 Roo.bootstrap.layout.Basic = function(config){
36063     
36064     this.mgr = config.mgr;
36065     
36066     this.position = config.region;
36067     
36068     var skipConfig = config.skipConfig;
36069     
36070     this.events = {
36071         /**
36072          * @scope Roo.BasicLayoutRegion
36073          */
36074         
36075         /**
36076          * @event beforeremove
36077          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36078          * @param {Roo.LayoutRegion} this
36079          * @param {Roo.ContentPanel} panel The panel
36080          * @param {Object} e The cancel event object
36081          */
36082         "beforeremove" : true,
36083         /**
36084          * @event invalidated
36085          * Fires when the layout for this region is changed.
36086          * @param {Roo.LayoutRegion} this
36087          */
36088         "invalidated" : true,
36089         /**
36090          * @event visibilitychange
36091          * Fires when this region is shown or hidden 
36092          * @param {Roo.LayoutRegion} this
36093          * @param {Boolean} visibility true or false
36094          */
36095         "visibilitychange" : true,
36096         /**
36097          * @event paneladded
36098          * Fires when a panel is added. 
36099          * @param {Roo.LayoutRegion} this
36100          * @param {Roo.ContentPanel} panel The panel
36101          */
36102         "paneladded" : true,
36103         /**
36104          * @event panelremoved
36105          * Fires when a panel is removed. 
36106          * @param {Roo.LayoutRegion} this
36107          * @param {Roo.ContentPanel} panel The panel
36108          */
36109         "panelremoved" : true,
36110         /**
36111          * @event beforecollapse
36112          * Fires when this region before collapse.
36113          * @param {Roo.LayoutRegion} this
36114          */
36115         "beforecollapse" : true,
36116         /**
36117          * @event collapsed
36118          * Fires when this region is collapsed.
36119          * @param {Roo.LayoutRegion} this
36120          */
36121         "collapsed" : true,
36122         /**
36123          * @event expanded
36124          * Fires when this region is expanded.
36125          * @param {Roo.LayoutRegion} this
36126          */
36127         "expanded" : true,
36128         /**
36129          * @event slideshow
36130          * Fires when this region is slid into view.
36131          * @param {Roo.LayoutRegion} this
36132          */
36133         "slideshow" : true,
36134         /**
36135          * @event slidehide
36136          * Fires when this region slides out of view. 
36137          * @param {Roo.LayoutRegion} this
36138          */
36139         "slidehide" : true,
36140         /**
36141          * @event panelactivated
36142          * Fires when a panel is activated. 
36143          * @param {Roo.LayoutRegion} this
36144          * @param {Roo.ContentPanel} panel The activated panel
36145          */
36146         "panelactivated" : true,
36147         /**
36148          * @event resized
36149          * Fires when the user resizes this region. 
36150          * @param {Roo.LayoutRegion} this
36151          * @param {Number} newSize The new size (width for east/west, height for north/south)
36152          */
36153         "resized" : true
36154     };
36155     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36156     this.panels = new Roo.util.MixedCollection();
36157     this.panels.getKey = this.getPanelId.createDelegate(this);
36158     this.box = null;
36159     this.activePanel = null;
36160     // ensure listeners are added...
36161     
36162     if (config.listeners || config.events) {
36163         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36164             listeners : config.listeners || {},
36165             events : config.events || {}
36166         });
36167     }
36168     
36169     if(skipConfig !== true){
36170         this.applyConfig(config);
36171     }
36172 };
36173
36174 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36175 {
36176     getPanelId : function(p){
36177         return p.getId();
36178     },
36179     
36180     applyConfig : function(config){
36181         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36182         this.config = config;
36183         
36184     },
36185     
36186     /**
36187      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36188      * the width, for horizontal (north, south) the height.
36189      * @param {Number} newSize The new width or height
36190      */
36191     resizeTo : function(newSize){
36192         var el = this.el ? this.el :
36193                  (this.activePanel ? this.activePanel.getEl() : null);
36194         if(el){
36195             switch(this.position){
36196                 case "east":
36197                 case "west":
36198                     el.setWidth(newSize);
36199                     this.fireEvent("resized", this, newSize);
36200                 break;
36201                 case "north":
36202                 case "south":
36203                     el.setHeight(newSize);
36204                     this.fireEvent("resized", this, newSize);
36205                 break;                
36206             }
36207         }
36208     },
36209     
36210     getBox : function(){
36211         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36212     },
36213     
36214     getMargins : function(){
36215         return this.margins;
36216     },
36217     
36218     updateBox : function(box){
36219         this.box = box;
36220         var el = this.activePanel.getEl();
36221         el.dom.style.left = box.x + "px";
36222         el.dom.style.top = box.y + "px";
36223         this.activePanel.setSize(box.width, box.height);
36224     },
36225     
36226     /**
36227      * Returns the container element for this region.
36228      * @return {Roo.Element}
36229      */
36230     getEl : function(){
36231         return this.activePanel;
36232     },
36233     
36234     /**
36235      * Returns true if this region is currently visible.
36236      * @return {Boolean}
36237      */
36238     isVisible : function(){
36239         return this.activePanel ? true : false;
36240     },
36241     
36242     setActivePanel : function(panel){
36243         panel = this.getPanel(panel);
36244         if(this.activePanel && this.activePanel != panel){
36245             this.activePanel.setActiveState(false);
36246             this.activePanel.getEl().setLeftTop(-10000,-10000);
36247         }
36248         this.activePanel = panel;
36249         panel.setActiveState(true);
36250         if(this.box){
36251             panel.setSize(this.box.width, this.box.height);
36252         }
36253         this.fireEvent("panelactivated", this, panel);
36254         this.fireEvent("invalidated");
36255     },
36256     
36257     /**
36258      * Show the specified panel.
36259      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36260      * @return {Roo.ContentPanel} The shown panel or null
36261      */
36262     showPanel : function(panel){
36263         panel = this.getPanel(panel);
36264         if(panel){
36265             this.setActivePanel(panel);
36266         }
36267         return panel;
36268     },
36269     
36270     /**
36271      * Get the active panel for this region.
36272      * @return {Roo.ContentPanel} The active panel or null
36273      */
36274     getActivePanel : function(){
36275         return this.activePanel;
36276     },
36277     
36278     /**
36279      * Add the passed ContentPanel(s)
36280      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36281      * @return {Roo.ContentPanel} The panel added (if only one was added)
36282      */
36283     add : function(panel){
36284         if(arguments.length > 1){
36285             for(var i = 0, len = arguments.length; i < len; i++) {
36286                 this.add(arguments[i]);
36287             }
36288             return null;
36289         }
36290         if(this.hasPanel(panel)){
36291             this.showPanel(panel);
36292             return panel;
36293         }
36294         var el = panel.getEl();
36295         if(el.dom.parentNode != this.mgr.el.dom){
36296             this.mgr.el.dom.appendChild(el.dom);
36297         }
36298         if(panel.setRegion){
36299             panel.setRegion(this);
36300         }
36301         this.panels.add(panel);
36302         el.setStyle("position", "absolute");
36303         if(!panel.background){
36304             this.setActivePanel(panel);
36305             if(this.config.initialSize && this.panels.getCount()==1){
36306                 this.resizeTo(this.config.initialSize);
36307             }
36308         }
36309         this.fireEvent("paneladded", this, panel);
36310         return panel;
36311     },
36312     
36313     /**
36314      * Returns true if the panel is in this region.
36315      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36316      * @return {Boolean}
36317      */
36318     hasPanel : function(panel){
36319         if(typeof panel == "object"){ // must be panel obj
36320             panel = panel.getId();
36321         }
36322         return this.getPanel(panel) ? true : false;
36323     },
36324     
36325     /**
36326      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36327      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36328      * @param {Boolean} preservePanel Overrides the config preservePanel option
36329      * @return {Roo.ContentPanel} The panel that was removed
36330      */
36331     remove : function(panel, preservePanel){
36332         panel = this.getPanel(panel);
36333         if(!panel){
36334             return null;
36335         }
36336         var e = {};
36337         this.fireEvent("beforeremove", this, panel, e);
36338         if(e.cancel === true){
36339             return null;
36340         }
36341         var panelId = panel.getId();
36342         this.panels.removeKey(panelId);
36343         return panel;
36344     },
36345     
36346     /**
36347      * Returns the panel specified or null if it's not in this region.
36348      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36349      * @return {Roo.ContentPanel}
36350      */
36351     getPanel : function(id){
36352         if(typeof id == "object"){ // must be panel obj
36353             return id;
36354         }
36355         return this.panels.get(id);
36356     },
36357     
36358     /**
36359      * Returns this regions position (north/south/east/west/center).
36360      * @return {String} 
36361      */
36362     getPosition: function(){
36363         return this.position;    
36364     }
36365 });/*
36366  * Based on:
36367  * Ext JS Library 1.1.1
36368  * Copyright(c) 2006-2007, Ext JS, LLC.
36369  *
36370  * Originally Released Under LGPL - original licence link has changed is not relivant.
36371  *
36372  * Fork - LGPL
36373  * <script type="text/javascript">
36374  */
36375  
36376 /**
36377  * @class Roo.bootstrap.layout.Region
36378  * @extends Roo.bootstrap.layout.Basic
36379  * This class represents a region in a layout manager.
36380  
36381  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36382  * @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})
36383  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36384  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36385  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36386  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36387  * @cfg {String}    title           The title for the region (overrides panel titles)
36388  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36389  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36390  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36391  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36392  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36393  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36394  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36395  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36396  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36397  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36398
36399  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36400  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36401  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36402  * @cfg {Number}    width           For East/West panels
36403  * @cfg {Number}    height          For North/South panels
36404  * @cfg {Boolean}   split           To show the splitter
36405  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36406  * 
36407  * @cfg {string}   cls             Extra CSS classes to add to region
36408  * 
36409  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36410  * @cfg {string}   region  the region that it inhabits..
36411  *
36412
36413  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36414  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36415
36416  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36417  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36418  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36419  */
36420 Roo.bootstrap.layout.Region = function(config)
36421 {
36422     this.applyConfig(config);
36423
36424     var mgr = config.mgr;
36425     var pos = config.region;
36426     config.skipConfig = true;
36427     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36428     
36429     if (mgr.el) {
36430         this.onRender(mgr.el);   
36431     }
36432      
36433     this.visible = true;
36434     this.collapsed = false;
36435     this.unrendered_panels = [];
36436 };
36437
36438 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36439
36440     position: '', // set by wrapper (eg. north/south etc..)
36441     unrendered_panels : null,  // unrendered panels.
36442     
36443     tabPosition : false,
36444     
36445     mgr: false, // points to 'Border'
36446     
36447     
36448     createBody : function(){
36449         /** This region's body element 
36450         * @type Roo.Element */
36451         this.bodyEl = this.el.createChild({
36452                 tag: "div",
36453                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36454         });
36455     },
36456
36457     onRender: function(ctr, pos)
36458     {
36459         var dh = Roo.DomHelper;
36460         /** This region's container element 
36461         * @type Roo.Element */
36462         this.el = dh.append(ctr.dom, {
36463                 tag: "div",
36464                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36465             }, true);
36466         /** This region's title element 
36467         * @type Roo.Element */
36468     
36469         this.titleEl = dh.append(this.el.dom,  {
36470                 tag: "div",
36471                 unselectable: "on",
36472                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36473                 children:[
36474                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36475                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36476                 ]
36477             }, true);
36478         
36479         this.titleEl.enableDisplayMode();
36480         /** This region's title text element 
36481         * @type HTMLElement */
36482         this.titleTextEl = this.titleEl.dom.firstChild;
36483         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36484         /*
36485         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36486         this.closeBtn.enableDisplayMode();
36487         this.closeBtn.on("click", this.closeClicked, this);
36488         this.closeBtn.hide();
36489     */
36490         this.createBody(this.config);
36491         if(this.config.hideWhenEmpty){
36492             this.hide();
36493             this.on("paneladded", this.validateVisibility, this);
36494             this.on("panelremoved", this.validateVisibility, this);
36495         }
36496         if(this.autoScroll){
36497             this.bodyEl.setStyle("overflow", "auto");
36498         }else{
36499             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36500         }
36501         //if(c.titlebar !== false){
36502             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36503                 this.titleEl.hide();
36504             }else{
36505                 this.titleEl.show();
36506                 if(this.config.title){
36507                     this.titleTextEl.innerHTML = this.config.title;
36508                 }
36509             }
36510         //}
36511         if(this.config.collapsed){
36512             this.collapse(true);
36513         }
36514         if(this.config.hidden){
36515             this.hide();
36516         }
36517         
36518         if (this.unrendered_panels && this.unrendered_panels.length) {
36519             for (var i =0;i< this.unrendered_panels.length; i++) {
36520                 this.add(this.unrendered_panels[i]);
36521             }
36522             this.unrendered_panels = null;
36523             
36524         }
36525         
36526     },
36527     
36528     applyConfig : function(c)
36529     {
36530         /*
36531          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36532             var dh = Roo.DomHelper;
36533             if(c.titlebar !== false){
36534                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36535                 this.collapseBtn.on("click", this.collapse, this);
36536                 this.collapseBtn.enableDisplayMode();
36537                 /*
36538                 if(c.showPin === true || this.showPin){
36539                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36540                     this.stickBtn.enableDisplayMode();
36541                     this.stickBtn.on("click", this.expand, this);
36542                     this.stickBtn.hide();
36543                 }
36544                 
36545             }
36546             */
36547             /** This region's collapsed element
36548             * @type Roo.Element */
36549             /*
36550              *
36551             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36552                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36553             ]}, true);
36554             
36555             if(c.floatable !== false){
36556                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36557                this.collapsedEl.on("click", this.collapseClick, this);
36558             }
36559
36560             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36561                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36562                    id: "message", unselectable: "on", style:{"float":"left"}});
36563                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36564              }
36565             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36566             this.expandBtn.on("click", this.expand, this);
36567             
36568         }
36569         
36570         if(this.collapseBtn){
36571             this.collapseBtn.setVisible(c.collapsible == true);
36572         }
36573         
36574         this.cmargins = c.cmargins || this.cmargins ||
36575                          (this.position == "west" || this.position == "east" ?
36576                              {top: 0, left: 2, right:2, bottom: 0} :
36577                              {top: 2, left: 0, right:0, bottom: 2});
36578         */
36579         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36580         
36581         
36582         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36583         
36584         this.autoScroll = c.autoScroll || false;
36585         
36586         
36587        
36588         
36589         this.duration = c.duration || .30;
36590         this.slideDuration = c.slideDuration || .45;
36591         this.config = c;
36592        
36593     },
36594     /**
36595      * Returns true if this region is currently visible.
36596      * @return {Boolean}
36597      */
36598     isVisible : function(){
36599         return this.visible;
36600     },
36601
36602     /**
36603      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36604      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36605      */
36606     //setCollapsedTitle : function(title){
36607     //    title = title || "&#160;";
36608      //   if(this.collapsedTitleTextEl){
36609       //      this.collapsedTitleTextEl.innerHTML = title;
36610        // }
36611     //},
36612
36613     getBox : function(){
36614         var b;
36615       //  if(!this.collapsed){
36616             b = this.el.getBox(false, true);
36617        // }else{
36618           //  b = this.collapsedEl.getBox(false, true);
36619         //}
36620         return b;
36621     },
36622
36623     getMargins : function(){
36624         return this.margins;
36625         //return this.collapsed ? this.cmargins : this.margins;
36626     },
36627 /*
36628     highlight : function(){
36629         this.el.addClass("x-layout-panel-dragover");
36630     },
36631
36632     unhighlight : function(){
36633         this.el.removeClass("x-layout-panel-dragover");
36634     },
36635 */
36636     updateBox : function(box)
36637     {
36638         if (!this.bodyEl) {
36639             return; // not rendered yet..
36640         }
36641         
36642         this.box = box;
36643         if(!this.collapsed){
36644             this.el.dom.style.left = box.x + "px";
36645             this.el.dom.style.top = box.y + "px";
36646             this.updateBody(box.width, box.height);
36647         }else{
36648             this.collapsedEl.dom.style.left = box.x + "px";
36649             this.collapsedEl.dom.style.top = box.y + "px";
36650             this.collapsedEl.setSize(box.width, box.height);
36651         }
36652         if(this.tabs){
36653             this.tabs.autoSizeTabs();
36654         }
36655     },
36656
36657     updateBody : function(w, h)
36658     {
36659         if(w !== null){
36660             this.el.setWidth(w);
36661             w -= this.el.getBorderWidth("rl");
36662             if(this.config.adjustments){
36663                 w += this.config.adjustments[0];
36664             }
36665         }
36666         if(h !== null && h > 0){
36667             this.el.setHeight(h);
36668             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36669             h -= this.el.getBorderWidth("tb");
36670             if(this.config.adjustments){
36671                 h += this.config.adjustments[1];
36672             }
36673             this.bodyEl.setHeight(h);
36674             if(this.tabs){
36675                 h = this.tabs.syncHeight(h);
36676             }
36677         }
36678         if(this.panelSize){
36679             w = w !== null ? w : this.panelSize.width;
36680             h = h !== null ? h : this.panelSize.height;
36681         }
36682         if(this.activePanel){
36683             var el = this.activePanel.getEl();
36684             w = w !== null ? w : el.getWidth();
36685             h = h !== null ? h : el.getHeight();
36686             this.panelSize = {width: w, height: h};
36687             this.activePanel.setSize(w, h);
36688         }
36689         if(Roo.isIE && this.tabs){
36690             this.tabs.el.repaint();
36691         }
36692     },
36693
36694     /**
36695      * Returns the container element for this region.
36696      * @return {Roo.Element}
36697      */
36698     getEl : function(){
36699         return this.el;
36700     },
36701
36702     /**
36703      * Hides this region.
36704      */
36705     hide : function(){
36706         //if(!this.collapsed){
36707             this.el.dom.style.left = "-2000px";
36708             this.el.hide();
36709         //}else{
36710          //   this.collapsedEl.dom.style.left = "-2000px";
36711          //   this.collapsedEl.hide();
36712        // }
36713         this.visible = false;
36714         this.fireEvent("visibilitychange", this, false);
36715     },
36716
36717     /**
36718      * Shows this region if it was previously hidden.
36719      */
36720     show : function(){
36721         //if(!this.collapsed){
36722             this.el.show();
36723         //}else{
36724         //    this.collapsedEl.show();
36725        // }
36726         this.visible = true;
36727         this.fireEvent("visibilitychange", this, true);
36728     },
36729 /*
36730     closeClicked : function(){
36731         if(this.activePanel){
36732             this.remove(this.activePanel);
36733         }
36734     },
36735
36736     collapseClick : function(e){
36737         if(this.isSlid){
36738            e.stopPropagation();
36739            this.slideIn();
36740         }else{
36741            e.stopPropagation();
36742            this.slideOut();
36743         }
36744     },
36745 */
36746     /**
36747      * Collapses this region.
36748      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36749      */
36750     /*
36751     collapse : function(skipAnim, skipCheck = false){
36752         if(this.collapsed) {
36753             return;
36754         }
36755         
36756         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36757             
36758             this.collapsed = true;
36759             if(this.split){
36760                 this.split.el.hide();
36761             }
36762             if(this.config.animate && skipAnim !== true){
36763                 this.fireEvent("invalidated", this);
36764                 this.animateCollapse();
36765             }else{
36766                 this.el.setLocation(-20000,-20000);
36767                 this.el.hide();
36768                 this.collapsedEl.show();
36769                 this.fireEvent("collapsed", this);
36770                 this.fireEvent("invalidated", this);
36771             }
36772         }
36773         
36774     },
36775 */
36776     animateCollapse : function(){
36777         // overridden
36778     },
36779
36780     /**
36781      * Expands this region if it was previously collapsed.
36782      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36783      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36784      */
36785     /*
36786     expand : function(e, skipAnim){
36787         if(e) {
36788             e.stopPropagation();
36789         }
36790         if(!this.collapsed || this.el.hasActiveFx()) {
36791             return;
36792         }
36793         if(this.isSlid){
36794             this.afterSlideIn();
36795             skipAnim = true;
36796         }
36797         this.collapsed = false;
36798         if(this.config.animate && skipAnim !== true){
36799             this.animateExpand();
36800         }else{
36801             this.el.show();
36802             if(this.split){
36803                 this.split.el.show();
36804             }
36805             this.collapsedEl.setLocation(-2000,-2000);
36806             this.collapsedEl.hide();
36807             this.fireEvent("invalidated", this);
36808             this.fireEvent("expanded", this);
36809         }
36810     },
36811 */
36812     animateExpand : function(){
36813         // overridden
36814     },
36815
36816     initTabs : function()
36817     {
36818         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36819         
36820         var ts = new Roo.bootstrap.panel.Tabs({
36821             el: this.bodyEl.dom,
36822             region : this,
36823             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36824             disableTooltips: this.config.disableTabTips,
36825             toolbar : this.config.toolbar
36826         });
36827         
36828         if(this.config.hideTabs){
36829             ts.stripWrap.setDisplayed(false);
36830         }
36831         this.tabs = ts;
36832         ts.resizeTabs = this.config.resizeTabs === true;
36833         ts.minTabWidth = this.config.minTabWidth || 40;
36834         ts.maxTabWidth = this.config.maxTabWidth || 250;
36835         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36836         ts.monitorResize = false;
36837         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36838         ts.bodyEl.addClass('roo-layout-tabs-body');
36839         this.panels.each(this.initPanelAsTab, this);
36840     },
36841
36842     initPanelAsTab : function(panel){
36843         var ti = this.tabs.addTab(
36844             panel.getEl().id,
36845             panel.getTitle(),
36846             null,
36847             this.config.closeOnTab && panel.isClosable(),
36848             panel.tpl
36849         );
36850         if(panel.tabTip !== undefined){
36851             ti.setTooltip(panel.tabTip);
36852         }
36853         ti.on("activate", function(){
36854               this.setActivePanel(panel);
36855         }, this);
36856         
36857         if(this.config.closeOnTab){
36858             ti.on("beforeclose", function(t, e){
36859                 e.cancel = true;
36860                 this.remove(panel);
36861             }, this);
36862         }
36863         
36864         panel.tabItem = ti;
36865         
36866         return ti;
36867     },
36868
36869     updatePanelTitle : function(panel, title)
36870     {
36871         if(this.activePanel == panel){
36872             this.updateTitle(title);
36873         }
36874         if(this.tabs){
36875             var ti = this.tabs.getTab(panel.getEl().id);
36876             ti.setText(title);
36877             if(panel.tabTip !== undefined){
36878                 ti.setTooltip(panel.tabTip);
36879             }
36880         }
36881     },
36882
36883     updateTitle : function(title){
36884         if(this.titleTextEl && !this.config.title){
36885             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36886         }
36887     },
36888
36889     setActivePanel : function(panel)
36890     {
36891         panel = this.getPanel(panel);
36892         if(this.activePanel && this.activePanel != panel){
36893             if(this.activePanel.setActiveState(false) === false){
36894                 return;
36895             }
36896         }
36897         this.activePanel = panel;
36898         panel.setActiveState(true);
36899         if(this.panelSize){
36900             panel.setSize(this.panelSize.width, this.panelSize.height);
36901         }
36902         if(this.closeBtn){
36903             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36904         }
36905         this.updateTitle(panel.getTitle());
36906         if(this.tabs){
36907             this.fireEvent("invalidated", this);
36908         }
36909         this.fireEvent("panelactivated", this, panel);
36910     },
36911
36912     /**
36913      * Shows the specified panel.
36914      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36915      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36916      */
36917     showPanel : function(panel)
36918     {
36919         panel = this.getPanel(panel);
36920         if(panel){
36921             if(this.tabs){
36922                 var tab = this.tabs.getTab(panel.getEl().id);
36923                 if(tab.isHidden()){
36924                     this.tabs.unhideTab(tab.id);
36925                 }
36926                 tab.activate();
36927             }else{
36928                 this.setActivePanel(panel);
36929             }
36930         }
36931         return panel;
36932     },
36933
36934     /**
36935      * Get the active panel for this region.
36936      * @return {Roo.ContentPanel} The active panel or null
36937      */
36938     getActivePanel : function(){
36939         return this.activePanel;
36940     },
36941
36942     validateVisibility : function(){
36943         if(this.panels.getCount() < 1){
36944             this.updateTitle("&#160;");
36945             this.closeBtn.hide();
36946             this.hide();
36947         }else{
36948             if(!this.isVisible()){
36949                 this.show();
36950             }
36951         }
36952     },
36953
36954     /**
36955      * Adds the passed ContentPanel(s) to this region.
36956      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36957      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36958      */
36959     add : function(panel)
36960     {
36961         if(arguments.length > 1){
36962             for(var i = 0, len = arguments.length; i < len; i++) {
36963                 this.add(arguments[i]);
36964             }
36965             return null;
36966         }
36967         
36968         // if we have not been rendered yet, then we can not really do much of this..
36969         if (!this.bodyEl) {
36970             this.unrendered_panels.push(panel);
36971             return panel;
36972         }
36973         
36974         
36975         
36976         
36977         if(this.hasPanel(panel)){
36978             this.showPanel(panel);
36979             return panel;
36980         }
36981         panel.setRegion(this);
36982         this.panels.add(panel);
36983        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36984             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36985             // and hide them... ???
36986             this.bodyEl.dom.appendChild(panel.getEl().dom);
36987             if(panel.background !== true){
36988                 this.setActivePanel(panel);
36989             }
36990             this.fireEvent("paneladded", this, panel);
36991             return panel;
36992         }
36993         */
36994         if(!this.tabs){
36995             this.initTabs();
36996         }else{
36997             this.initPanelAsTab(panel);
36998         }
36999         
37000         
37001         if(panel.background !== true){
37002             this.tabs.activate(panel.getEl().id);
37003         }
37004         this.fireEvent("paneladded", this, panel);
37005         return panel;
37006     },
37007
37008     /**
37009      * Hides the tab for the specified panel.
37010      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37011      */
37012     hidePanel : function(panel){
37013         if(this.tabs && (panel = this.getPanel(panel))){
37014             this.tabs.hideTab(panel.getEl().id);
37015         }
37016     },
37017
37018     /**
37019      * Unhides the tab for a previously hidden panel.
37020      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37021      */
37022     unhidePanel : function(panel){
37023         if(this.tabs && (panel = this.getPanel(panel))){
37024             this.tabs.unhideTab(panel.getEl().id);
37025         }
37026     },
37027
37028     clearPanels : function(){
37029         while(this.panels.getCount() > 0){
37030              this.remove(this.panels.first());
37031         }
37032     },
37033
37034     /**
37035      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37036      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37037      * @param {Boolean} preservePanel Overrides the config preservePanel option
37038      * @return {Roo.ContentPanel} The panel that was removed
37039      */
37040     remove : function(panel, preservePanel)
37041     {
37042         panel = this.getPanel(panel);
37043         if(!panel){
37044             return null;
37045         }
37046         var e = {};
37047         this.fireEvent("beforeremove", this, panel, e);
37048         if(e.cancel === true){
37049             return null;
37050         }
37051         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37052         var panelId = panel.getId();
37053         this.panels.removeKey(panelId);
37054         if(preservePanel){
37055             document.body.appendChild(panel.getEl().dom);
37056         }
37057         if(this.tabs){
37058             this.tabs.removeTab(panel.getEl().id);
37059         }else if (!preservePanel){
37060             this.bodyEl.dom.removeChild(panel.getEl().dom);
37061         }
37062         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37063             var p = this.panels.first();
37064             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37065             tempEl.appendChild(p.getEl().dom);
37066             this.bodyEl.update("");
37067             this.bodyEl.dom.appendChild(p.getEl().dom);
37068             tempEl = null;
37069             this.updateTitle(p.getTitle());
37070             this.tabs = null;
37071             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37072             this.setActivePanel(p);
37073         }
37074         panel.setRegion(null);
37075         if(this.activePanel == panel){
37076             this.activePanel = null;
37077         }
37078         if(this.config.autoDestroy !== false && preservePanel !== true){
37079             try{panel.destroy();}catch(e){}
37080         }
37081         this.fireEvent("panelremoved", this, panel);
37082         return panel;
37083     },
37084
37085     /**
37086      * Returns the TabPanel component used by this region
37087      * @return {Roo.TabPanel}
37088      */
37089     getTabs : function(){
37090         return this.tabs;
37091     },
37092
37093     createTool : function(parentEl, className){
37094         var btn = Roo.DomHelper.append(parentEl, {
37095             tag: "div",
37096             cls: "x-layout-tools-button",
37097             children: [ {
37098                 tag: "div",
37099                 cls: "roo-layout-tools-button-inner " + className,
37100                 html: "&#160;"
37101             }]
37102         }, true);
37103         btn.addClassOnOver("roo-layout-tools-button-over");
37104         return btn;
37105     }
37106 });/*
37107  * Based on:
37108  * Ext JS Library 1.1.1
37109  * Copyright(c) 2006-2007, Ext JS, LLC.
37110  *
37111  * Originally Released Under LGPL - original licence link has changed is not relivant.
37112  *
37113  * Fork - LGPL
37114  * <script type="text/javascript">
37115  */
37116  
37117
37118
37119 /**
37120  * @class Roo.SplitLayoutRegion
37121  * @extends Roo.LayoutRegion
37122  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37123  */
37124 Roo.bootstrap.layout.Split = function(config){
37125     this.cursor = config.cursor;
37126     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37127 };
37128
37129 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37130 {
37131     splitTip : "Drag to resize.",
37132     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37133     useSplitTips : false,
37134
37135     applyConfig : function(config){
37136         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37137     },
37138     
37139     onRender : function(ctr,pos) {
37140         
37141         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37142         if(!this.config.split){
37143             return;
37144         }
37145         if(!this.split){
37146             
37147             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37148                             tag: "div",
37149                             id: this.el.id + "-split",
37150                             cls: "roo-layout-split roo-layout-split-"+this.position,
37151                             html: "&#160;"
37152             });
37153             /** The SplitBar for this region 
37154             * @type Roo.SplitBar */
37155             // does not exist yet...
37156             Roo.log([this.position, this.orientation]);
37157             
37158             this.split = new Roo.bootstrap.SplitBar({
37159                 dragElement : splitEl,
37160                 resizingElement: this.el,
37161                 orientation : this.orientation
37162             });
37163             
37164             this.split.on("moved", this.onSplitMove, this);
37165             this.split.useShim = this.config.useShim === true;
37166             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37167             if(this.useSplitTips){
37168                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37169             }
37170             //if(config.collapsible){
37171             //    this.split.el.on("dblclick", this.collapse,  this);
37172             //}
37173         }
37174         if(typeof this.config.minSize != "undefined"){
37175             this.split.minSize = this.config.minSize;
37176         }
37177         if(typeof this.config.maxSize != "undefined"){
37178             this.split.maxSize = this.config.maxSize;
37179         }
37180         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37181             this.hideSplitter();
37182         }
37183         
37184     },
37185
37186     getHMaxSize : function(){
37187          var cmax = this.config.maxSize || 10000;
37188          var center = this.mgr.getRegion("center");
37189          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37190     },
37191
37192     getVMaxSize : function(){
37193          var cmax = this.config.maxSize || 10000;
37194          var center = this.mgr.getRegion("center");
37195          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37196     },
37197
37198     onSplitMove : function(split, newSize){
37199         this.fireEvent("resized", this, newSize);
37200     },
37201     
37202     /** 
37203      * Returns the {@link Roo.SplitBar} for this region.
37204      * @return {Roo.SplitBar}
37205      */
37206     getSplitBar : function(){
37207         return this.split;
37208     },
37209     
37210     hide : function(){
37211         this.hideSplitter();
37212         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37213     },
37214
37215     hideSplitter : function(){
37216         if(this.split){
37217             this.split.el.setLocation(-2000,-2000);
37218             this.split.el.hide();
37219         }
37220     },
37221
37222     show : function(){
37223         if(this.split){
37224             this.split.el.show();
37225         }
37226         Roo.bootstrap.layout.Split.superclass.show.call(this);
37227     },
37228     
37229     beforeSlide: function(){
37230         if(Roo.isGecko){// firefox overflow auto bug workaround
37231             this.bodyEl.clip();
37232             if(this.tabs) {
37233                 this.tabs.bodyEl.clip();
37234             }
37235             if(this.activePanel){
37236                 this.activePanel.getEl().clip();
37237                 
37238                 if(this.activePanel.beforeSlide){
37239                     this.activePanel.beforeSlide();
37240                 }
37241             }
37242         }
37243     },
37244     
37245     afterSlide : function(){
37246         if(Roo.isGecko){// firefox overflow auto bug workaround
37247             this.bodyEl.unclip();
37248             if(this.tabs) {
37249                 this.tabs.bodyEl.unclip();
37250             }
37251             if(this.activePanel){
37252                 this.activePanel.getEl().unclip();
37253                 if(this.activePanel.afterSlide){
37254                     this.activePanel.afterSlide();
37255                 }
37256             }
37257         }
37258     },
37259
37260     initAutoHide : function(){
37261         if(this.autoHide !== false){
37262             if(!this.autoHideHd){
37263                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37264                 this.autoHideHd = {
37265                     "mouseout": function(e){
37266                         if(!e.within(this.el, true)){
37267                             st.delay(500);
37268                         }
37269                     },
37270                     "mouseover" : function(e){
37271                         st.cancel();
37272                     },
37273                     scope : this
37274                 };
37275             }
37276             this.el.on(this.autoHideHd);
37277         }
37278     },
37279
37280     clearAutoHide : function(){
37281         if(this.autoHide !== false){
37282             this.el.un("mouseout", this.autoHideHd.mouseout);
37283             this.el.un("mouseover", this.autoHideHd.mouseover);
37284         }
37285     },
37286
37287     clearMonitor : function(){
37288         Roo.get(document).un("click", this.slideInIf, this);
37289     },
37290
37291     // these names are backwards but not changed for compat
37292     slideOut : function(){
37293         if(this.isSlid || this.el.hasActiveFx()){
37294             return;
37295         }
37296         this.isSlid = true;
37297         if(this.collapseBtn){
37298             this.collapseBtn.hide();
37299         }
37300         this.closeBtnState = this.closeBtn.getStyle('display');
37301         this.closeBtn.hide();
37302         if(this.stickBtn){
37303             this.stickBtn.show();
37304         }
37305         this.el.show();
37306         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37307         this.beforeSlide();
37308         this.el.setStyle("z-index", 10001);
37309         this.el.slideIn(this.getSlideAnchor(), {
37310             callback: function(){
37311                 this.afterSlide();
37312                 this.initAutoHide();
37313                 Roo.get(document).on("click", this.slideInIf, this);
37314                 this.fireEvent("slideshow", this);
37315             },
37316             scope: this,
37317             block: true
37318         });
37319     },
37320
37321     afterSlideIn : function(){
37322         this.clearAutoHide();
37323         this.isSlid = false;
37324         this.clearMonitor();
37325         this.el.setStyle("z-index", "");
37326         if(this.collapseBtn){
37327             this.collapseBtn.show();
37328         }
37329         this.closeBtn.setStyle('display', this.closeBtnState);
37330         if(this.stickBtn){
37331             this.stickBtn.hide();
37332         }
37333         this.fireEvent("slidehide", this);
37334     },
37335
37336     slideIn : function(cb){
37337         if(!this.isSlid || this.el.hasActiveFx()){
37338             Roo.callback(cb);
37339             return;
37340         }
37341         this.isSlid = false;
37342         this.beforeSlide();
37343         this.el.slideOut(this.getSlideAnchor(), {
37344             callback: function(){
37345                 this.el.setLeftTop(-10000, -10000);
37346                 this.afterSlide();
37347                 this.afterSlideIn();
37348                 Roo.callback(cb);
37349             },
37350             scope: this,
37351             block: true
37352         });
37353     },
37354     
37355     slideInIf : function(e){
37356         if(!e.within(this.el)){
37357             this.slideIn();
37358         }
37359     },
37360
37361     animateCollapse : function(){
37362         this.beforeSlide();
37363         this.el.setStyle("z-index", 20000);
37364         var anchor = this.getSlideAnchor();
37365         this.el.slideOut(anchor, {
37366             callback : function(){
37367                 this.el.setStyle("z-index", "");
37368                 this.collapsedEl.slideIn(anchor, {duration:.3});
37369                 this.afterSlide();
37370                 this.el.setLocation(-10000,-10000);
37371                 this.el.hide();
37372                 this.fireEvent("collapsed", this);
37373             },
37374             scope: this,
37375             block: true
37376         });
37377     },
37378
37379     animateExpand : function(){
37380         this.beforeSlide();
37381         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37382         this.el.setStyle("z-index", 20000);
37383         this.collapsedEl.hide({
37384             duration:.1
37385         });
37386         this.el.slideIn(this.getSlideAnchor(), {
37387             callback : function(){
37388                 this.el.setStyle("z-index", "");
37389                 this.afterSlide();
37390                 if(this.split){
37391                     this.split.el.show();
37392                 }
37393                 this.fireEvent("invalidated", this);
37394                 this.fireEvent("expanded", this);
37395             },
37396             scope: this,
37397             block: true
37398         });
37399     },
37400
37401     anchors : {
37402         "west" : "left",
37403         "east" : "right",
37404         "north" : "top",
37405         "south" : "bottom"
37406     },
37407
37408     sanchors : {
37409         "west" : "l",
37410         "east" : "r",
37411         "north" : "t",
37412         "south" : "b"
37413     },
37414
37415     canchors : {
37416         "west" : "tl-tr",
37417         "east" : "tr-tl",
37418         "north" : "tl-bl",
37419         "south" : "bl-tl"
37420     },
37421
37422     getAnchor : function(){
37423         return this.anchors[this.position];
37424     },
37425
37426     getCollapseAnchor : function(){
37427         return this.canchors[this.position];
37428     },
37429
37430     getSlideAnchor : function(){
37431         return this.sanchors[this.position];
37432     },
37433
37434     getAlignAdj : function(){
37435         var cm = this.cmargins;
37436         switch(this.position){
37437             case "west":
37438                 return [0, 0];
37439             break;
37440             case "east":
37441                 return [0, 0];
37442             break;
37443             case "north":
37444                 return [0, 0];
37445             break;
37446             case "south":
37447                 return [0, 0];
37448             break;
37449         }
37450     },
37451
37452     getExpandAdj : function(){
37453         var c = this.collapsedEl, cm = this.cmargins;
37454         switch(this.position){
37455             case "west":
37456                 return [-(cm.right+c.getWidth()+cm.left), 0];
37457             break;
37458             case "east":
37459                 return [cm.right+c.getWidth()+cm.left, 0];
37460             break;
37461             case "north":
37462                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37463             break;
37464             case "south":
37465                 return [0, cm.top+cm.bottom+c.getHeight()];
37466             break;
37467         }
37468     }
37469 });/*
37470  * Based on:
37471  * Ext JS Library 1.1.1
37472  * Copyright(c) 2006-2007, Ext JS, LLC.
37473  *
37474  * Originally Released Under LGPL - original licence link has changed is not relivant.
37475  *
37476  * Fork - LGPL
37477  * <script type="text/javascript">
37478  */
37479 /*
37480  * These classes are private internal classes
37481  */
37482 Roo.bootstrap.layout.Center = function(config){
37483     config.region = "center";
37484     Roo.bootstrap.layout.Region.call(this, config);
37485     this.visible = true;
37486     this.minWidth = config.minWidth || 20;
37487     this.minHeight = config.minHeight || 20;
37488 };
37489
37490 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37491     hide : function(){
37492         // center panel can't be hidden
37493     },
37494     
37495     show : function(){
37496         // center panel can't be hidden
37497     },
37498     
37499     getMinWidth: function(){
37500         return this.minWidth;
37501     },
37502     
37503     getMinHeight: function(){
37504         return this.minHeight;
37505     }
37506 });
37507
37508
37509
37510
37511  
37512
37513
37514
37515
37516
37517
37518 Roo.bootstrap.layout.North = function(config)
37519 {
37520     config.region = 'north';
37521     config.cursor = 'n-resize';
37522     
37523     Roo.bootstrap.layout.Split.call(this, config);
37524     
37525     
37526     if(this.split){
37527         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37528         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37529         this.split.el.addClass("roo-layout-split-v");
37530     }
37531     var size = config.initialSize || config.height;
37532     if(typeof size != "undefined"){
37533         this.el.setHeight(size);
37534     }
37535 };
37536 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37537 {
37538     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37539     
37540     
37541     
37542     getBox : function(){
37543         if(this.collapsed){
37544             return this.collapsedEl.getBox();
37545         }
37546         var box = this.el.getBox();
37547         if(this.split){
37548             box.height += this.split.el.getHeight();
37549         }
37550         return box;
37551     },
37552     
37553     updateBox : function(box){
37554         if(this.split && !this.collapsed){
37555             box.height -= this.split.el.getHeight();
37556             this.split.el.setLeft(box.x);
37557             this.split.el.setTop(box.y+box.height);
37558             this.split.el.setWidth(box.width);
37559         }
37560         if(this.collapsed){
37561             this.updateBody(box.width, null);
37562         }
37563         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37564     }
37565 });
37566
37567
37568
37569
37570
37571 Roo.bootstrap.layout.South = function(config){
37572     config.region = 'south';
37573     config.cursor = 's-resize';
37574     Roo.bootstrap.layout.Split.call(this, config);
37575     if(this.split){
37576         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37577         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37578         this.split.el.addClass("roo-layout-split-v");
37579     }
37580     var size = config.initialSize || config.height;
37581     if(typeof size != "undefined"){
37582         this.el.setHeight(size);
37583     }
37584 };
37585
37586 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37587     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37588     getBox : function(){
37589         if(this.collapsed){
37590             return this.collapsedEl.getBox();
37591         }
37592         var box = this.el.getBox();
37593         if(this.split){
37594             var sh = this.split.el.getHeight();
37595             box.height += sh;
37596             box.y -= sh;
37597         }
37598         return box;
37599     },
37600     
37601     updateBox : function(box){
37602         if(this.split && !this.collapsed){
37603             var sh = this.split.el.getHeight();
37604             box.height -= sh;
37605             box.y += sh;
37606             this.split.el.setLeft(box.x);
37607             this.split.el.setTop(box.y-sh);
37608             this.split.el.setWidth(box.width);
37609         }
37610         if(this.collapsed){
37611             this.updateBody(box.width, null);
37612         }
37613         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37614     }
37615 });
37616
37617 Roo.bootstrap.layout.East = function(config){
37618     config.region = "east";
37619     config.cursor = "e-resize";
37620     Roo.bootstrap.layout.Split.call(this, config);
37621     if(this.split){
37622         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37623         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37624         this.split.el.addClass("roo-layout-split-h");
37625     }
37626     var size = config.initialSize || config.width;
37627     if(typeof size != "undefined"){
37628         this.el.setWidth(size);
37629     }
37630 };
37631 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37632     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37633     getBox : function(){
37634         if(this.collapsed){
37635             return this.collapsedEl.getBox();
37636         }
37637         var box = this.el.getBox();
37638         if(this.split){
37639             var sw = this.split.el.getWidth();
37640             box.width += sw;
37641             box.x -= sw;
37642         }
37643         return box;
37644     },
37645
37646     updateBox : function(box){
37647         if(this.split && !this.collapsed){
37648             var sw = this.split.el.getWidth();
37649             box.width -= sw;
37650             this.split.el.setLeft(box.x);
37651             this.split.el.setTop(box.y);
37652             this.split.el.setHeight(box.height);
37653             box.x += sw;
37654         }
37655         if(this.collapsed){
37656             this.updateBody(null, box.height);
37657         }
37658         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37659     }
37660 });
37661
37662 Roo.bootstrap.layout.West = function(config){
37663     config.region = "west";
37664     config.cursor = "w-resize";
37665     
37666     Roo.bootstrap.layout.Split.call(this, config);
37667     if(this.split){
37668         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37669         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37670         this.split.el.addClass("roo-layout-split-h");
37671     }
37672     
37673 };
37674 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37675     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37676     
37677     onRender: function(ctr, pos)
37678     {
37679         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37680         var size = this.config.initialSize || this.config.width;
37681         if(typeof size != "undefined"){
37682             this.el.setWidth(size);
37683         }
37684     },
37685     
37686     getBox : function(){
37687         if(this.collapsed){
37688             return this.collapsedEl.getBox();
37689         }
37690         var box = this.el.getBox();
37691         if(this.split){
37692             box.width += this.split.el.getWidth();
37693         }
37694         return box;
37695     },
37696     
37697     updateBox : function(box){
37698         if(this.split && !this.collapsed){
37699             var sw = this.split.el.getWidth();
37700             box.width -= sw;
37701             this.split.el.setLeft(box.x+box.width);
37702             this.split.el.setTop(box.y);
37703             this.split.el.setHeight(box.height);
37704         }
37705         if(this.collapsed){
37706             this.updateBody(null, box.height);
37707         }
37708         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37709     }
37710 });Roo.namespace("Roo.bootstrap.panel");/*
37711  * Based on:
37712  * Ext JS Library 1.1.1
37713  * Copyright(c) 2006-2007, Ext JS, LLC.
37714  *
37715  * Originally Released Under LGPL - original licence link has changed is not relivant.
37716  *
37717  * Fork - LGPL
37718  * <script type="text/javascript">
37719  */
37720 /**
37721  * @class Roo.ContentPanel
37722  * @extends Roo.util.Observable
37723  * A basic ContentPanel element.
37724  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37725  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37726  * @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
37727  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37728  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37729  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37730  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37731  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37732  * @cfg {String} title          The title for this panel
37733  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37734  * @cfg {String} url            Calls {@link #setUrl} with this value
37735  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37736  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37737  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37738  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37739  * @cfg {Boolean} badges render the badges
37740
37741  * @constructor
37742  * Create a new ContentPanel.
37743  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37744  * @param {String/Object} config A string to set only the title or a config object
37745  * @param {String} content (optional) Set the HTML content for this panel
37746  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37747  */
37748 Roo.bootstrap.panel.Content = function( config){
37749     
37750     this.tpl = config.tpl || false;
37751     
37752     var el = config.el;
37753     var content = config.content;
37754
37755     if(config.autoCreate){ // xtype is available if this is called from factory
37756         el = Roo.id();
37757     }
37758     this.el = Roo.get(el);
37759     if(!this.el && config && config.autoCreate){
37760         if(typeof config.autoCreate == "object"){
37761             if(!config.autoCreate.id){
37762                 config.autoCreate.id = config.id||el;
37763             }
37764             this.el = Roo.DomHelper.append(document.body,
37765                         config.autoCreate, true);
37766         }else{
37767             var elcfg =  {   tag: "div",
37768                             cls: "roo-layout-inactive-content",
37769                             id: config.id||el
37770                             };
37771             if (config.html) {
37772                 elcfg.html = config.html;
37773                 
37774             }
37775                         
37776             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37777         }
37778     } 
37779     this.closable = false;
37780     this.loaded = false;
37781     this.active = false;
37782    
37783       
37784     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37785         
37786         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37787         
37788         this.wrapEl = this.el; //this.el.wrap();
37789         var ti = [];
37790         if (config.toolbar.items) {
37791             ti = config.toolbar.items ;
37792             delete config.toolbar.items ;
37793         }
37794         
37795         var nitems = [];
37796         this.toolbar.render(this.wrapEl, 'before');
37797         for(var i =0;i < ti.length;i++) {
37798           //  Roo.log(['add child', items[i]]);
37799             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37800         }
37801         this.toolbar.items = nitems;
37802         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37803         delete config.toolbar;
37804         
37805     }
37806     /*
37807     // xtype created footer. - not sure if will work as we normally have to render first..
37808     if (this.footer && !this.footer.el && this.footer.xtype) {
37809         if (!this.wrapEl) {
37810             this.wrapEl = this.el.wrap();
37811         }
37812     
37813         this.footer.container = this.wrapEl.createChild();
37814          
37815         this.footer = Roo.factory(this.footer, Roo);
37816         
37817     }
37818     */
37819     
37820      if(typeof config == "string"){
37821         this.title = config;
37822     }else{
37823         Roo.apply(this, config);
37824     }
37825     
37826     if(this.resizeEl){
37827         this.resizeEl = Roo.get(this.resizeEl, true);
37828     }else{
37829         this.resizeEl = this.el;
37830     }
37831     // handle view.xtype
37832     
37833  
37834     
37835     
37836     this.addEvents({
37837         /**
37838          * @event activate
37839          * Fires when this panel is activated. 
37840          * @param {Roo.ContentPanel} this
37841          */
37842         "activate" : true,
37843         /**
37844          * @event deactivate
37845          * Fires when this panel is activated. 
37846          * @param {Roo.ContentPanel} this
37847          */
37848         "deactivate" : true,
37849
37850         /**
37851          * @event resize
37852          * Fires when this panel is resized if fitToFrame is true.
37853          * @param {Roo.ContentPanel} this
37854          * @param {Number} width The width after any component adjustments
37855          * @param {Number} height The height after any component adjustments
37856          */
37857         "resize" : true,
37858         
37859          /**
37860          * @event render
37861          * Fires when this tab is created
37862          * @param {Roo.ContentPanel} this
37863          */
37864         "render" : true
37865         
37866         
37867         
37868     });
37869     
37870
37871     
37872     
37873     if(this.autoScroll){
37874         this.resizeEl.setStyle("overflow", "auto");
37875     } else {
37876         // fix randome scrolling
37877         //this.el.on('scroll', function() {
37878         //    Roo.log('fix random scolling');
37879         //    this.scrollTo('top',0); 
37880         //});
37881     }
37882     content = content || this.content;
37883     if(content){
37884         this.setContent(content);
37885     }
37886     if(config && config.url){
37887         this.setUrl(this.url, this.params, this.loadOnce);
37888     }
37889     
37890     
37891     
37892     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37893     
37894     if (this.view && typeof(this.view.xtype) != 'undefined') {
37895         this.view.el = this.el.appendChild(document.createElement("div"));
37896         this.view = Roo.factory(this.view); 
37897         this.view.render  &&  this.view.render(false, '');  
37898     }
37899     
37900     
37901     this.fireEvent('render', this);
37902 };
37903
37904 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37905     
37906     tabTip : '',
37907     
37908     setRegion : function(region){
37909         this.region = region;
37910         this.setActiveClass(region && !this.background);
37911     },
37912     
37913     
37914     setActiveClass: function(state)
37915     {
37916         if(state){
37917            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37918            this.el.setStyle('position','relative');
37919         }else{
37920            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37921            this.el.setStyle('position', 'absolute');
37922         } 
37923     },
37924     
37925     /**
37926      * Returns the toolbar for this Panel if one was configured. 
37927      * @return {Roo.Toolbar} 
37928      */
37929     getToolbar : function(){
37930         return this.toolbar;
37931     },
37932     
37933     setActiveState : function(active)
37934     {
37935         this.active = active;
37936         this.setActiveClass(active);
37937         if(!active){
37938             if(this.fireEvent("deactivate", this) === false){
37939                 return false;
37940             }
37941             return true;
37942         }
37943         this.fireEvent("activate", this);
37944         return true;
37945     },
37946     /**
37947      * Updates this panel's element
37948      * @param {String} content The new content
37949      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37950     */
37951     setContent : function(content, loadScripts){
37952         this.el.update(content, loadScripts);
37953     },
37954
37955     ignoreResize : function(w, h){
37956         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37957             return true;
37958         }else{
37959             this.lastSize = {width: w, height: h};
37960             return false;
37961         }
37962     },
37963     /**
37964      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37965      * @return {Roo.UpdateManager} The UpdateManager
37966      */
37967     getUpdateManager : function(){
37968         return this.el.getUpdateManager();
37969     },
37970      /**
37971      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37972      * @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:
37973 <pre><code>
37974 panel.load({
37975     url: "your-url.php",
37976     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37977     callback: yourFunction,
37978     scope: yourObject, //(optional scope)
37979     discardUrl: false,
37980     nocache: false,
37981     text: "Loading...",
37982     timeout: 30,
37983     scripts: false
37984 });
37985 </code></pre>
37986      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37987      * 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.
37988      * @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}
37989      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37990      * @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.
37991      * @return {Roo.ContentPanel} this
37992      */
37993     load : function(){
37994         var um = this.el.getUpdateManager();
37995         um.update.apply(um, arguments);
37996         return this;
37997     },
37998
37999
38000     /**
38001      * 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.
38002      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38003      * @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)
38004      * @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)
38005      * @return {Roo.UpdateManager} The UpdateManager
38006      */
38007     setUrl : function(url, params, loadOnce){
38008         if(this.refreshDelegate){
38009             this.removeListener("activate", this.refreshDelegate);
38010         }
38011         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38012         this.on("activate", this.refreshDelegate);
38013         return this.el.getUpdateManager();
38014     },
38015     
38016     _handleRefresh : function(url, params, loadOnce){
38017         if(!loadOnce || !this.loaded){
38018             var updater = this.el.getUpdateManager();
38019             updater.update(url, params, this._setLoaded.createDelegate(this));
38020         }
38021     },
38022     
38023     _setLoaded : function(){
38024         this.loaded = true;
38025     }, 
38026     
38027     /**
38028      * Returns this panel's id
38029      * @return {String} 
38030      */
38031     getId : function(){
38032         return this.el.id;
38033     },
38034     
38035     /** 
38036      * Returns this panel's element - used by regiosn to add.
38037      * @return {Roo.Element} 
38038      */
38039     getEl : function(){
38040         return this.wrapEl || this.el;
38041     },
38042     
38043    
38044     
38045     adjustForComponents : function(width, height)
38046     {
38047         //Roo.log('adjustForComponents ');
38048         if(this.resizeEl != this.el){
38049             width -= this.el.getFrameWidth('lr');
38050             height -= this.el.getFrameWidth('tb');
38051         }
38052         if(this.toolbar){
38053             var te = this.toolbar.getEl();
38054             te.setWidth(width);
38055             height -= te.getHeight();
38056         }
38057         if(this.footer){
38058             var te = this.footer.getEl();
38059             te.setWidth(width);
38060             height -= te.getHeight();
38061         }
38062         
38063         
38064         if(this.adjustments){
38065             width += this.adjustments[0];
38066             height += this.adjustments[1];
38067         }
38068         return {"width": width, "height": height};
38069     },
38070     
38071     setSize : function(width, height){
38072         if(this.fitToFrame && !this.ignoreResize(width, height)){
38073             if(this.fitContainer && this.resizeEl != this.el){
38074                 this.el.setSize(width, height);
38075             }
38076             var size = this.adjustForComponents(width, height);
38077             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38078             this.fireEvent('resize', this, size.width, size.height);
38079         }
38080     },
38081     
38082     /**
38083      * Returns this panel's title
38084      * @return {String} 
38085      */
38086     getTitle : function(){
38087         
38088         if (typeof(this.title) != 'object') {
38089             return this.title;
38090         }
38091         
38092         var t = '';
38093         for (var k in this.title) {
38094             if (!this.title.hasOwnProperty(k)) {
38095                 continue;
38096             }
38097             
38098             if (k.indexOf('-') >= 0) {
38099                 var s = k.split('-');
38100                 for (var i = 0; i<s.length; i++) {
38101                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38102                 }
38103             } else {
38104                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38105             }
38106         }
38107         return t;
38108     },
38109     
38110     /**
38111      * Set this panel's title
38112      * @param {String} title
38113      */
38114     setTitle : function(title){
38115         this.title = title;
38116         if(this.region){
38117             this.region.updatePanelTitle(this, title);
38118         }
38119     },
38120     
38121     /**
38122      * Returns true is this panel was configured to be closable
38123      * @return {Boolean} 
38124      */
38125     isClosable : function(){
38126         return this.closable;
38127     },
38128     
38129     beforeSlide : function(){
38130         this.el.clip();
38131         this.resizeEl.clip();
38132     },
38133     
38134     afterSlide : function(){
38135         this.el.unclip();
38136         this.resizeEl.unclip();
38137     },
38138     
38139     /**
38140      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38141      *   Will fail silently if the {@link #setUrl} method has not been called.
38142      *   This does not activate the panel, just updates its content.
38143      */
38144     refresh : function(){
38145         if(this.refreshDelegate){
38146            this.loaded = false;
38147            this.refreshDelegate();
38148         }
38149     },
38150     
38151     /**
38152      * Destroys this panel
38153      */
38154     destroy : function(){
38155         this.el.removeAllListeners();
38156         var tempEl = document.createElement("span");
38157         tempEl.appendChild(this.el.dom);
38158         tempEl.innerHTML = "";
38159         this.el.remove();
38160         this.el = null;
38161     },
38162     
38163     /**
38164      * form - if the content panel contains a form - this is a reference to it.
38165      * @type {Roo.form.Form}
38166      */
38167     form : false,
38168     /**
38169      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38170      *    This contains a reference to it.
38171      * @type {Roo.View}
38172      */
38173     view : false,
38174     
38175       /**
38176      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38177      * <pre><code>
38178
38179 layout.addxtype({
38180        xtype : 'Form',
38181        items: [ .... ]
38182    }
38183 );
38184
38185 </code></pre>
38186      * @param {Object} cfg Xtype definition of item to add.
38187      */
38188     
38189     
38190     getChildContainer: function () {
38191         return this.getEl();
38192     }
38193     
38194     
38195     /*
38196         var  ret = new Roo.factory(cfg);
38197         return ret;
38198         
38199         
38200         // add form..
38201         if (cfg.xtype.match(/^Form$/)) {
38202             
38203             var el;
38204             //if (this.footer) {
38205             //    el = this.footer.container.insertSibling(false, 'before');
38206             //} else {
38207                 el = this.el.createChild();
38208             //}
38209
38210             this.form = new  Roo.form.Form(cfg);
38211             
38212             
38213             if ( this.form.allItems.length) {
38214                 this.form.render(el.dom);
38215             }
38216             return this.form;
38217         }
38218         // should only have one of theses..
38219         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38220             // views.. should not be just added - used named prop 'view''
38221             
38222             cfg.el = this.el.appendChild(document.createElement("div"));
38223             // factory?
38224             
38225             var ret = new Roo.factory(cfg);
38226              
38227              ret.render && ret.render(false, ''); // render blank..
38228             this.view = ret;
38229             return ret;
38230         }
38231         return false;
38232     }
38233     \*/
38234 });
38235  
38236 /**
38237  * @class Roo.bootstrap.panel.Grid
38238  * @extends Roo.bootstrap.panel.Content
38239  * @constructor
38240  * Create a new GridPanel.
38241  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38242  * @param {Object} config A the config object
38243   
38244  */
38245
38246
38247
38248 Roo.bootstrap.panel.Grid = function(config)
38249 {
38250     
38251       
38252     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38253         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38254
38255     config.el = this.wrapper;
38256     //this.el = this.wrapper;
38257     
38258       if (config.container) {
38259         // ctor'ed from a Border/panel.grid
38260         
38261         
38262         this.wrapper.setStyle("overflow", "hidden");
38263         this.wrapper.addClass('roo-grid-container');
38264
38265     }
38266     
38267     
38268     if(config.toolbar){
38269         var tool_el = this.wrapper.createChild();    
38270         this.toolbar = Roo.factory(config.toolbar);
38271         var ti = [];
38272         if (config.toolbar.items) {
38273             ti = config.toolbar.items ;
38274             delete config.toolbar.items ;
38275         }
38276         
38277         var nitems = [];
38278         this.toolbar.render(tool_el);
38279         for(var i =0;i < ti.length;i++) {
38280           //  Roo.log(['add child', items[i]]);
38281             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38282         }
38283         this.toolbar.items = nitems;
38284         
38285         delete config.toolbar;
38286     }
38287     
38288     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38289     config.grid.scrollBody = true;;
38290     config.grid.monitorWindowResize = false; // turn off autosizing
38291     config.grid.autoHeight = false;
38292     config.grid.autoWidth = false;
38293     
38294     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38295     
38296     if (config.background) {
38297         // render grid on panel activation (if panel background)
38298         this.on('activate', function(gp) {
38299             if (!gp.grid.rendered) {
38300                 gp.grid.render(this.wrapper);
38301                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38302             }
38303         });
38304             
38305     } else {
38306         this.grid.render(this.wrapper);
38307         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38308
38309     }
38310     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38311     // ??? needed ??? config.el = this.wrapper;
38312     
38313     
38314     
38315   
38316     // xtype created footer. - not sure if will work as we normally have to render first..
38317     if (this.footer && !this.footer.el && this.footer.xtype) {
38318         
38319         var ctr = this.grid.getView().getFooterPanel(true);
38320         this.footer.dataSource = this.grid.dataSource;
38321         this.footer = Roo.factory(this.footer, Roo);
38322         this.footer.render(ctr);
38323         
38324     }
38325     
38326     
38327     
38328     
38329      
38330 };
38331
38332 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38333     getId : function(){
38334         return this.grid.id;
38335     },
38336     
38337     /**
38338      * Returns the grid for this panel
38339      * @return {Roo.bootstrap.Table} 
38340      */
38341     getGrid : function(){
38342         return this.grid;    
38343     },
38344     
38345     setSize : function(width, height){
38346         if(!this.ignoreResize(width, height)){
38347             var grid = this.grid;
38348             var size = this.adjustForComponents(width, height);
38349             var gridel = grid.getGridEl();
38350             gridel.setSize(size.width, size.height);
38351             /*
38352             var thd = grid.getGridEl().select('thead',true).first();
38353             var tbd = grid.getGridEl().select('tbody', true).first();
38354             if (tbd) {
38355                 tbd.setSize(width, height - thd.getHeight());
38356             }
38357             */
38358             grid.autoSize();
38359         }
38360     },
38361      
38362     
38363     
38364     beforeSlide : function(){
38365         this.grid.getView().scroller.clip();
38366     },
38367     
38368     afterSlide : function(){
38369         this.grid.getView().scroller.unclip();
38370     },
38371     
38372     destroy : function(){
38373         this.grid.destroy();
38374         delete this.grid;
38375         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38376     }
38377 });
38378
38379 /**
38380  * @class Roo.bootstrap.panel.Nest
38381  * @extends Roo.bootstrap.panel.Content
38382  * @constructor
38383  * Create a new Panel, that can contain a layout.Border.
38384  * 
38385  * 
38386  * @param {Roo.BorderLayout} layout The layout for this panel
38387  * @param {String/Object} config A string to set only the title or a config object
38388  */
38389 Roo.bootstrap.panel.Nest = function(config)
38390 {
38391     // construct with only one argument..
38392     /* FIXME - implement nicer consturctors
38393     if (layout.layout) {
38394         config = layout;
38395         layout = config.layout;
38396         delete config.layout;
38397     }
38398     if (layout.xtype && !layout.getEl) {
38399         // then layout needs constructing..
38400         layout = Roo.factory(layout, Roo);
38401     }
38402     */
38403     
38404     config.el =  config.layout.getEl();
38405     
38406     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38407     
38408     config.layout.monitorWindowResize = false; // turn off autosizing
38409     this.layout = config.layout;
38410     this.layout.getEl().addClass("roo-layout-nested-layout");
38411     this.layout.parent = this;
38412     
38413     
38414     
38415     
38416 };
38417
38418 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38419
38420     setSize : function(width, height){
38421         if(!this.ignoreResize(width, height)){
38422             var size = this.adjustForComponents(width, height);
38423             var el = this.layout.getEl();
38424             if (size.height < 1) {
38425                 el.setWidth(size.width);   
38426             } else {
38427                 el.setSize(size.width, size.height);
38428             }
38429             var touch = el.dom.offsetWidth;
38430             this.layout.layout();
38431             // ie requires a double layout on the first pass
38432             if(Roo.isIE && !this.initialized){
38433                 this.initialized = true;
38434                 this.layout.layout();
38435             }
38436         }
38437     },
38438     
38439     // activate all subpanels if not currently active..
38440     
38441     setActiveState : function(active){
38442         this.active = active;
38443         this.setActiveClass(active);
38444         
38445         if(!active){
38446             this.fireEvent("deactivate", this);
38447             return;
38448         }
38449         
38450         this.fireEvent("activate", this);
38451         // not sure if this should happen before or after..
38452         if (!this.layout) {
38453             return; // should not happen..
38454         }
38455         var reg = false;
38456         for (var r in this.layout.regions) {
38457             reg = this.layout.getRegion(r);
38458             if (reg.getActivePanel()) {
38459                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38460                 reg.setActivePanel(reg.getActivePanel());
38461                 continue;
38462             }
38463             if (!reg.panels.length) {
38464                 continue;
38465             }
38466             reg.showPanel(reg.getPanel(0));
38467         }
38468         
38469         
38470         
38471         
38472     },
38473     
38474     /**
38475      * Returns the nested BorderLayout for this panel
38476      * @return {Roo.BorderLayout} 
38477      */
38478     getLayout : function(){
38479         return this.layout;
38480     },
38481     
38482      /**
38483      * Adds a xtype elements to the layout of the nested panel
38484      * <pre><code>
38485
38486 panel.addxtype({
38487        xtype : 'ContentPanel',
38488        region: 'west',
38489        items: [ .... ]
38490    }
38491 );
38492
38493 panel.addxtype({
38494         xtype : 'NestedLayoutPanel',
38495         region: 'west',
38496         layout: {
38497            center: { },
38498            west: { }   
38499         },
38500         items : [ ... list of content panels or nested layout panels.. ]
38501    }
38502 );
38503 </code></pre>
38504      * @param {Object} cfg Xtype definition of item to add.
38505      */
38506     addxtype : function(cfg) {
38507         return this.layout.addxtype(cfg);
38508     
38509     }
38510 });/*
38511  * Based on:
38512  * Ext JS Library 1.1.1
38513  * Copyright(c) 2006-2007, Ext JS, LLC.
38514  *
38515  * Originally Released Under LGPL - original licence link has changed is not relivant.
38516  *
38517  * Fork - LGPL
38518  * <script type="text/javascript">
38519  */
38520 /**
38521  * @class Roo.TabPanel
38522  * @extends Roo.util.Observable
38523  * A lightweight tab container.
38524  * <br><br>
38525  * Usage:
38526  * <pre><code>
38527 // basic tabs 1, built from existing content
38528 var tabs = new Roo.TabPanel("tabs1");
38529 tabs.addTab("script", "View Script");
38530 tabs.addTab("markup", "View Markup");
38531 tabs.activate("script");
38532
38533 // more advanced tabs, built from javascript
38534 var jtabs = new Roo.TabPanel("jtabs");
38535 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38536
38537 // set up the UpdateManager
38538 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38539 var updater = tab2.getUpdateManager();
38540 updater.setDefaultUrl("ajax1.htm");
38541 tab2.on('activate', updater.refresh, updater, true);
38542
38543 // Use setUrl for Ajax loading
38544 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38545 tab3.setUrl("ajax2.htm", null, true);
38546
38547 // Disabled tab
38548 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38549 tab4.disable();
38550
38551 jtabs.activate("jtabs-1");
38552  * </code></pre>
38553  * @constructor
38554  * Create a new TabPanel.
38555  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38556  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38557  */
38558 Roo.bootstrap.panel.Tabs = function(config){
38559     /**
38560     * The container element for this TabPanel.
38561     * @type Roo.Element
38562     */
38563     this.el = Roo.get(config.el);
38564     delete config.el;
38565     if(config){
38566         if(typeof config == "boolean"){
38567             this.tabPosition = config ? "bottom" : "top";
38568         }else{
38569             Roo.apply(this, config);
38570         }
38571     }
38572     
38573     if(this.tabPosition == "bottom"){
38574         // if tabs are at the bottom = create the body first.
38575         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38576         this.el.addClass("roo-tabs-bottom");
38577     }
38578     // next create the tabs holders
38579     
38580     if (this.tabPosition == "west"){
38581         
38582         var reg = this.region; // fake it..
38583         while (reg) {
38584             if (!reg.mgr.parent) {
38585                 break;
38586             }
38587             reg = reg.mgr.parent.region;
38588         }
38589         Roo.log("got nest?");
38590         Roo.log(reg);
38591         if (reg.mgr.getRegion('west')) {
38592             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38593             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38594             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38595             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38596             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38597         
38598             
38599         }
38600         
38601         
38602     } else {
38603      
38604         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38605         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38606         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38607         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38608     }
38609     
38610     
38611     if(Roo.isIE){
38612         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38613     }
38614     
38615     // finally - if tabs are at the top, then create the body last..
38616     if(this.tabPosition != "bottom"){
38617         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38618          * @type Roo.Element
38619          */
38620         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38621         this.el.addClass("roo-tabs-top");
38622     }
38623     this.items = [];
38624
38625     this.bodyEl.setStyle("position", "relative");
38626
38627     this.active = null;
38628     this.activateDelegate = this.activate.createDelegate(this);
38629
38630     this.addEvents({
38631         /**
38632          * @event tabchange
38633          * Fires when the active tab changes
38634          * @param {Roo.TabPanel} this
38635          * @param {Roo.TabPanelItem} activePanel The new active tab
38636          */
38637         "tabchange": true,
38638         /**
38639          * @event beforetabchange
38640          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38641          * @param {Roo.TabPanel} this
38642          * @param {Object} e Set cancel to true on this object to cancel the tab change
38643          * @param {Roo.TabPanelItem} tab The tab being changed to
38644          */
38645         "beforetabchange" : true
38646     });
38647
38648     Roo.EventManager.onWindowResize(this.onResize, this);
38649     this.cpad = this.el.getPadding("lr");
38650     this.hiddenCount = 0;
38651
38652
38653     // toolbar on the tabbar support...
38654     if (this.toolbar) {
38655         alert("no toolbar support yet");
38656         this.toolbar  = false;
38657         /*
38658         var tcfg = this.toolbar;
38659         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38660         this.toolbar = new Roo.Toolbar(tcfg);
38661         if (Roo.isSafari) {
38662             var tbl = tcfg.container.child('table', true);
38663             tbl.setAttribute('width', '100%');
38664         }
38665         */
38666         
38667     }
38668    
38669
38670
38671     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38672 };
38673
38674 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38675     /*
38676      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38677      */
38678     tabPosition : "top",
38679     /*
38680      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38681      */
38682     currentTabWidth : 0,
38683     /*
38684      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38685      */
38686     minTabWidth : 40,
38687     /*
38688      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38689      */
38690     maxTabWidth : 250,
38691     /*
38692      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38693      */
38694     preferredTabWidth : 175,
38695     /*
38696      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38697      */
38698     resizeTabs : false,
38699     /*
38700      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38701      */
38702     monitorResize : true,
38703     /*
38704      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38705      */
38706     toolbar : false,  // set by caller..
38707     
38708     region : false, /// set by caller
38709     
38710     disableTooltips : true, // not used yet...
38711
38712     /**
38713      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38714      * @param {String} id The id of the div to use <b>or create</b>
38715      * @param {String} text The text for the tab
38716      * @param {String} content (optional) Content to put in the TabPanelItem body
38717      * @param {Boolean} closable (optional) True to create a close icon on the tab
38718      * @return {Roo.TabPanelItem} The created TabPanelItem
38719      */
38720     addTab : function(id, text, content, closable, tpl)
38721     {
38722         var item = new Roo.bootstrap.panel.TabItem({
38723             panel: this,
38724             id : id,
38725             text : text,
38726             closable : closable,
38727             tpl : tpl
38728         });
38729         this.addTabItem(item);
38730         if(content){
38731             item.setContent(content);
38732         }
38733         return item;
38734     },
38735
38736     /**
38737      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38738      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38739      * @return {Roo.TabPanelItem}
38740      */
38741     getTab : function(id){
38742         return this.items[id];
38743     },
38744
38745     /**
38746      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38747      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38748      */
38749     hideTab : function(id){
38750         var t = this.items[id];
38751         if(!t.isHidden()){
38752            t.setHidden(true);
38753            this.hiddenCount++;
38754            this.autoSizeTabs();
38755         }
38756     },
38757
38758     /**
38759      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38760      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38761      */
38762     unhideTab : function(id){
38763         var t = this.items[id];
38764         if(t.isHidden()){
38765            t.setHidden(false);
38766            this.hiddenCount--;
38767            this.autoSizeTabs();
38768         }
38769     },
38770
38771     /**
38772      * Adds an existing {@link Roo.TabPanelItem}.
38773      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38774      */
38775     addTabItem : function(item)
38776     {
38777         this.items[item.id] = item;
38778         this.items.push(item);
38779         this.autoSizeTabs();
38780       //  if(this.resizeTabs){
38781     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38782   //         this.autoSizeTabs();
38783 //        }else{
38784 //            item.autoSize();
38785        // }
38786     },
38787
38788     /**
38789      * Removes a {@link Roo.TabPanelItem}.
38790      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38791      */
38792     removeTab : function(id){
38793         var items = this.items;
38794         var tab = items[id];
38795         if(!tab) { return; }
38796         var index = items.indexOf(tab);
38797         if(this.active == tab && items.length > 1){
38798             var newTab = this.getNextAvailable(index);
38799             if(newTab) {
38800                 newTab.activate();
38801             }
38802         }
38803         this.stripEl.dom.removeChild(tab.pnode.dom);
38804         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38805             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38806         }
38807         items.splice(index, 1);
38808         delete this.items[tab.id];
38809         tab.fireEvent("close", tab);
38810         tab.purgeListeners();
38811         this.autoSizeTabs();
38812     },
38813
38814     getNextAvailable : function(start){
38815         var items = this.items;
38816         var index = start;
38817         // look for a next tab that will slide over to
38818         // replace the one being removed
38819         while(index < items.length){
38820             var item = items[++index];
38821             if(item && !item.isHidden()){
38822                 return item;
38823             }
38824         }
38825         // if one isn't found select the previous tab (on the left)
38826         index = start;
38827         while(index >= 0){
38828             var item = items[--index];
38829             if(item && !item.isHidden()){
38830                 return item;
38831             }
38832         }
38833         return null;
38834     },
38835
38836     /**
38837      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38838      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38839      */
38840     disableTab : function(id){
38841         var tab = this.items[id];
38842         if(tab && this.active != tab){
38843             tab.disable();
38844         }
38845     },
38846
38847     /**
38848      * Enables a {@link Roo.TabPanelItem} that is disabled.
38849      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38850      */
38851     enableTab : function(id){
38852         var tab = this.items[id];
38853         tab.enable();
38854     },
38855
38856     /**
38857      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38858      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38859      * @return {Roo.TabPanelItem} The TabPanelItem.
38860      */
38861     activate : function(id)
38862     {
38863         //Roo.log('activite:'  + id);
38864         
38865         var tab = this.items[id];
38866         if(!tab){
38867             return null;
38868         }
38869         if(tab == this.active || tab.disabled){
38870             return tab;
38871         }
38872         var e = {};
38873         this.fireEvent("beforetabchange", this, e, tab);
38874         if(e.cancel !== true && !tab.disabled){
38875             if(this.active){
38876                 this.active.hide();
38877             }
38878             this.active = this.items[id];
38879             this.active.show();
38880             this.fireEvent("tabchange", this, this.active);
38881         }
38882         return tab;
38883     },
38884
38885     /**
38886      * Gets the active {@link Roo.TabPanelItem}.
38887      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38888      */
38889     getActiveTab : function(){
38890         return this.active;
38891     },
38892
38893     /**
38894      * Updates the tab body element to fit the height of the container element
38895      * for overflow scrolling
38896      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38897      */
38898     syncHeight : function(targetHeight){
38899         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38900         var bm = this.bodyEl.getMargins();
38901         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38902         this.bodyEl.setHeight(newHeight);
38903         return newHeight;
38904     },
38905
38906     onResize : function(){
38907         if(this.monitorResize){
38908             this.autoSizeTabs();
38909         }
38910     },
38911
38912     /**
38913      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38914      */
38915     beginUpdate : function(){
38916         this.updating = true;
38917     },
38918
38919     /**
38920      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38921      */
38922     endUpdate : function(){
38923         this.updating = false;
38924         this.autoSizeTabs();
38925     },
38926
38927     /**
38928      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38929      */
38930     autoSizeTabs : function()
38931     {
38932         var count = this.items.length;
38933         var vcount = count - this.hiddenCount;
38934         
38935         if (vcount < 2) {
38936             this.stripEl.hide();
38937         } else {
38938             this.stripEl.show();
38939         }
38940         
38941         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38942             return;
38943         }
38944         
38945         
38946         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38947         var availWidth = Math.floor(w / vcount);
38948         var b = this.stripBody;
38949         if(b.getWidth() > w){
38950             var tabs = this.items;
38951             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38952             if(availWidth < this.minTabWidth){
38953                 /*if(!this.sleft){    // incomplete scrolling code
38954                     this.createScrollButtons();
38955                 }
38956                 this.showScroll();
38957                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38958             }
38959         }else{
38960             if(this.currentTabWidth < this.preferredTabWidth){
38961                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38962             }
38963         }
38964     },
38965
38966     /**
38967      * Returns the number of tabs in this TabPanel.
38968      * @return {Number}
38969      */
38970      getCount : function(){
38971          return this.items.length;
38972      },
38973
38974     /**
38975      * Resizes all the tabs to the passed width
38976      * @param {Number} The new width
38977      */
38978     setTabWidth : function(width){
38979         this.currentTabWidth = width;
38980         for(var i = 0, len = this.items.length; i < len; i++) {
38981                 if(!this.items[i].isHidden()) {
38982                 this.items[i].setWidth(width);
38983             }
38984         }
38985     },
38986
38987     /**
38988      * Destroys this TabPanel
38989      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38990      */
38991     destroy : function(removeEl){
38992         Roo.EventManager.removeResizeListener(this.onResize, this);
38993         for(var i = 0, len = this.items.length; i < len; i++){
38994             this.items[i].purgeListeners();
38995         }
38996         if(removeEl === true){
38997             this.el.update("");
38998             this.el.remove();
38999         }
39000     },
39001     
39002     createStrip : function(container)
39003     {
39004         var strip = document.createElement("nav");
39005         strip.className = Roo.bootstrap.version == 4 ?
39006             "navbar-light bg-light" : 
39007             "navbar navbar-default"; //"x-tabs-wrap";
39008         container.appendChild(strip);
39009         return strip;
39010     },
39011     
39012     createStripList : function(strip)
39013     {
39014         // div wrapper for retard IE
39015         // returns the "tr" element.
39016         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39017         //'<div class="x-tabs-strip-wrap">'+
39018           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39019           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39020         return strip.firstChild; //.firstChild.firstChild.firstChild;
39021     },
39022     createBody : function(container)
39023     {
39024         var body = document.createElement("div");
39025         Roo.id(body, "tab-body");
39026         //Roo.fly(body).addClass("x-tabs-body");
39027         Roo.fly(body).addClass("tab-content");
39028         container.appendChild(body);
39029         return body;
39030     },
39031     createItemBody :function(bodyEl, id){
39032         var body = Roo.getDom(id);
39033         if(!body){
39034             body = document.createElement("div");
39035             body.id = id;
39036         }
39037         //Roo.fly(body).addClass("x-tabs-item-body");
39038         Roo.fly(body).addClass("tab-pane");
39039          bodyEl.insertBefore(body, bodyEl.firstChild);
39040         return body;
39041     },
39042     /** @private */
39043     createStripElements :  function(stripEl, text, closable, tpl)
39044     {
39045         var td = document.createElement("li"); // was td..
39046         td.className = 'nav-item';
39047         
39048         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39049         
39050         
39051         stripEl.appendChild(td);
39052         /*if(closable){
39053             td.className = "x-tabs-closable";
39054             if(!this.closeTpl){
39055                 this.closeTpl = new Roo.Template(
39056                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39057                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39058                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39059                 );
39060             }
39061             var el = this.closeTpl.overwrite(td, {"text": text});
39062             var close = el.getElementsByTagName("div")[0];
39063             var inner = el.getElementsByTagName("em")[0];
39064             return {"el": el, "close": close, "inner": inner};
39065         } else {
39066         */
39067         // not sure what this is..
39068 //            if(!this.tabTpl){
39069                 //this.tabTpl = new Roo.Template(
39070                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39071                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39072                 //);
39073 //                this.tabTpl = new Roo.Template(
39074 //                   '<a href="#">' +
39075 //                   '<span unselectable="on"' +
39076 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39077 //                            ' >{text}</span></a>'
39078 //                );
39079 //                
39080 //            }
39081
39082
39083             var template = tpl || this.tabTpl || false;
39084             
39085             if(!template){
39086                 template =  new Roo.Template(
39087                         Roo.bootstrap.version == 4 ? 
39088                             (
39089                                 '<a class="nav-link" href="#" unselectable="on"' +
39090                                      (this.disableTooltips ? '' : ' title="{text}"') +
39091                                      ' >{text}</a>'
39092                             ) : (
39093                                 '<a class="nav-link" href="#">' +
39094                                 '<span unselectable="on"' +
39095                                          (this.disableTooltips ? '' : ' title="{text}"') +
39096                                     ' >{text}</span></a>'
39097                             )
39098                 );
39099             }
39100             
39101             switch (typeof(template)) {
39102                 case 'object' :
39103                     break;
39104                 case 'string' :
39105                     template = new Roo.Template(template);
39106                     break;
39107                 default :
39108                     break;
39109             }
39110             
39111             var el = template.overwrite(td, {"text": text});
39112             
39113             var inner = el.getElementsByTagName("span")[0];
39114             
39115             return {"el": el, "inner": inner};
39116             
39117     }
39118         
39119     
39120 });
39121
39122 /**
39123  * @class Roo.TabPanelItem
39124  * @extends Roo.util.Observable
39125  * Represents an individual item (tab plus body) in a TabPanel.
39126  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39127  * @param {String} id The id of this TabPanelItem
39128  * @param {String} text The text for the tab of this TabPanelItem
39129  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39130  */
39131 Roo.bootstrap.panel.TabItem = function(config){
39132     /**
39133      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39134      * @type Roo.TabPanel
39135      */
39136     this.tabPanel = config.panel;
39137     /**
39138      * The id for this TabPanelItem
39139      * @type String
39140      */
39141     this.id = config.id;
39142     /** @private */
39143     this.disabled = false;
39144     /** @private */
39145     this.text = config.text;
39146     /** @private */
39147     this.loaded = false;
39148     this.closable = config.closable;
39149
39150     /**
39151      * The body element for this TabPanelItem.
39152      * @type Roo.Element
39153      */
39154     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39155     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39156     this.bodyEl.setStyle("display", "block");
39157     this.bodyEl.setStyle("zoom", "1");
39158     //this.hideAction();
39159
39160     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39161     /** @private */
39162     this.el = Roo.get(els.el);
39163     this.inner = Roo.get(els.inner, true);
39164      this.textEl = Roo.bootstrap.version == 4 ?
39165         this.el : Roo.get(this.el.dom.firstChild, true);
39166
39167     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39168     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39169
39170     
39171 //    this.el.on("mousedown", this.onTabMouseDown, this);
39172     this.el.on("click", this.onTabClick, this);
39173     /** @private */
39174     if(config.closable){
39175         var c = Roo.get(els.close, true);
39176         c.dom.title = this.closeText;
39177         c.addClassOnOver("close-over");
39178         c.on("click", this.closeClick, this);
39179      }
39180
39181     this.addEvents({
39182          /**
39183          * @event activate
39184          * Fires when this tab becomes the active tab.
39185          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39186          * @param {Roo.TabPanelItem} this
39187          */
39188         "activate": true,
39189         /**
39190          * @event beforeclose
39191          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39192          * @param {Roo.TabPanelItem} this
39193          * @param {Object} e Set cancel to true on this object to cancel the close.
39194          */
39195         "beforeclose": true,
39196         /**
39197          * @event close
39198          * Fires when this tab is closed.
39199          * @param {Roo.TabPanelItem} this
39200          */
39201          "close": true,
39202         /**
39203          * @event deactivate
39204          * Fires when this tab is no longer the active tab.
39205          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39206          * @param {Roo.TabPanelItem} this
39207          */
39208          "deactivate" : true
39209     });
39210     this.hidden = false;
39211
39212     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39213 };
39214
39215 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39216            {
39217     purgeListeners : function(){
39218        Roo.util.Observable.prototype.purgeListeners.call(this);
39219        this.el.removeAllListeners();
39220     },
39221     /**
39222      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39223      */
39224     show : function(){
39225         this.status_node.addClass("active");
39226         this.showAction();
39227         if(Roo.isOpera){
39228             this.tabPanel.stripWrap.repaint();
39229         }
39230         this.fireEvent("activate", this.tabPanel, this);
39231     },
39232
39233     /**
39234      * Returns true if this tab is the active tab.
39235      * @return {Boolean}
39236      */
39237     isActive : function(){
39238         return this.tabPanel.getActiveTab() == this;
39239     },
39240
39241     /**
39242      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39243      */
39244     hide : function(){
39245         this.status_node.removeClass("active");
39246         this.hideAction();
39247         this.fireEvent("deactivate", this.tabPanel, this);
39248     },
39249
39250     hideAction : function(){
39251         this.bodyEl.hide();
39252         this.bodyEl.setStyle("position", "absolute");
39253         this.bodyEl.setLeft("-20000px");
39254         this.bodyEl.setTop("-20000px");
39255     },
39256
39257     showAction : function(){
39258         this.bodyEl.setStyle("position", "relative");
39259         this.bodyEl.setTop("");
39260         this.bodyEl.setLeft("");
39261         this.bodyEl.show();
39262     },
39263
39264     /**
39265      * Set the tooltip for the tab.
39266      * @param {String} tooltip The tab's tooltip
39267      */
39268     setTooltip : function(text){
39269         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39270             this.textEl.dom.qtip = text;
39271             this.textEl.dom.removeAttribute('title');
39272         }else{
39273             this.textEl.dom.title = text;
39274         }
39275     },
39276
39277     onTabClick : function(e){
39278         e.preventDefault();
39279         this.tabPanel.activate(this.id);
39280     },
39281
39282     onTabMouseDown : function(e){
39283         e.preventDefault();
39284         this.tabPanel.activate(this.id);
39285     },
39286 /*
39287     getWidth : function(){
39288         return this.inner.getWidth();
39289     },
39290
39291     setWidth : function(width){
39292         var iwidth = width - this.linode.getPadding("lr");
39293         this.inner.setWidth(iwidth);
39294         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39295         this.linode.setWidth(width);
39296     },
39297 */
39298     /**
39299      * Show or hide the tab
39300      * @param {Boolean} hidden True to hide or false to show.
39301      */
39302     setHidden : function(hidden){
39303         this.hidden = hidden;
39304         this.linode.setStyle("display", hidden ? "none" : "");
39305     },
39306
39307     /**
39308      * Returns true if this tab is "hidden"
39309      * @return {Boolean}
39310      */
39311     isHidden : function(){
39312         return this.hidden;
39313     },
39314
39315     /**
39316      * Returns the text for this tab
39317      * @return {String}
39318      */
39319     getText : function(){
39320         return this.text;
39321     },
39322     /*
39323     autoSize : function(){
39324         //this.el.beginMeasure();
39325         this.textEl.setWidth(1);
39326         /*
39327          *  #2804 [new] Tabs in Roojs
39328          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39329          */
39330         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39331         //this.el.endMeasure();
39332     //},
39333
39334     /**
39335      * Sets the text for the tab (Note: this also sets the tooltip text)
39336      * @param {String} text The tab's text and tooltip
39337      */
39338     setText : function(text){
39339         this.text = text;
39340         this.textEl.update(text);
39341         this.setTooltip(text);
39342         //if(!this.tabPanel.resizeTabs){
39343         //    this.autoSize();
39344         //}
39345     },
39346     /**
39347      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39348      */
39349     activate : function(){
39350         this.tabPanel.activate(this.id);
39351     },
39352
39353     /**
39354      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39355      */
39356     disable : function(){
39357         if(this.tabPanel.active != this){
39358             this.disabled = true;
39359             this.status_node.addClass("disabled");
39360         }
39361     },
39362
39363     /**
39364      * Enables this TabPanelItem if it was previously disabled.
39365      */
39366     enable : function(){
39367         this.disabled = false;
39368         this.status_node.removeClass("disabled");
39369     },
39370
39371     /**
39372      * Sets the content for this TabPanelItem.
39373      * @param {String} content The content
39374      * @param {Boolean} loadScripts true to look for and load scripts
39375      */
39376     setContent : function(content, loadScripts){
39377         this.bodyEl.update(content, loadScripts);
39378     },
39379
39380     /**
39381      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39382      * @return {Roo.UpdateManager} The UpdateManager
39383      */
39384     getUpdateManager : function(){
39385         return this.bodyEl.getUpdateManager();
39386     },
39387
39388     /**
39389      * Set a URL to be used to load the content for this TabPanelItem.
39390      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39391      * @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)
39392      * @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)
39393      * @return {Roo.UpdateManager} The UpdateManager
39394      */
39395     setUrl : function(url, params, loadOnce){
39396         if(this.refreshDelegate){
39397             this.un('activate', this.refreshDelegate);
39398         }
39399         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39400         this.on("activate", this.refreshDelegate);
39401         return this.bodyEl.getUpdateManager();
39402     },
39403
39404     /** @private */
39405     _handleRefresh : function(url, params, loadOnce){
39406         if(!loadOnce || !this.loaded){
39407             var updater = this.bodyEl.getUpdateManager();
39408             updater.update(url, params, this._setLoaded.createDelegate(this));
39409         }
39410     },
39411
39412     /**
39413      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39414      *   Will fail silently if the setUrl method has not been called.
39415      *   This does not activate the panel, just updates its content.
39416      */
39417     refresh : function(){
39418         if(this.refreshDelegate){
39419            this.loaded = false;
39420            this.refreshDelegate();
39421         }
39422     },
39423
39424     /** @private */
39425     _setLoaded : function(){
39426         this.loaded = true;
39427     },
39428
39429     /** @private */
39430     closeClick : function(e){
39431         var o = {};
39432         e.stopEvent();
39433         this.fireEvent("beforeclose", this, o);
39434         if(o.cancel !== true){
39435             this.tabPanel.removeTab(this.id);
39436         }
39437     },
39438     /**
39439      * The text displayed in the tooltip for the close icon.
39440      * @type String
39441      */
39442     closeText : "Close this tab"
39443 });
39444 /**
39445 *    This script refer to:
39446 *    Title: International Telephone Input
39447 *    Author: Jack O'Connor
39448 *    Code version:  v12.1.12
39449 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39450 **/
39451
39452 Roo.bootstrap.PhoneInputData = function() {
39453     var d = [
39454       [
39455         "Afghanistan (‫افغانستان‬‎)",
39456         "af",
39457         "93"
39458       ],
39459       [
39460         "Albania (Shqipëri)",
39461         "al",
39462         "355"
39463       ],
39464       [
39465         "Algeria (‫الجزائر‬‎)",
39466         "dz",
39467         "213"
39468       ],
39469       [
39470         "American Samoa",
39471         "as",
39472         "1684"
39473       ],
39474       [
39475         "Andorra",
39476         "ad",
39477         "376"
39478       ],
39479       [
39480         "Angola",
39481         "ao",
39482         "244"
39483       ],
39484       [
39485         "Anguilla",
39486         "ai",
39487         "1264"
39488       ],
39489       [
39490         "Antigua and Barbuda",
39491         "ag",
39492         "1268"
39493       ],
39494       [
39495         "Argentina",
39496         "ar",
39497         "54"
39498       ],
39499       [
39500         "Armenia (Հայաստան)",
39501         "am",
39502         "374"
39503       ],
39504       [
39505         "Aruba",
39506         "aw",
39507         "297"
39508       ],
39509       [
39510         "Australia",
39511         "au",
39512         "61",
39513         0
39514       ],
39515       [
39516         "Austria (Österreich)",
39517         "at",
39518         "43"
39519       ],
39520       [
39521         "Azerbaijan (Azərbaycan)",
39522         "az",
39523         "994"
39524       ],
39525       [
39526         "Bahamas",
39527         "bs",
39528         "1242"
39529       ],
39530       [
39531         "Bahrain (‫البحرين‬‎)",
39532         "bh",
39533         "973"
39534       ],
39535       [
39536         "Bangladesh (বাংলাদেশ)",
39537         "bd",
39538         "880"
39539       ],
39540       [
39541         "Barbados",
39542         "bb",
39543         "1246"
39544       ],
39545       [
39546         "Belarus (Беларусь)",
39547         "by",
39548         "375"
39549       ],
39550       [
39551         "Belgium (België)",
39552         "be",
39553         "32"
39554       ],
39555       [
39556         "Belize",
39557         "bz",
39558         "501"
39559       ],
39560       [
39561         "Benin (Bénin)",
39562         "bj",
39563         "229"
39564       ],
39565       [
39566         "Bermuda",
39567         "bm",
39568         "1441"
39569       ],
39570       [
39571         "Bhutan (འབྲུག)",
39572         "bt",
39573         "975"
39574       ],
39575       [
39576         "Bolivia",
39577         "bo",
39578         "591"
39579       ],
39580       [
39581         "Bosnia and Herzegovina (Босна и Херцеговина)",
39582         "ba",
39583         "387"
39584       ],
39585       [
39586         "Botswana",
39587         "bw",
39588         "267"
39589       ],
39590       [
39591         "Brazil (Brasil)",
39592         "br",
39593         "55"
39594       ],
39595       [
39596         "British Indian Ocean Territory",
39597         "io",
39598         "246"
39599       ],
39600       [
39601         "British Virgin Islands",
39602         "vg",
39603         "1284"
39604       ],
39605       [
39606         "Brunei",
39607         "bn",
39608         "673"
39609       ],
39610       [
39611         "Bulgaria (България)",
39612         "bg",
39613         "359"
39614       ],
39615       [
39616         "Burkina Faso",
39617         "bf",
39618         "226"
39619       ],
39620       [
39621         "Burundi (Uburundi)",
39622         "bi",
39623         "257"
39624       ],
39625       [
39626         "Cambodia (កម្ពុជា)",
39627         "kh",
39628         "855"
39629       ],
39630       [
39631         "Cameroon (Cameroun)",
39632         "cm",
39633         "237"
39634       ],
39635       [
39636         "Canada",
39637         "ca",
39638         "1",
39639         1,
39640         ["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"]
39641       ],
39642       [
39643         "Cape Verde (Kabu Verdi)",
39644         "cv",
39645         "238"
39646       ],
39647       [
39648         "Caribbean Netherlands",
39649         "bq",
39650         "599",
39651         1
39652       ],
39653       [
39654         "Cayman Islands",
39655         "ky",
39656         "1345"
39657       ],
39658       [
39659         "Central African Republic (République centrafricaine)",
39660         "cf",
39661         "236"
39662       ],
39663       [
39664         "Chad (Tchad)",
39665         "td",
39666         "235"
39667       ],
39668       [
39669         "Chile",
39670         "cl",
39671         "56"
39672       ],
39673       [
39674         "China (中国)",
39675         "cn",
39676         "86"
39677       ],
39678       [
39679         "Christmas Island",
39680         "cx",
39681         "61",
39682         2
39683       ],
39684       [
39685         "Cocos (Keeling) Islands",
39686         "cc",
39687         "61",
39688         1
39689       ],
39690       [
39691         "Colombia",
39692         "co",
39693         "57"
39694       ],
39695       [
39696         "Comoros (‫جزر القمر‬‎)",
39697         "km",
39698         "269"
39699       ],
39700       [
39701         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39702         "cd",
39703         "243"
39704       ],
39705       [
39706         "Congo (Republic) (Congo-Brazzaville)",
39707         "cg",
39708         "242"
39709       ],
39710       [
39711         "Cook Islands",
39712         "ck",
39713         "682"
39714       ],
39715       [
39716         "Costa Rica",
39717         "cr",
39718         "506"
39719       ],
39720       [
39721         "Côte d’Ivoire",
39722         "ci",
39723         "225"
39724       ],
39725       [
39726         "Croatia (Hrvatska)",
39727         "hr",
39728         "385"
39729       ],
39730       [
39731         "Cuba",
39732         "cu",
39733         "53"
39734       ],
39735       [
39736         "Curaçao",
39737         "cw",
39738         "599",
39739         0
39740       ],
39741       [
39742         "Cyprus (Κύπρος)",
39743         "cy",
39744         "357"
39745       ],
39746       [
39747         "Czech Republic (Česká republika)",
39748         "cz",
39749         "420"
39750       ],
39751       [
39752         "Denmark (Danmark)",
39753         "dk",
39754         "45"
39755       ],
39756       [
39757         "Djibouti",
39758         "dj",
39759         "253"
39760       ],
39761       [
39762         "Dominica",
39763         "dm",
39764         "1767"
39765       ],
39766       [
39767         "Dominican Republic (República Dominicana)",
39768         "do",
39769         "1",
39770         2,
39771         ["809", "829", "849"]
39772       ],
39773       [
39774         "Ecuador",
39775         "ec",
39776         "593"
39777       ],
39778       [
39779         "Egypt (‫مصر‬‎)",
39780         "eg",
39781         "20"
39782       ],
39783       [
39784         "El Salvador",
39785         "sv",
39786         "503"
39787       ],
39788       [
39789         "Equatorial Guinea (Guinea Ecuatorial)",
39790         "gq",
39791         "240"
39792       ],
39793       [
39794         "Eritrea",
39795         "er",
39796         "291"
39797       ],
39798       [
39799         "Estonia (Eesti)",
39800         "ee",
39801         "372"
39802       ],
39803       [
39804         "Ethiopia",
39805         "et",
39806         "251"
39807       ],
39808       [
39809         "Falkland Islands (Islas Malvinas)",
39810         "fk",
39811         "500"
39812       ],
39813       [
39814         "Faroe Islands (Føroyar)",
39815         "fo",
39816         "298"
39817       ],
39818       [
39819         "Fiji",
39820         "fj",
39821         "679"
39822       ],
39823       [
39824         "Finland (Suomi)",
39825         "fi",
39826         "358",
39827         0
39828       ],
39829       [
39830         "France",
39831         "fr",
39832         "33"
39833       ],
39834       [
39835         "French Guiana (Guyane française)",
39836         "gf",
39837         "594"
39838       ],
39839       [
39840         "French Polynesia (Polynésie française)",
39841         "pf",
39842         "689"
39843       ],
39844       [
39845         "Gabon",
39846         "ga",
39847         "241"
39848       ],
39849       [
39850         "Gambia",
39851         "gm",
39852         "220"
39853       ],
39854       [
39855         "Georgia (საქართველო)",
39856         "ge",
39857         "995"
39858       ],
39859       [
39860         "Germany (Deutschland)",
39861         "de",
39862         "49"
39863       ],
39864       [
39865         "Ghana (Gaana)",
39866         "gh",
39867         "233"
39868       ],
39869       [
39870         "Gibraltar",
39871         "gi",
39872         "350"
39873       ],
39874       [
39875         "Greece (Ελλάδα)",
39876         "gr",
39877         "30"
39878       ],
39879       [
39880         "Greenland (Kalaallit Nunaat)",
39881         "gl",
39882         "299"
39883       ],
39884       [
39885         "Grenada",
39886         "gd",
39887         "1473"
39888       ],
39889       [
39890         "Guadeloupe",
39891         "gp",
39892         "590",
39893         0
39894       ],
39895       [
39896         "Guam",
39897         "gu",
39898         "1671"
39899       ],
39900       [
39901         "Guatemala",
39902         "gt",
39903         "502"
39904       ],
39905       [
39906         "Guernsey",
39907         "gg",
39908         "44",
39909         1
39910       ],
39911       [
39912         "Guinea (Guinée)",
39913         "gn",
39914         "224"
39915       ],
39916       [
39917         "Guinea-Bissau (Guiné Bissau)",
39918         "gw",
39919         "245"
39920       ],
39921       [
39922         "Guyana",
39923         "gy",
39924         "592"
39925       ],
39926       [
39927         "Haiti",
39928         "ht",
39929         "509"
39930       ],
39931       [
39932         "Honduras",
39933         "hn",
39934         "504"
39935       ],
39936       [
39937         "Hong Kong (香港)",
39938         "hk",
39939         "852"
39940       ],
39941       [
39942         "Hungary (Magyarország)",
39943         "hu",
39944         "36"
39945       ],
39946       [
39947         "Iceland (Ísland)",
39948         "is",
39949         "354"
39950       ],
39951       [
39952         "India (भारत)",
39953         "in",
39954         "91"
39955       ],
39956       [
39957         "Indonesia",
39958         "id",
39959         "62"
39960       ],
39961       [
39962         "Iran (‫ایران‬‎)",
39963         "ir",
39964         "98"
39965       ],
39966       [
39967         "Iraq (‫العراق‬‎)",
39968         "iq",
39969         "964"
39970       ],
39971       [
39972         "Ireland",
39973         "ie",
39974         "353"
39975       ],
39976       [
39977         "Isle of Man",
39978         "im",
39979         "44",
39980         2
39981       ],
39982       [
39983         "Israel (‫ישראל‬‎)",
39984         "il",
39985         "972"
39986       ],
39987       [
39988         "Italy (Italia)",
39989         "it",
39990         "39",
39991         0
39992       ],
39993       [
39994         "Jamaica",
39995         "jm",
39996         "1876"
39997       ],
39998       [
39999         "Japan (日本)",
40000         "jp",
40001         "81"
40002       ],
40003       [
40004         "Jersey",
40005         "je",
40006         "44",
40007         3
40008       ],
40009       [
40010         "Jordan (‫الأردن‬‎)",
40011         "jo",
40012         "962"
40013       ],
40014       [
40015         "Kazakhstan (Казахстан)",
40016         "kz",
40017         "7",
40018         1
40019       ],
40020       [
40021         "Kenya",
40022         "ke",
40023         "254"
40024       ],
40025       [
40026         "Kiribati",
40027         "ki",
40028         "686"
40029       ],
40030       [
40031         "Kosovo",
40032         "xk",
40033         "383"
40034       ],
40035       [
40036         "Kuwait (‫الكويت‬‎)",
40037         "kw",
40038         "965"
40039       ],
40040       [
40041         "Kyrgyzstan (Кыргызстан)",
40042         "kg",
40043         "996"
40044       ],
40045       [
40046         "Laos (ລາວ)",
40047         "la",
40048         "856"
40049       ],
40050       [
40051         "Latvia (Latvija)",
40052         "lv",
40053         "371"
40054       ],
40055       [
40056         "Lebanon (‫لبنان‬‎)",
40057         "lb",
40058         "961"
40059       ],
40060       [
40061         "Lesotho",
40062         "ls",
40063         "266"
40064       ],
40065       [
40066         "Liberia",
40067         "lr",
40068         "231"
40069       ],
40070       [
40071         "Libya (‫ليبيا‬‎)",
40072         "ly",
40073         "218"
40074       ],
40075       [
40076         "Liechtenstein",
40077         "li",
40078         "423"
40079       ],
40080       [
40081         "Lithuania (Lietuva)",
40082         "lt",
40083         "370"
40084       ],
40085       [
40086         "Luxembourg",
40087         "lu",
40088         "352"
40089       ],
40090       [
40091         "Macau (澳門)",
40092         "mo",
40093         "853"
40094       ],
40095       [
40096         "Macedonia (FYROM) (Македонија)",
40097         "mk",
40098         "389"
40099       ],
40100       [
40101         "Madagascar (Madagasikara)",
40102         "mg",
40103         "261"
40104       ],
40105       [
40106         "Malawi",
40107         "mw",
40108         "265"
40109       ],
40110       [
40111         "Malaysia",
40112         "my",
40113         "60"
40114       ],
40115       [
40116         "Maldives",
40117         "mv",
40118         "960"
40119       ],
40120       [
40121         "Mali",
40122         "ml",
40123         "223"
40124       ],
40125       [
40126         "Malta",
40127         "mt",
40128         "356"
40129       ],
40130       [
40131         "Marshall Islands",
40132         "mh",
40133         "692"
40134       ],
40135       [
40136         "Martinique",
40137         "mq",
40138         "596"
40139       ],
40140       [
40141         "Mauritania (‫موريتانيا‬‎)",
40142         "mr",
40143         "222"
40144       ],
40145       [
40146         "Mauritius (Moris)",
40147         "mu",
40148         "230"
40149       ],
40150       [
40151         "Mayotte",
40152         "yt",
40153         "262",
40154         1
40155       ],
40156       [
40157         "Mexico (México)",
40158         "mx",
40159         "52"
40160       ],
40161       [
40162         "Micronesia",
40163         "fm",
40164         "691"
40165       ],
40166       [
40167         "Moldova (Republica Moldova)",
40168         "md",
40169         "373"
40170       ],
40171       [
40172         "Monaco",
40173         "mc",
40174         "377"
40175       ],
40176       [
40177         "Mongolia (Монгол)",
40178         "mn",
40179         "976"
40180       ],
40181       [
40182         "Montenegro (Crna Gora)",
40183         "me",
40184         "382"
40185       ],
40186       [
40187         "Montserrat",
40188         "ms",
40189         "1664"
40190       ],
40191       [
40192         "Morocco (‫المغرب‬‎)",
40193         "ma",
40194         "212",
40195         0
40196       ],
40197       [
40198         "Mozambique (Moçambique)",
40199         "mz",
40200         "258"
40201       ],
40202       [
40203         "Myanmar (Burma) (မြန်မာ)",
40204         "mm",
40205         "95"
40206       ],
40207       [
40208         "Namibia (Namibië)",
40209         "na",
40210         "264"
40211       ],
40212       [
40213         "Nauru",
40214         "nr",
40215         "674"
40216       ],
40217       [
40218         "Nepal (नेपाल)",
40219         "np",
40220         "977"
40221       ],
40222       [
40223         "Netherlands (Nederland)",
40224         "nl",
40225         "31"
40226       ],
40227       [
40228         "New Caledonia (Nouvelle-Calédonie)",
40229         "nc",
40230         "687"
40231       ],
40232       [
40233         "New Zealand",
40234         "nz",
40235         "64"
40236       ],
40237       [
40238         "Nicaragua",
40239         "ni",
40240         "505"
40241       ],
40242       [
40243         "Niger (Nijar)",
40244         "ne",
40245         "227"
40246       ],
40247       [
40248         "Nigeria",
40249         "ng",
40250         "234"
40251       ],
40252       [
40253         "Niue",
40254         "nu",
40255         "683"
40256       ],
40257       [
40258         "Norfolk Island",
40259         "nf",
40260         "672"
40261       ],
40262       [
40263         "North Korea (조선 민주주의 인민 공화국)",
40264         "kp",
40265         "850"
40266       ],
40267       [
40268         "Northern Mariana Islands",
40269         "mp",
40270         "1670"
40271       ],
40272       [
40273         "Norway (Norge)",
40274         "no",
40275         "47",
40276         0
40277       ],
40278       [
40279         "Oman (‫عُمان‬‎)",
40280         "om",
40281         "968"
40282       ],
40283       [
40284         "Pakistan (‫پاکستان‬‎)",
40285         "pk",
40286         "92"
40287       ],
40288       [
40289         "Palau",
40290         "pw",
40291         "680"
40292       ],
40293       [
40294         "Palestine (‫فلسطين‬‎)",
40295         "ps",
40296         "970"
40297       ],
40298       [
40299         "Panama (Panamá)",
40300         "pa",
40301         "507"
40302       ],
40303       [
40304         "Papua New Guinea",
40305         "pg",
40306         "675"
40307       ],
40308       [
40309         "Paraguay",
40310         "py",
40311         "595"
40312       ],
40313       [
40314         "Peru (Perú)",
40315         "pe",
40316         "51"
40317       ],
40318       [
40319         "Philippines",
40320         "ph",
40321         "63"
40322       ],
40323       [
40324         "Poland (Polska)",
40325         "pl",
40326         "48"
40327       ],
40328       [
40329         "Portugal",
40330         "pt",
40331         "351"
40332       ],
40333       [
40334         "Puerto Rico",
40335         "pr",
40336         "1",
40337         3,
40338         ["787", "939"]
40339       ],
40340       [
40341         "Qatar (‫قطر‬‎)",
40342         "qa",
40343         "974"
40344       ],
40345       [
40346         "Réunion (La Réunion)",
40347         "re",
40348         "262",
40349         0
40350       ],
40351       [
40352         "Romania (România)",
40353         "ro",
40354         "40"
40355       ],
40356       [
40357         "Russia (Россия)",
40358         "ru",
40359         "7",
40360         0
40361       ],
40362       [
40363         "Rwanda",
40364         "rw",
40365         "250"
40366       ],
40367       [
40368         "Saint Barthélemy",
40369         "bl",
40370         "590",
40371         1
40372       ],
40373       [
40374         "Saint Helena",
40375         "sh",
40376         "290"
40377       ],
40378       [
40379         "Saint Kitts and Nevis",
40380         "kn",
40381         "1869"
40382       ],
40383       [
40384         "Saint Lucia",
40385         "lc",
40386         "1758"
40387       ],
40388       [
40389         "Saint Martin (Saint-Martin (partie française))",
40390         "mf",
40391         "590",
40392         2
40393       ],
40394       [
40395         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40396         "pm",
40397         "508"
40398       ],
40399       [
40400         "Saint Vincent and the Grenadines",
40401         "vc",
40402         "1784"
40403       ],
40404       [
40405         "Samoa",
40406         "ws",
40407         "685"
40408       ],
40409       [
40410         "San Marino",
40411         "sm",
40412         "378"
40413       ],
40414       [
40415         "São Tomé and Príncipe (São Tomé e Príncipe)",
40416         "st",
40417         "239"
40418       ],
40419       [
40420         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40421         "sa",
40422         "966"
40423       ],
40424       [
40425         "Senegal (Sénégal)",
40426         "sn",
40427         "221"
40428       ],
40429       [
40430         "Serbia (Србија)",
40431         "rs",
40432         "381"
40433       ],
40434       [
40435         "Seychelles",
40436         "sc",
40437         "248"
40438       ],
40439       [
40440         "Sierra Leone",
40441         "sl",
40442         "232"
40443       ],
40444       [
40445         "Singapore",
40446         "sg",
40447         "65"
40448       ],
40449       [
40450         "Sint Maarten",
40451         "sx",
40452         "1721"
40453       ],
40454       [
40455         "Slovakia (Slovensko)",
40456         "sk",
40457         "421"
40458       ],
40459       [
40460         "Slovenia (Slovenija)",
40461         "si",
40462         "386"
40463       ],
40464       [
40465         "Solomon Islands",
40466         "sb",
40467         "677"
40468       ],
40469       [
40470         "Somalia (Soomaaliya)",
40471         "so",
40472         "252"
40473       ],
40474       [
40475         "South Africa",
40476         "za",
40477         "27"
40478       ],
40479       [
40480         "South Korea (대한민국)",
40481         "kr",
40482         "82"
40483       ],
40484       [
40485         "South Sudan (‫جنوب السودان‬‎)",
40486         "ss",
40487         "211"
40488       ],
40489       [
40490         "Spain (España)",
40491         "es",
40492         "34"
40493       ],
40494       [
40495         "Sri Lanka (ශ්‍රී ලංකාව)",
40496         "lk",
40497         "94"
40498       ],
40499       [
40500         "Sudan (‫السودان‬‎)",
40501         "sd",
40502         "249"
40503       ],
40504       [
40505         "Suriname",
40506         "sr",
40507         "597"
40508       ],
40509       [
40510         "Svalbard and Jan Mayen",
40511         "sj",
40512         "47",
40513         1
40514       ],
40515       [
40516         "Swaziland",
40517         "sz",
40518         "268"
40519       ],
40520       [
40521         "Sweden (Sverige)",
40522         "se",
40523         "46"
40524       ],
40525       [
40526         "Switzerland (Schweiz)",
40527         "ch",
40528         "41"
40529       ],
40530       [
40531         "Syria (‫سوريا‬‎)",
40532         "sy",
40533         "963"
40534       ],
40535       [
40536         "Taiwan (台灣)",
40537         "tw",
40538         "886"
40539       ],
40540       [
40541         "Tajikistan",
40542         "tj",
40543         "992"
40544       ],
40545       [
40546         "Tanzania",
40547         "tz",
40548         "255"
40549       ],
40550       [
40551         "Thailand (ไทย)",
40552         "th",
40553         "66"
40554       ],
40555       [
40556         "Timor-Leste",
40557         "tl",
40558         "670"
40559       ],
40560       [
40561         "Togo",
40562         "tg",
40563         "228"
40564       ],
40565       [
40566         "Tokelau",
40567         "tk",
40568         "690"
40569       ],
40570       [
40571         "Tonga",
40572         "to",
40573         "676"
40574       ],
40575       [
40576         "Trinidad and Tobago",
40577         "tt",
40578         "1868"
40579       ],
40580       [
40581         "Tunisia (‫تونس‬‎)",
40582         "tn",
40583         "216"
40584       ],
40585       [
40586         "Turkey (Türkiye)",
40587         "tr",
40588         "90"
40589       ],
40590       [
40591         "Turkmenistan",
40592         "tm",
40593         "993"
40594       ],
40595       [
40596         "Turks and Caicos Islands",
40597         "tc",
40598         "1649"
40599       ],
40600       [
40601         "Tuvalu",
40602         "tv",
40603         "688"
40604       ],
40605       [
40606         "U.S. Virgin Islands",
40607         "vi",
40608         "1340"
40609       ],
40610       [
40611         "Uganda",
40612         "ug",
40613         "256"
40614       ],
40615       [
40616         "Ukraine (Україна)",
40617         "ua",
40618         "380"
40619       ],
40620       [
40621         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40622         "ae",
40623         "971"
40624       ],
40625       [
40626         "United Kingdom",
40627         "gb",
40628         "44",
40629         0
40630       ],
40631       [
40632         "United States",
40633         "us",
40634         "1",
40635         0
40636       ],
40637       [
40638         "Uruguay",
40639         "uy",
40640         "598"
40641       ],
40642       [
40643         "Uzbekistan (Oʻzbekiston)",
40644         "uz",
40645         "998"
40646       ],
40647       [
40648         "Vanuatu",
40649         "vu",
40650         "678"
40651       ],
40652       [
40653         "Vatican City (Città del Vaticano)",
40654         "va",
40655         "39",
40656         1
40657       ],
40658       [
40659         "Venezuela",
40660         "ve",
40661         "58"
40662       ],
40663       [
40664         "Vietnam (Việt Nam)",
40665         "vn",
40666         "84"
40667       ],
40668       [
40669         "Wallis and Futuna (Wallis-et-Futuna)",
40670         "wf",
40671         "681"
40672       ],
40673       [
40674         "Western Sahara (‫الصحراء الغربية‬‎)",
40675         "eh",
40676         "212",
40677         1
40678       ],
40679       [
40680         "Yemen (‫اليمن‬‎)",
40681         "ye",
40682         "967"
40683       ],
40684       [
40685         "Zambia",
40686         "zm",
40687         "260"
40688       ],
40689       [
40690         "Zimbabwe",
40691         "zw",
40692         "263"
40693       ],
40694       [
40695         "Åland Islands",
40696         "ax",
40697         "358",
40698         1
40699       ]
40700   ];
40701   
40702   return d;
40703 }/**
40704 *    This script refer to:
40705 *    Title: International Telephone Input
40706 *    Author: Jack O'Connor
40707 *    Code version:  v12.1.12
40708 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40709 **/
40710
40711 /**
40712  * @class Roo.bootstrap.PhoneInput
40713  * @extends Roo.bootstrap.TriggerField
40714  * An input with International dial-code selection
40715  
40716  * @cfg {String} defaultDialCode default '+852'
40717  * @cfg {Array} preferedCountries default []
40718   
40719  * @constructor
40720  * Create a new PhoneInput.
40721  * @param {Object} config Configuration options
40722  */
40723
40724 Roo.bootstrap.PhoneInput = function(config) {
40725     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40726 };
40727
40728 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40729         
40730         listWidth: undefined,
40731         
40732         selectedClass: 'active',
40733         
40734         invalidClass : "has-warning",
40735         
40736         validClass: 'has-success',
40737         
40738         allowed: '0123456789',
40739         
40740         max_length: 15,
40741         
40742         /**
40743          * @cfg {String} defaultDialCode The default dial code when initializing the input
40744          */
40745         defaultDialCode: '+852',
40746         
40747         /**
40748          * @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
40749          */
40750         preferedCountries: false,
40751         
40752         getAutoCreate : function()
40753         {
40754             var data = Roo.bootstrap.PhoneInputData();
40755             var align = this.labelAlign || this.parentLabelAlign();
40756             var id = Roo.id();
40757             
40758             this.allCountries = [];
40759             this.dialCodeMapping = [];
40760             
40761             for (var i = 0; i < data.length; i++) {
40762               var c = data[i];
40763               this.allCountries[i] = {
40764                 name: c[0],
40765                 iso2: c[1],
40766                 dialCode: c[2],
40767                 priority: c[3] || 0,
40768                 areaCodes: c[4] || null
40769               };
40770               this.dialCodeMapping[c[2]] = {
40771                   name: c[0],
40772                   iso2: c[1],
40773                   priority: c[3] || 0,
40774                   areaCodes: c[4] || null
40775               };
40776             }
40777             
40778             var cfg = {
40779                 cls: 'form-group',
40780                 cn: []
40781             };
40782             
40783             var input =  {
40784                 tag: 'input',
40785                 id : id,
40786                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40787                 maxlength: this.max_length,
40788                 cls : 'form-control tel-input',
40789                 autocomplete: 'new-password'
40790             };
40791             
40792             var hiddenInput = {
40793                 tag: 'input',
40794                 type: 'hidden',
40795                 cls: 'hidden-tel-input'
40796             };
40797             
40798             if (this.name) {
40799                 hiddenInput.name = this.name;
40800             }
40801             
40802             if (this.disabled) {
40803                 input.disabled = true;
40804             }
40805             
40806             var flag_container = {
40807                 tag: 'div',
40808                 cls: 'flag-box',
40809                 cn: [
40810                     {
40811                         tag: 'div',
40812                         cls: 'flag'
40813                     },
40814                     {
40815                         tag: 'div',
40816                         cls: 'caret'
40817                     }
40818                 ]
40819             };
40820             
40821             var box = {
40822                 tag: 'div',
40823                 cls: this.hasFeedback ? 'has-feedback' : '',
40824                 cn: [
40825                     hiddenInput,
40826                     input,
40827                     {
40828                         tag: 'input',
40829                         cls: 'dial-code-holder',
40830                         disabled: true
40831                     }
40832                 ]
40833             };
40834             
40835             var container = {
40836                 cls: 'roo-select2-container input-group',
40837                 cn: [
40838                     flag_container,
40839                     box
40840                 ]
40841             };
40842             
40843             if (this.fieldLabel.length) {
40844                 var indicator = {
40845                     tag: 'i',
40846                     tooltip: 'This field is required'
40847                 };
40848                 
40849                 var label = {
40850                     tag: 'label',
40851                     'for':  id,
40852                     cls: 'control-label',
40853                     cn: []
40854                 };
40855                 
40856                 var label_text = {
40857                     tag: 'span',
40858                     html: this.fieldLabel
40859                 };
40860                 
40861                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40862                 label.cn = [
40863                     indicator,
40864                     label_text
40865                 ];
40866                 
40867                 if(this.indicatorpos == 'right') {
40868                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40869                     label.cn = [
40870                         label_text,
40871                         indicator
40872                     ];
40873                 }
40874                 
40875                 if(align == 'left') {
40876                     container = {
40877                         tag: 'div',
40878                         cn: [
40879                             container
40880                         ]
40881                     };
40882                     
40883                     if(this.labelWidth > 12){
40884                         label.style = "width: " + this.labelWidth + 'px';
40885                     }
40886                     if(this.labelWidth < 13 && this.labelmd == 0){
40887                         this.labelmd = this.labelWidth;
40888                     }
40889                     if(this.labellg > 0){
40890                         label.cls += ' col-lg-' + this.labellg;
40891                         input.cls += ' col-lg-' + (12 - this.labellg);
40892                     }
40893                     if(this.labelmd > 0){
40894                         label.cls += ' col-md-' + this.labelmd;
40895                         container.cls += ' col-md-' + (12 - this.labelmd);
40896                     }
40897                     if(this.labelsm > 0){
40898                         label.cls += ' col-sm-' + this.labelsm;
40899                         container.cls += ' col-sm-' + (12 - this.labelsm);
40900                     }
40901                     if(this.labelxs > 0){
40902                         label.cls += ' col-xs-' + this.labelxs;
40903                         container.cls += ' col-xs-' + (12 - this.labelxs);
40904                     }
40905                 }
40906             }
40907             
40908             cfg.cn = [
40909                 label,
40910                 container
40911             ];
40912             
40913             var settings = this;
40914             
40915             ['xs','sm','md','lg'].map(function(size){
40916                 if (settings[size]) {
40917                     cfg.cls += ' col-' + size + '-' + settings[size];
40918                 }
40919             });
40920             
40921             this.store = new Roo.data.Store({
40922                 proxy : new Roo.data.MemoryProxy({}),
40923                 reader : new Roo.data.JsonReader({
40924                     fields : [
40925                         {
40926                             'name' : 'name',
40927                             'type' : 'string'
40928                         },
40929                         {
40930                             'name' : 'iso2',
40931                             'type' : 'string'
40932                         },
40933                         {
40934                             'name' : 'dialCode',
40935                             'type' : 'string'
40936                         },
40937                         {
40938                             'name' : 'priority',
40939                             'type' : 'string'
40940                         },
40941                         {
40942                             'name' : 'areaCodes',
40943                             'type' : 'string'
40944                         }
40945                     ]
40946                 })
40947             });
40948             
40949             if(!this.preferedCountries) {
40950                 this.preferedCountries = [
40951                     'hk',
40952                     'gb',
40953                     'us'
40954                 ];
40955             }
40956             
40957             var p = this.preferedCountries.reverse();
40958             
40959             if(p) {
40960                 for (var i = 0; i < p.length; i++) {
40961                     for (var j = 0; j < this.allCountries.length; j++) {
40962                         if(this.allCountries[j].iso2 == p[i]) {
40963                             var t = this.allCountries[j];
40964                             this.allCountries.splice(j,1);
40965                             this.allCountries.unshift(t);
40966                         }
40967                     } 
40968                 }
40969             }
40970             
40971             this.store.proxy.data = {
40972                 success: true,
40973                 data: this.allCountries
40974             };
40975             
40976             return cfg;
40977         },
40978         
40979         initEvents : function()
40980         {
40981             this.createList();
40982             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40983             
40984             this.indicator = this.indicatorEl();
40985             this.flag = this.flagEl();
40986             this.dialCodeHolder = this.dialCodeHolderEl();
40987             
40988             this.trigger = this.el.select('div.flag-box',true).first();
40989             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40990             
40991             var _this = this;
40992             
40993             (function(){
40994                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40995                 _this.list.setWidth(lw);
40996             }).defer(100);
40997             
40998             this.list.on('mouseover', this.onViewOver, this);
40999             this.list.on('mousemove', this.onViewMove, this);
41000             this.inputEl().on("keyup", this.onKeyUp, this);
41001             this.inputEl().on("keypress", this.onKeyPress, this);
41002             
41003             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41004
41005             this.view = new Roo.View(this.list, this.tpl, {
41006                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41007             });
41008             
41009             this.view.on('click', this.onViewClick, this);
41010             this.setValue(this.defaultDialCode);
41011         },
41012         
41013         onTriggerClick : function(e)
41014         {
41015             Roo.log('trigger click');
41016             if(this.disabled){
41017                 return;
41018             }
41019             
41020             if(this.isExpanded()){
41021                 this.collapse();
41022                 this.hasFocus = false;
41023             }else {
41024                 this.store.load({});
41025                 this.hasFocus = true;
41026                 this.expand();
41027             }
41028         },
41029         
41030         isExpanded : function()
41031         {
41032             return this.list.isVisible();
41033         },
41034         
41035         collapse : function()
41036         {
41037             if(!this.isExpanded()){
41038                 return;
41039             }
41040             this.list.hide();
41041             Roo.get(document).un('mousedown', this.collapseIf, this);
41042             Roo.get(document).un('mousewheel', this.collapseIf, this);
41043             this.fireEvent('collapse', this);
41044             this.validate();
41045         },
41046         
41047         expand : function()
41048         {
41049             Roo.log('expand');
41050
41051             if(this.isExpanded() || !this.hasFocus){
41052                 return;
41053             }
41054             
41055             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41056             this.list.setWidth(lw);
41057             
41058             this.list.show();
41059             this.restrictHeight();
41060             
41061             Roo.get(document).on('mousedown', this.collapseIf, this);
41062             Roo.get(document).on('mousewheel', this.collapseIf, this);
41063             
41064             this.fireEvent('expand', this);
41065         },
41066         
41067         restrictHeight : function()
41068         {
41069             this.list.alignTo(this.inputEl(), this.listAlign);
41070             this.list.alignTo(this.inputEl(), this.listAlign);
41071         },
41072         
41073         onViewOver : function(e, t)
41074         {
41075             if(this.inKeyMode){
41076                 return;
41077             }
41078             var item = this.view.findItemFromChild(t);
41079             
41080             if(item){
41081                 var index = this.view.indexOf(item);
41082                 this.select(index, false);
41083             }
41084         },
41085
41086         // private
41087         onViewClick : function(view, doFocus, el, e)
41088         {
41089             var index = this.view.getSelectedIndexes()[0];
41090             
41091             var r = this.store.getAt(index);
41092             
41093             if(r){
41094                 this.onSelect(r, index);
41095             }
41096             if(doFocus !== false && !this.blockFocus){
41097                 this.inputEl().focus();
41098             }
41099         },
41100         
41101         onViewMove : function(e, t)
41102         {
41103             this.inKeyMode = false;
41104         },
41105         
41106         select : function(index, scrollIntoView)
41107         {
41108             this.selectedIndex = index;
41109             this.view.select(index);
41110             if(scrollIntoView !== false){
41111                 var el = this.view.getNode(index);
41112                 if(el){
41113                     this.list.scrollChildIntoView(el, false);
41114                 }
41115             }
41116         },
41117         
41118         createList : function()
41119         {
41120             this.list = Roo.get(document.body).createChild({
41121                 tag: 'ul',
41122                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41123                 style: 'display:none'
41124             });
41125             
41126             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41127         },
41128         
41129         collapseIf : function(e)
41130         {
41131             var in_combo  = e.within(this.el);
41132             var in_list =  e.within(this.list);
41133             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41134             
41135             if (in_combo || in_list || is_list) {
41136                 return;
41137             }
41138             this.collapse();
41139         },
41140         
41141         onSelect : function(record, index)
41142         {
41143             if(this.fireEvent('beforeselect', this, record, index) !== false){
41144                 
41145                 this.setFlagClass(record.data.iso2);
41146                 this.setDialCode(record.data.dialCode);
41147                 this.hasFocus = false;
41148                 this.collapse();
41149                 this.fireEvent('select', this, record, index);
41150             }
41151         },
41152         
41153         flagEl : function()
41154         {
41155             var flag = this.el.select('div.flag',true).first();
41156             if(!flag){
41157                 return false;
41158             }
41159             return flag;
41160         },
41161         
41162         dialCodeHolderEl : function()
41163         {
41164             var d = this.el.select('input.dial-code-holder',true).first();
41165             if(!d){
41166                 return false;
41167             }
41168             return d;
41169         },
41170         
41171         setDialCode : function(v)
41172         {
41173             this.dialCodeHolder.dom.value = '+'+v;
41174         },
41175         
41176         setFlagClass : function(n)
41177         {
41178             this.flag.dom.className = 'flag '+n;
41179         },
41180         
41181         getValue : function()
41182         {
41183             var v = this.inputEl().getValue();
41184             if(this.dialCodeHolder) {
41185                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41186             }
41187             return v;
41188         },
41189         
41190         setValue : function(v)
41191         {
41192             var d = this.getDialCode(v);
41193             
41194             //invalid dial code
41195             if(v.length == 0 || !d || d.length == 0) {
41196                 if(this.rendered){
41197                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41198                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41199                 }
41200                 return;
41201             }
41202             
41203             //valid dial code
41204             this.setFlagClass(this.dialCodeMapping[d].iso2);
41205             this.setDialCode(d);
41206             this.inputEl().dom.value = v.replace('+'+d,'');
41207             this.hiddenEl().dom.value = this.getValue();
41208             
41209             this.validate();
41210         },
41211         
41212         getDialCode : function(v)
41213         {
41214             v = v ||  '';
41215             
41216             if (v.length == 0) {
41217                 return this.dialCodeHolder.dom.value;
41218             }
41219             
41220             var dialCode = "";
41221             if (v.charAt(0) != "+") {
41222                 return false;
41223             }
41224             var numericChars = "";
41225             for (var i = 1; i < v.length; i++) {
41226               var c = v.charAt(i);
41227               if (!isNaN(c)) {
41228                 numericChars += c;
41229                 if (this.dialCodeMapping[numericChars]) {
41230                   dialCode = v.substr(1, i);
41231                 }
41232                 if (numericChars.length == 4) {
41233                   break;
41234                 }
41235               }
41236             }
41237             return dialCode;
41238         },
41239         
41240         reset : function()
41241         {
41242             this.setValue(this.defaultDialCode);
41243             this.validate();
41244         },
41245         
41246         hiddenEl : function()
41247         {
41248             return this.el.select('input.hidden-tel-input',true).first();
41249         },
41250         
41251         // after setting val
41252         onKeyUp : function(e){
41253             this.setValue(this.getValue());
41254         },
41255         
41256         onKeyPress : function(e){
41257             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41258                 e.stopEvent();
41259             }
41260         }
41261         
41262 });
41263 /**
41264  * @class Roo.bootstrap.MoneyField
41265  * @extends Roo.bootstrap.ComboBox
41266  * Bootstrap MoneyField class
41267  * 
41268  * @constructor
41269  * Create a new MoneyField.
41270  * @param {Object} config Configuration options
41271  */
41272
41273 Roo.bootstrap.MoneyField = function(config) {
41274     
41275     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41276     
41277 };
41278
41279 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41280     
41281     /**
41282      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41283      */
41284     allowDecimals : true,
41285     /**
41286      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41287      */
41288     decimalSeparator : ".",
41289     /**
41290      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41291      */
41292     decimalPrecision : 0,
41293     /**
41294      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41295      */
41296     allowNegative : true,
41297     /**
41298      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41299      */
41300     allowZero: true,
41301     /**
41302      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41303      */
41304     minValue : Number.NEGATIVE_INFINITY,
41305     /**
41306      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41307      */
41308     maxValue : Number.MAX_VALUE,
41309     /**
41310      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41311      */
41312     minText : "The minimum value for this field is {0}",
41313     /**
41314      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41315      */
41316     maxText : "The maximum value for this field is {0}",
41317     /**
41318      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41319      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41320      */
41321     nanText : "{0} is not a valid number",
41322     /**
41323      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41324      */
41325     castInt : true,
41326     /**
41327      * @cfg {String} defaults currency of the MoneyField
41328      * value should be in lkey
41329      */
41330     defaultCurrency : false,
41331     /**
41332      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41333      */
41334     thousandsDelimiter : false,
41335     /**
41336      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41337      */
41338     max_length: false,
41339     
41340     inputlg : 9,
41341     inputmd : 9,
41342     inputsm : 9,
41343     inputxs : 6,
41344     
41345     store : false,
41346     
41347     getAutoCreate : function()
41348     {
41349         var align = this.labelAlign || this.parentLabelAlign();
41350         
41351         var id = Roo.id();
41352
41353         var cfg = {
41354             cls: 'form-group',
41355             cn: []
41356         };
41357
41358         var input =  {
41359             tag: 'input',
41360             id : id,
41361             cls : 'form-control roo-money-amount-input',
41362             autocomplete: 'new-password'
41363         };
41364         
41365         var hiddenInput = {
41366             tag: 'input',
41367             type: 'hidden',
41368             id: Roo.id(),
41369             cls: 'hidden-number-input'
41370         };
41371         
41372         if(this.max_length) {
41373             input.maxlength = this.max_length; 
41374         }
41375         
41376         if (this.name) {
41377             hiddenInput.name = this.name;
41378         }
41379
41380         if (this.disabled) {
41381             input.disabled = true;
41382         }
41383
41384         var clg = 12 - this.inputlg;
41385         var cmd = 12 - this.inputmd;
41386         var csm = 12 - this.inputsm;
41387         var cxs = 12 - this.inputxs;
41388         
41389         var container = {
41390             tag : 'div',
41391             cls : 'row roo-money-field',
41392             cn : [
41393                 {
41394                     tag : 'div',
41395                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41396                     cn : [
41397                         {
41398                             tag : 'div',
41399                             cls: 'roo-select2-container input-group',
41400                             cn: [
41401                                 {
41402                                     tag : 'input',
41403                                     cls : 'form-control roo-money-currency-input',
41404                                     autocomplete: 'new-password',
41405                                     readOnly : 1,
41406                                     name : this.currencyName
41407                                 },
41408                                 {
41409                                     tag :'span',
41410                                     cls : 'input-group-addon',
41411                                     cn : [
41412                                         {
41413                                             tag: 'span',
41414                                             cls: 'caret'
41415                                         }
41416                                     ]
41417                                 }
41418                             ]
41419                         }
41420                     ]
41421                 },
41422                 {
41423                     tag : 'div',
41424                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41425                     cn : [
41426                         {
41427                             tag: 'div',
41428                             cls: this.hasFeedback ? 'has-feedback' : '',
41429                             cn: [
41430                                 input
41431                             ]
41432                         }
41433                     ]
41434                 }
41435             ]
41436             
41437         };
41438         
41439         if (this.fieldLabel.length) {
41440             var indicator = {
41441                 tag: 'i',
41442                 tooltip: 'This field is required'
41443             };
41444
41445             var label = {
41446                 tag: 'label',
41447                 'for':  id,
41448                 cls: 'control-label',
41449                 cn: []
41450             };
41451
41452             var label_text = {
41453                 tag: 'span',
41454                 html: this.fieldLabel
41455             };
41456
41457             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41458             label.cn = [
41459                 indicator,
41460                 label_text
41461             ];
41462
41463             if(this.indicatorpos == 'right') {
41464                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41465                 label.cn = [
41466                     label_text,
41467                     indicator
41468                 ];
41469             }
41470
41471             if(align == 'left') {
41472                 container = {
41473                     tag: 'div',
41474                     cn: [
41475                         container
41476                     ]
41477                 };
41478
41479                 if(this.labelWidth > 12){
41480                     label.style = "width: " + this.labelWidth + 'px';
41481                 }
41482                 if(this.labelWidth < 13 && this.labelmd == 0){
41483                     this.labelmd = this.labelWidth;
41484                 }
41485                 if(this.labellg > 0){
41486                     label.cls += ' col-lg-' + this.labellg;
41487                     input.cls += ' col-lg-' + (12 - this.labellg);
41488                 }
41489                 if(this.labelmd > 0){
41490                     label.cls += ' col-md-' + this.labelmd;
41491                     container.cls += ' col-md-' + (12 - this.labelmd);
41492                 }
41493                 if(this.labelsm > 0){
41494                     label.cls += ' col-sm-' + this.labelsm;
41495                     container.cls += ' col-sm-' + (12 - this.labelsm);
41496                 }
41497                 if(this.labelxs > 0){
41498                     label.cls += ' col-xs-' + this.labelxs;
41499                     container.cls += ' col-xs-' + (12 - this.labelxs);
41500                 }
41501             }
41502         }
41503
41504         cfg.cn = [
41505             label,
41506             container,
41507             hiddenInput
41508         ];
41509         
41510         var settings = this;
41511
41512         ['xs','sm','md','lg'].map(function(size){
41513             if (settings[size]) {
41514                 cfg.cls += ' col-' + size + '-' + settings[size];
41515             }
41516         });
41517         
41518         return cfg;
41519     },
41520     
41521     initEvents : function()
41522     {
41523         this.indicator = this.indicatorEl();
41524         
41525         this.initCurrencyEvent();
41526         
41527         this.initNumberEvent();
41528     },
41529     
41530     initCurrencyEvent : function()
41531     {
41532         if (!this.store) {
41533             throw "can not find store for combo";
41534         }
41535         
41536         this.store = Roo.factory(this.store, Roo.data);
41537         this.store.parent = this;
41538         
41539         this.createList();
41540         
41541         this.triggerEl = this.el.select('.input-group-addon', true).first();
41542         
41543         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41544         
41545         var _this = this;
41546         
41547         (function(){
41548             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41549             _this.list.setWidth(lw);
41550         }).defer(100);
41551         
41552         this.list.on('mouseover', this.onViewOver, this);
41553         this.list.on('mousemove', this.onViewMove, this);
41554         this.list.on('scroll', this.onViewScroll, this);
41555         
41556         if(!this.tpl){
41557             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41558         }
41559         
41560         this.view = new Roo.View(this.list, this.tpl, {
41561             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41562         });
41563         
41564         this.view.on('click', this.onViewClick, this);
41565         
41566         this.store.on('beforeload', this.onBeforeLoad, this);
41567         this.store.on('load', this.onLoad, this);
41568         this.store.on('loadexception', this.onLoadException, this);
41569         
41570         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41571             "up" : function(e){
41572                 this.inKeyMode = true;
41573                 this.selectPrev();
41574             },
41575
41576             "down" : function(e){
41577                 if(!this.isExpanded()){
41578                     this.onTriggerClick();
41579                 }else{
41580                     this.inKeyMode = true;
41581                     this.selectNext();
41582                 }
41583             },
41584
41585             "enter" : function(e){
41586                 this.collapse();
41587                 
41588                 if(this.fireEvent("specialkey", this, e)){
41589                     this.onViewClick(false);
41590                 }
41591                 
41592                 return true;
41593             },
41594
41595             "esc" : function(e){
41596                 this.collapse();
41597             },
41598
41599             "tab" : function(e){
41600                 this.collapse();
41601                 
41602                 if(this.fireEvent("specialkey", this, e)){
41603                     this.onViewClick(false);
41604                 }
41605                 
41606                 return true;
41607             },
41608
41609             scope : this,
41610
41611             doRelay : function(foo, bar, hname){
41612                 if(hname == 'down' || this.scope.isExpanded()){
41613                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41614                 }
41615                 return true;
41616             },
41617
41618             forceKeyDown: true
41619         });
41620         
41621         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41622         
41623     },
41624     
41625     initNumberEvent : function(e)
41626     {
41627         this.inputEl().on("keydown" , this.fireKey,  this);
41628         this.inputEl().on("focus", this.onFocus,  this);
41629         this.inputEl().on("blur", this.onBlur,  this);
41630         
41631         this.inputEl().relayEvent('keyup', this);
41632         
41633         if(this.indicator){
41634             this.indicator.addClass('invisible');
41635         }
41636  
41637         this.originalValue = this.getValue();
41638         
41639         if(this.validationEvent == 'keyup'){
41640             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41641             this.inputEl().on('keyup', this.filterValidation, this);
41642         }
41643         else if(this.validationEvent !== false){
41644             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41645         }
41646         
41647         if(this.selectOnFocus){
41648             this.on("focus", this.preFocus, this);
41649             
41650         }
41651         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41652             this.inputEl().on("keypress", this.filterKeys, this);
41653         } else {
41654             this.inputEl().relayEvent('keypress', this);
41655         }
41656         
41657         var allowed = "0123456789";
41658         
41659         if(this.allowDecimals){
41660             allowed += this.decimalSeparator;
41661         }
41662         
41663         if(this.allowNegative){
41664             allowed += "-";
41665         }
41666         
41667         if(this.thousandsDelimiter) {
41668             allowed += ",";
41669         }
41670         
41671         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41672         
41673         var keyPress = function(e){
41674             
41675             var k = e.getKey();
41676             
41677             var c = e.getCharCode();
41678             
41679             if(
41680                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41681                     allowed.indexOf(String.fromCharCode(c)) === -1
41682             ){
41683                 e.stopEvent();
41684                 return;
41685             }
41686             
41687             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41688                 return;
41689             }
41690             
41691             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41692                 e.stopEvent();
41693             }
41694         };
41695         
41696         this.inputEl().on("keypress", keyPress, this);
41697         
41698     },
41699     
41700     onTriggerClick : function(e)
41701     {   
41702         if(this.disabled){
41703             return;
41704         }
41705         
41706         this.page = 0;
41707         this.loadNext = false;
41708         
41709         if(this.isExpanded()){
41710             this.collapse();
41711             return;
41712         }
41713         
41714         this.hasFocus = true;
41715         
41716         if(this.triggerAction == 'all') {
41717             this.doQuery(this.allQuery, true);
41718             return;
41719         }
41720         
41721         this.doQuery(this.getRawValue());
41722     },
41723     
41724     getCurrency : function()
41725     {   
41726         var v = this.currencyEl().getValue();
41727         
41728         return v;
41729     },
41730     
41731     restrictHeight : function()
41732     {
41733         this.list.alignTo(this.currencyEl(), this.listAlign);
41734         this.list.alignTo(this.currencyEl(), this.listAlign);
41735     },
41736     
41737     onViewClick : function(view, doFocus, el, e)
41738     {
41739         var index = this.view.getSelectedIndexes()[0];
41740         
41741         var r = this.store.getAt(index);
41742         
41743         if(r){
41744             this.onSelect(r, index);
41745         }
41746     },
41747     
41748     onSelect : function(record, index){
41749         
41750         if(this.fireEvent('beforeselect', this, record, index) !== false){
41751         
41752             this.setFromCurrencyData(index > -1 ? record.data : false);
41753             
41754             this.collapse();
41755             
41756             this.fireEvent('select', this, record, index);
41757         }
41758     },
41759     
41760     setFromCurrencyData : function(o)
41761     {
41762         var currency = '';
41763         
41764         this.lastCurrency = o;
41765         
41766         if (this.currencyField) {
41767             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41768         } else {
41769             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41770         }
41771         
41772         this.lastSelectionText = currency;
41773         
41774         //setting default currency
41775         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41776             this.setCurrency(this.defaultCurrency);
41777             return;
41778         }
41779         
41780         this.setCurrency(currency);
41781     },
41782     
41783     setFromData : function(o)
41784     {
41785         var c = {};
41786         
41787         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41788         
41789         this.setFromCurrencyData(c);
41790         
41791         var value = '';
41792         
41793         if (this.name) {
41794             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41795         } else {
41796             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41797         }
41798         
41799         this.setValue(value);
41800         
41801     },
41802     
41803     setCurrency : function(v)
41804     {   
41805         this.currencyValue = v;
41806         
41807         if(this.rendered){
41808             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41809             this.validate();
41810         }
41811     },
41812     
41813     setValue : function(v)
41814     {
41815         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41816         
41817         this.value = v;
41818         
41819         if(this.rendered){
41820             
41821             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41822             
41823             this.inputEl().dom.value = (v == '') ? '' :
41824                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41825             
41826             if(!this.allowZero && v === '0') {
41827                 this.hiddenEl().dom.value = '';
41828                 this.inputEl().dom.value = '';
41829             }
41830             
41831             this.validate();
41832         }
41833     },
41834     
41835     getRawValue : function()
41836     {
41837         var v = this.inputEl().getValue();
41838         
41839         return v;
41840     },
41841     
41842     getValue : function()
41843     {
41844         return this.fixPrecision(this.parseValue(this.getRawValue()));
41845     },
41846     
41847     parseValue : function(value)
41848     {
41849         if(this.thousandsDelimiter) {
41850             value += "";
41851             r = new RegExp(",", "g");
41852             value = value.replace(r, "");
41853         }
41854         
41855         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41856         return isNaN(value) ? '' : value;
41857         
41858     },
41859     
41860     fixPrecision : function(value)
41861     {
41862         if(this.thousandsDelimiter) {
41863             value += "";
41864             r = new RegExp(",", "g");
41865             value = value.replace(r, "");
41866         }
41867         
41868         var nan = isNaN(value);
41869         
41870         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41871             return nan ? '' : value;
41872         }
41873         return parseFloat(value).toFixed(this.decimalPrecision);
41874     },
41875     
41876     decimalPrecisionFcn : function(v)
41877     {
41878         return Math.floor(v);
41879     },
41880     
41881     validateValue : function(value)
41882     {
41883         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41884             return false;
41885         }
41886         
41887         var num = this.parseValue(value);
41888         
41889         if(isNaN(num)){
41890             this.markInvalid(String.format(this.nanText, value));
41891             return false;
41892         }
41893         
41894         if(num < this.minValue){
41895             this.markInvalid(String.format(this.minText, this.minValue));
41896             return false;
41897         }
41898         
41899         if(num > this.maxValue){
41900             this.markInvalid(String.format(this.maxText, this.maxValue));
41901             return false;
41902         }
41903         
41904         return true;
41905     },
41906     
41907     validate : function()
41908     {
41909         if(this.disabled || this.allowBlank){
41910             this.markValid();
41911             return true;
41912         }
41913         
41914         var currency = this.getCurrency();
41915         
41916         if(this.validateValue(this.getRawValue()) && currency.length){
41917             this.markValid();
41918             return true;
41919         }
41920         
41921         this.markInvalid();
41922         return false;
41923     },
41924     
41925     getName: function()
41926     {
41927         return this.name;
41928     },
41929     
41930     beforeBlur : function()
41931     {
41932         if(!this.castInt){
41933             return;
41934         }
41935         
41936         var v = this.parseValue(this.getRawValue());
41937         
41938         if(v || v == 0){
41939             this.setValue(v);
41940         }
41941     },
41942     
41943     onBlur : function()
41944     {
41945         this.beforeBlur();
41946         
41947         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41948             //this.el.removeClass(this.focusClass);
41949         }
41950         
41951         this.hasFocus = false;
41952         
41953         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41954             this.validate();
41955         }
41956         
41957         var v = this.getValue();
41958         
41959         if(String(v) !== String(this.startValue)){
41960             this.fireEvent('change', this, v, this.startValue);
41961         }
41962         
41963         this.fireEvent("blur", this);
41964     },
41965     
41966     inputEl : function()
41967     {
41968         return this.el.select('.roo-money-amount-input', true).first();
41969     },
41970     
41971     currencyEl : function()
41972     {
41973         return this.el.select('.roo-money-currency-input', true).first();
41974     },
41975     
41976     hiddenEl : function()
41977     {
41978         return this.el.select('input.hidden-number-input',true).first();
41979     }
41980     
41981 });/**
41982  * @class Roo.bootstrap.BezierSignature
41983  * @extends Roo.bootstrap.Component
41984  * Bootstrap BezierSignature class
41985  * This script refer to:
41986  *    Title: Signature Pad
41987  *    Author: szimek
41988  *    Availability: https://github.com/szimek/signature_pad
41989  *
41990  * @constructor
41991  * Create a new BezierSignature
41992  * @param {Object} config The config object
41993  */
41994
41995 Roo.bootstrap.BezierSignature = function(config){
41996     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41997     this.addEvents({
41998         "resize" : true
41999     });
42000 };
42001
42002 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42003 {
42004      
42005     curve_data: [],
42006     
42007     is_empty: true,
42008     
42009     mouse_btn_down: true,
42010     
42011     /**
42012      * @cfg {int} canvas height
42013      */
42014     canvas_height: '200px',
42015     
42016     /**
42017      * @cfg {float|function} Radius of a single dot.
42018      */ 
42019     dot_size: false,
42020     
42021     /**
42022      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42023      */
42024     min_width: 0.5,
42025     
42026     /**
42027      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42028      */
42029     max_width: 2.5,
42030     
42031     /**
42032      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42033      */
42034     throttle: 16,
42035     
42036     /**
42037      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42038      */
42039     min_distance: 5,
42040     
42041     /**
42042      * @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.
42043      */
42044     bg_color: 'rgba(0, 0, 0, 0)',
42045     
42046     /**
42047      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42048      */
42049     dot_color: 'black',
42050     
42051     /**
42052      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42053      */ 
42054     velocity_filter_weight: 0.7,
42055     
42056     /**
42057      * @cfg {function} Callback when stroke begin. 
42058      */
42059     onBegin: false,
42060     
42061     /**
42062      * @cfg {function} Callback when stroke end.
42063      */
42064     onEnd: false,
42065     
42066     getAutoCreate : function()
42067     {
42068         var cls = 'roo-signature column';
42069         
42070         if(this.cls){
42071             cls += ' ' + this.cls;
42072         }
42073         
42074         var col_sizes = [
42075             'lg',
42076             'md',
42077             'sm',
42078             'xs'
42079         ];
42080         
42081         for(var i = 0; i < col_sizes.length; i++) {
42082             if(this[col_sizes[i]]) {
42083                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42084             }
42085         }
42086         
42087         var cfg = {
42088             tag: 'div',
42089             cls: cls,
42090             cn: [
42091                 {
42092                     tag: 'div',
42093                     cls: 'roo-signature-body',
42094                     cn: [
42095                         {
42096                             tag: 'canvas',
42097                             cls: 'roo-signature-body-canvas',
42098                             height: this.canvas_height,
42099                             width: this.canvas_width
42100                         }
42101                     ]
42102                 },
42103                 {
42104                     tag: 'input',
42105                     type: 'file',
42106                     style: 'display: none'
42107                 }
42108             ]
42109         };
42110         
42111         return cfg;
42112     },
42113     
42114     initEvents: function() 
42115     {
42116         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42117         
42118         var canvas = this.canvasEl();
42119         
42120         // mouse && touch event swapping...
42121         canvas.dom.style.touchAction = 'none';
42122         canvas.dom.style.msTouchAction = 'none';
42123         
42124         this.mouse_btn_down = false;
42125         canvas.on('mousedown', this._handleMouseDown, this);
42126         canvas.on('mousemove', this._handleMouseMove, this);
42127         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42128         
42129         if (window.PointerEvent) {
42130             canvas.on('pointerdown', this._handleMouseDown, this);
42131             canvas.on('pointermove', this._handleMouseMove, this);
42132             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42133         }
42134         
42135         if ('ontouchstart' in window) {
42136             canvas.on('touchstart', this._handleTouchStart, this);
42137             canvas.on('touchmove', this._handleTouchMove, this);
42138             canvas.on('touchend', this._handleTouchEnd, this);
42139         }
42140         
42141         Roo.EventManager.onWindowResize(this.resize, this, true);
42142         
42143         // file input event
42144         this.fileEl().on('change', this.uploadImage, this);
42145         
42146         this.clear();
42147         
42148         this.resize();
42149     },
42150     
42151     resize: function(){
42152         
42153         var canvas = this.canvasEl().dom;
42154         var ctx = this.canvasElCtx();
42155         var img_data = false;
42156         
42157         if(canvas.width > 0) {
42158             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42159         }
42160         // setting canvas width will clean img data
42161         canvas.width = 0;
42162         
42163         var style = window.getComputedStyle ? 
42164             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42165             
42166         var padding_left = parseInt(style.paddingLeft) || 0;
42167         var padding_right = parseInt(style.paddingRight) || 0;
42168         
42169         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42170         
42171         if(img_data) {
42172             ctx.putImageData(img_data, 0, 0);
42173         }
42174     },
42175     
42176     _handleMouseDown: function(e)
42177     {
42178         if (e.browserEvent.which === 1) {
42179             this.mouse_btn_down = true;
42180             this.strokeBegin(e);
42181         }
42182     },
42183     
42184     _handleMouseMove: function (e)
42185     {
42186         if (this.mouse_btn_down) {
42187             this.strokeMoveUpdate(e);
42188         }
42189     },
42190     
42191     _handleMouseUp: function (e)
42192     {
42193         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42194             this.mouse_btn_down = false;
42195             this.strokeEnd(e);
42196         }
42197     },
42198     
42199     _handleTouchStart: function (e) {
42200         
42201         e.preventDefault();
42202         if (e.browserEvent.targetTouches.length === 1) {
42203             // var touch = e.browserEvent.changedTouches[0];
42204             // this.strokeBegin(touch);
42205             
42206              this.strokeBegin(e); // assume e catching the correct xy...
42207         }
42208     },
42209     
42210     _handleTouchMove: function (e) {
42211         e.preventDefault();
42212         // var touch = event.targetTouches[0];
42213         // _this._strokeMoveUpdate(touch);
42214         this.strokeMoveUpdate(e);
42215     },
42216     
42217     _handleTouchEnd: function (e) {
42218         var wasCanvasTouched = e.target === this.canvasEl().dom;
42219         if (wasCanvasTouched) {
42220             e.preventDefault();
42221             // var touch = event.changedTouches[0];
42222             // _this._strokeEnd(touch);
42223             this.strokeEnd(e);
42224         }
42225     },
42226     
42227     reset: function () {
42228         this._lastPoints = [];
42229         this._lastVelocity = 0;
42230         this._lastWidth = (this.min_width + this.max_width) / 2;
42231         this.canvasElCtx().fillStyle = this.dot_color;
42232     },
42233     
42234     strokeMoveUpdate: function(e)
42235     {
42236         this.strokeUpdate(e);
42237         
42238         if (this.throttle) {
42239             this.throttleStroke(this.strokeUpdate, this.throttle);
42240         }
42241         else {
42242             this.strokeUpdate(e);
42243         }
42244     },
42245     
42246     strokeBegin: function(e)
42247     {
42248         var newPointGroup = {
42249             color: this.dot_color,
42250             points: []
42251         };
42252         
42253         if (typeof this.onBegin === 'function') {
42254             this.onBegin(e);
42255         }
42256         
42257         this.curve_data.push(newPointGroup);
42258         this.reset();
42259         this.strokeUpdate(e);
42260     },
42261     
42262     strokeUpdate: function(e)
42263     {
42264         var rect = this.canvasEl().dom.getBoundingClientRect();
42265         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42266         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42267         var lastPoints = lastPointGroup.points;
42268         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42269         var isLastPointTooClose = lastPoint
42270             ? point.distanceTo(lastPoint) <= this.min_distance
42271             : false;
42272         var color = lastPointGroup.color;
42273         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42274             var curve = this.addPoint(point);
42275             if (!lastPoint) {
42276                 this.drawDot({color: color, point: point});
42277             }
42278             else if (curve) {
42279                 this.drawCurve({color: color, curve: curve});
42280             }
42281             lastPoints.push({
42282                 time: point.time,
42283                 x: point.x,
42284                 y: point.y
42285             });
42286         }
42287     },
42288     
42289     strokeEnd: function(e)
42290     {
42291         this.strokeUpdate(e);
42292         if (typeof this.onEnd === 'function') {
42293             this.onEnd(e);
42294         }
42295     },
42296     
42297     addPoint:  function (point) {
42298         var _lastPoints = this._lastPoints;
42299         _lastPoints.push(point);
42300         if (_lastPoints.length > 2) {
42301             if (_lastPoints.length === 3) {
42302                 _lastPoints.unshift(_lastPoints[0]);
42303             }
42304             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42305             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42306             _lastPoints.shift();
42307             return curve;
42308         }
42309         return null;
42310     },
42311     
42312     calculateCurveWidths: function (startPoint, endPoint) {
42313         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42314             (1 - this.velocity_filter_weight) * this._lastVelocity;
42315
42316         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42317         var widths = {
42318             end: newWidth,
42319             start: this._lastWidth
42320         };
42321         
42322         this._lastVelocity = velocity;
42323         this._lastWidth = newWidth;
42324         return widths;
42325     },
42326     
42327     drawDot: function (_a) {
42328         var color = _a.color, point = _a.point;
42329         var ctx = this.canvasElCtx();
42330         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42331         ctx.beginPath();
42332         this.drawCurveSegment(point.x, point.y, width);
42333         ctx.closePath();
42334         ctx.fillStyle = color;
42335         ctx.fill();
42336     },
42337     
42338     drawCurve: function (_a) {
42339         var color = _a.color, curve = _a.curve;
42340         var ctx = this.canvasElCtx();
42341         var widthDelta = curve.endWidth - curve.startWidth;
42342         var drawSteps = Math.floor(curve.length()) * 2;
42343         ctx.beginPath();
42344         ctx.fillStyle = color;
42345         for (var i = 0; i < drawSteps; i += 1) {
42346         var t = i / drawSteps;
42347         var tt = t * t;
42348         var ttt = tt * t;
42349         var u = 1 - t;
42350         var uu = u * u;
42351         var uuu = uu * u;
42352         var x = uuu * curve.startPoint.x;
42353         x += 3 * uu * t * curve.control1.x;
42354         x += 3 * u * tt * curve.control2.x;
42355         x += ttt * curve.endPoint.x;
42356         var y = uuu * curve.startPoint.y;
42357         y += 3 * uu * t * curve.control1.y;
42358         y += 3 * u * tt * curve.control2.y;
42359         y += ttt * curve.endPoint.y;
42360         var width = curve.startWidth + ttt * widthDelta;
42361         this.drawCurveSegment(x, y, width);
42362         }
42363         ctx.closePath();
42364         ctx.fill();
42365     },
42366     
42367     drawCurveSegment: function (x, y, width) {
42368         var ctx = this.canvasElCtx();
42369         ctx.moveTo(x, y);
42370         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42371         this.is_empty = false;
42372     },
42373     
42374     clear: function()
42375     {
42376         var ctx = this.canvasElCtx();
42377         var canvas = this.canvasEl().dom;
42378         ctx.fillStyle = this.bg_color;
42379         ctx.clearRect(0, 0, canvas.width, canvas.height);
42380         ctx.fillRect(0, 0, canvas.width, canvas.height);
42381         this.curve_data = [];
42382         this.reset();
42383         this.is_empty = true;
42384     },
42385     
42386     fileEl: function()
42387     {
42388         return  this.el.select('input',true).first();
42389     },
42390     
42391     canvasEl: function()
42392     {
42393         return this.el.select('canvas',true).first();
42394     },
42395     
42396     canvasElCtx: function()
42397     {
42398         return this.el.select('canvas',true).first().dom.getContext('2d');
42399     },
42400     
42401     getImage: function(type)
42402     {
42403         if(this.is_empty) {
42404             return false;
42405         }
42406         
42407         // encryption ?
42408         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42409     },
42410     
42411     drawFromImage: function(img_src)
42412     {
42413         var img = new Image();
42414         
42415         img.onload = function(){
42416             this.canvasElCtx().drawImage(img, 0, 0);
42417         }.bind(this);
42418         
42419         img.src = img_src;
42420         
42421         this.is_empty = false;
42422     },
42423     
42424     selectImage: function()
42425     {
42426         this.fileEl().dom.click();
42427     },
42428     
42429     uploadImage: function(e)
42430     {
42431         var reader = new FileReader();
42432         
42433         reader.onload = function(e){
42434             var img = new Image();
42435             img.onload = function(){
42436                 this.reset();
42437                 this.canvasElCtx().drawImage(img, 0, 0);
42438             }.bind(this);
42439             img.src = e.target.result;
42440         }.bind(this);
42441         
42442         reader.readAsDataURL(e.target.files[0]);
42443     },
42444     
42445     // Bezier Point Constructor
42446     Point: (function () {
42447         function Point(x, y, time) {
42448             this.x = x;
42449             this.y = y;
42450             this.time = time || Date.now();
42451         }
42452         Point.prototype.distanceTo = function (start) {
42453             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42454         };
42455         Point.prototype.equals = function (other) {
42456             return this.x === other.x && this.y === other.y && this.time === other.time;
42457         };
42458         Point.prototype.velocityFrom = function (start) {
42459             return this.time !== start.time
42460             ? this.distanceTo(start) / (this.time - start.time)
42461             : 0;
42462         };
42463         return Point;
42464     }()),
42465     
42466     
42467     // Bezier Constructor
42468     Bezier: (function () {
42469         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42470             this.startPoint = startPoint;
42471             this.control2 = control2;
42472             this.control1 = control1;
42473             this.endPoint = endPoint;
42474             this.startWidth = startWidth;
42475             this.endWidth = endWidth;
42476         }
42477         Bezier.fromPoints = function (points, widths, scope) {
42478             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42479             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42480             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42481         };
42482         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42483             var dx1 = s1.x - s2.x;
42484             var dy1 = s1.y - s2.y;
42485             var dx2 = s2.x - s3.x;
42486             var dy2 = s2.y - s3.y;
42487             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42488             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42489             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42490             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42491             var dxm = m1.x - m2.x;
42492             var dym = m1.y - m2.y;
42493             var k = l2 / (l1 + l2);
42494             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42495             var tx = s2.x - cm.x;
42496             var ty = s2.y - cm.y;
42497             return {
42498                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42499                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42500             };
42501         };
42502         Bezier.prototype.length = function () {
42503             var steps = 10;
42504             var length = 0;
42505             var px;
42506             var py;
42507             for (var i = 0; i <= steps; i += 1) {
42508                 var t = i / steps;
42509                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42510                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42511                 if (i > 0) {
42512                     var xdiff = cx - px;
42513                     var ydiff = cy - py;
42514                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42515                 }
42516                 px = cx;
42517                 py = cy;
42518             }
42519             return length;
42520         };
42521         Bezier.prototype.point = function (t, start, c1, c2, end) {
42522             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42523             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42524             + (3.0 * c2 * (1.0 - t) * t * t)
42525             + (end * t * t * t);
42526         };
42527         return Bezier;
42528     }()),
42529     
42530     throttleStroke: function(fn, wait) {
42531       if (wait === void 0) { wait = 250; }
42532       var previous = 0;
42533       var timeout = null;
42534       var result;
42535       var storedContext;
42536       var storedArgs;
42537       var later = function () {
42538           previous = Date.now();
42539           timeout = null;
42540           result = fn.apply(storedContext, storedArgs);
42541           if (!timeout) {
42542               storedContext = null;
42543               storedArgs = [];
42544           }
42545       };
42546       return function wrapper() {
42547           var args = [];
42548           for (var _i = 0; _i < arguments.length; _i++) {
42549               args[_i] = arguments[_i];
42550           }
42551           var now = Date.now();
42552           var remaining = wait - (now - previous);
42553           storedContext = this;
42554           storedArgs = args;
42555           if (remaining <= 0 || remaining > wait) {
42556               if (timeout) {
42557                   clearTimeout(timeout);
42558                   timeout = null;
42559               }
42560               previous = now;
42561               result = fn.apply(storedContext, storedArgs);
42562               if (!timeout) {
42563                   storedContext = null;
42564                   storedArgs = [];
42565               }
42566           }
42567           else if (!timeout) {
42568               timeout = window.setTimeout(later, remaining);
42569           }
42570           return result;
42571       };
42572   }
42573   
42574 });
42575
42576  
42577
42578