roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','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         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size] + (
1079                 size == 'xs' ? (' col-' + cfg[size] ) : '' // bs4 col-{num} replaces col-xs
1080             );
1081             
1082         });
1083         
1084         if (this.hidden) {
1085             cfg.cls += ' hidden';
1086         }
1087         
1088         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089             cfg.cls +=' alert alert-' + this.alert;
1090         }
1091         
1092         
1093         if (this.html.length) {
1094             cfg.html = this.html;
1095         }
1096         if (this.fa) {
1097             var fasize = '';
1098             if (this.fasize > 1) {
1099                 fasize = ' fa-' + this.fasize + 'x';
1100             }
1101             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1102             
1103             
1104         }
1105         if (this.icon) {
1106             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1107         }
1108         
1109         return cfg;
1110     }
1111    
1112 });
1113
1114  
1115
1116  /*
1117  * - LGPL
1118  *
1119  * page container.
1120  * 
1121  */
1122
1123
1124 /**
1125  * @class Roo.bootstrap.Container
1126  * @extends Roo.bootstrap.Component
1127  * Bootstrap Container class
1128  * @cfg {Boolean} jumbotron is it a jumbotron element
1129  * @cfg {String} html content of element
1130  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1132  * @cfg {String} header content of header (for panel)
1133  * @cfg {String} footer content of footer (for panel)
1134  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135  * @cfg {String} tag (header|aside|section) type of HTML tag.
1136  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137  * @cfg {String} fa font awesome icon
1138  * @cfg {String} icon (info-sign|check|...) glyphicon name
1139  * @cfg {Boolean} hidden (true|false) hide the element
1140  * @cfg {Boolean} expandable (true|false) default false
1141  * @cfg {Boolean} expanded (true|false) default true
1142  * @cfg {String} rheader contet on the right of header
1143  * @cfg {Boolean} clickable (true|false) default false
1144
1145  *     
1146  * @constructor
1147  * Create a new Container
1148  * @param {Object} config The config object
1149  */
1150
1151 Roo.bootstrap.Container = function(config){
1152     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1153     
1154     this.addEvents({
1155         // raw events
1156          /**
1157          * @event expand
1158          * After the panel has been expand
1159          * 
1160          * @param {Roo.bootstrap.Container} this
1161          */
1162         "expand" : true,
1163         /**
1164          * @event collapse
1165          * After the panel has been collapsed
1166          * 
1167          * @param {Roo.bootstrap.Container} this
1168          */
1169         "collapse" : true,
1170         /**
1171          * @event click
1172          * When a element is chick
1173          * @param {Roo.bootstrap.Container} this
1174          * @param {Roo.EventObject} e
1175          */
1176         "click" : true
1177     });
1178 };
1179
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1181     
1182     jumbotron : false,
1183     well: '',
1184     panel : '',
1185     header: '',
1186     footer : '',
1187     sticky: '',
1188     tag : false,
1189     alert : false,
1190     fa: false,
1191     icon : false,
1192     expandable : false,
1193     rheader : '',
1194     expanded : true,
1195     clickable: false,
1196   
1197      
1198     getChildContainer : function() {
1199         
1200         if(!this.el){
1201             return false;
1202         }
1203         
1204         if (this.panel.length) {
1205             return this.el.select('.panel-body',true).first();
1206         }
1207         
1208         return this.el;
1209     },
1210     
1211     
1212     getAutoCreate : function(){
1213         
1214         var cfg = {
1215             tag : this.tag || 'div',
1216             html : '',
1217             cls : ''
1218         };
1219         if (this.jumbotron) {
1220             cfg.cls = 'jumbotron';
1221         }
1222         
1223         
1224         
1225         // - this is applied by the parent..
1226         //if (this.cls) {
1227         //    cfg.cls = this.cls + '';
1228         //}
1229         
1230         if (this.sticky.length) {
1231             
1232             var bd = Roo.get(document.body);
1233             if (!bd.hasClass('bootstrap-sticky')) {
1234                 bd.addClass('bootstrap-sticky');
1235                 Roo.select('html',true).setStyle('height', '100%');
1236             }
1237              
1238             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239         }
1240         
1241         
1242         if (this.well.length) {
1243             switch (this.well) {
1244                 case 'lg':
1245                 case 'sm':
1246                     cfg.cls +=' well well-' +this.well;
1247                     break;
1248                 default:
1249                     cfg.cls +=' well';
1250                     break;
1251             }
1252         }
1253         
1254         if (this.hidden) {
1255             cfg.cls += ' hidden';
1256         }
1257         
1258         
1259         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260             cfg.cls +=' alert alert-' + this.alert;
1261         }
1262         
1263         var body = cfg;
1264         
1265         if (this.panel.length) {
1266             cfg.cls += ' panel panel-' + this.panel;
1267             cfg.cn = [];
1268             if (this.header.length) {
1269                 
1270                 var h = [];
1271                 
1272                 if(this.expandable){
1273                     
1274                     cfg.cls = cfg.cls + ' expandable';
1275                     
1276                     h.push({
1277                         tag: 'i',
1278                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1279                     });
1280                     
1281                 }
1282                 
1283                 h.push(
1284                     {
1285                         tag: 'span',
1286                         cls : 'panel-title',
1287                         html : (this.expandable ? '&nbsp;' : '') + this.header
1288                     },
1289                     {
1290                         tag: 'span',
1291                         cls: 'panel-header-right',
1292                         html: this.rheader
1293                     }
1294                 );
1295                 
1296                 cfg.cn.push({
1297                     cls : 'panel-heading',
1298                     style : this.expandable ? 'cursor: pointer' : '',
1299                     cn : h
1300                 });
1301                 
1302             }
1303             
1304             body = false;
1305             cfg.cn.push({
1306                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1307                 html : this.html
1308             });
1309             
1310             
1311             if (this.footer.length) {
1312                 cfg.cn.push({
1313                     cls : 'panel-footer',
1314                     html : this.footer
1315                     
1316                 });
1317             }
1318             
1319         }
1320         
1321         if (body) {
1322             body.html = this.html || cfg.html;
1323             // prefix with the icons..
1324             if (this.fa) {
1325                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1326             }
1327             if (this.icon) {
1328                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1329             }
1330             
1331             
1332         }
1333         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334             cfg.cls =  'container';
1335         }
1336         
1337         return cfg;
1338     },
1339     
1340     initEvents: function() 
1341     {
1342         if(this.expandable){
1343             var headerEl = this.headerEl();
1344         
1345             if(headerEl){
1346                 headerEl.on('click', this.onToggleClick, this);
1347             }
1348         }
1349         
1350         if(this.clickable){
1351             this.el.on('click', this.onClick, this);
1352         }
1353         
1354     },
1355     
1356     onToggleClick : function()
1357     {
1358         var headerEl = this.headerEl();
1359         
1360         if(!headerEl){
1361             return;
1362         }
1363         
1364         if(this.expanded){
1365             this.collapse();
1366             return;
1367         }
1368         
1369         this.expand();
1370     },
1371     
1372     expand : function()
1373     {
1374         if(this.fireEvent('expand', this)) {
1375             
1376             this.expanded = true;
1377             
1378             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1379             
1380             this.el.select('.panel-body',true).first().removeClass('hide');
1381             
1382             var toggleEl = this.toggleEl();
1383
1384             if(!toggleEl){
1385                 return;
1386             }
1387
1388             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1389         }
1390         
1391     },
1392     
1393     collapse : function()
1394     {
1395         if(this.fireEvent('collapse', this)) {
1396             
1397             this.expanded = false;
1398             
1399             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400             this.el.select('.panel-body',true).first().addClass('hide');
1401         
1402             var toggleEl = this.toggleEl();
1403
1404             if(!toggleEl){
1405                 return;
1406             }
1407
1408             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409         }
1410     },
1411     
1412     toggleEl : function()
1413     {
1414         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415             return;
1416         }
1417         
1418         return this.el.select('.panel-heading .fa',true).first();
1419     },
1420     
1421     headerEl : function()
1422     {
1423         if(!this.el || !this.panel.length || !this.header.length){
1424             return;
1425         }
1426         
1427         return this.el.select('.panel-heading',true).first()
1428     },
1429     
1430     bodyEl : function()
1431     {
1432         if(!this.el || !this.panel.length){
1433             return;
1434         }
1435         
1436         return this.el.select('.panel-body',true).first()
1437     },
1438     
1439     titleEl : function()
1440     {
1441         if(!this.el || !this.panel.length || !this.header.length){
1442             return;
1443         }
1444         
1445         return this.el.select('.panel-title',true).first();
1446     },
1447     
1448     setTitle : function(v)
1449     {
1450         var titleEl = this.titleEl();
1451         
1452         if(!titleEl){
1453             return;
1454         }
1455         
1456         titleEl.dom.innerHTML = v;
1457     },
1458     
1459     getTitle : function()
1460     {
1461         
1462         var titleEl = this.titleEl();
1463         
1464         if(!titleEl){
1465             return '';
1466         }
1467         
1468         return titleEl.dom.innerHTML;
1469     },
1470     
1471     setRightTitle : function(v)
1472     {
1473         var t = this.el.select('.panel-header-right',true).first();
1474         
1475         if(!t){
1476             return;
1477         }
1478         
1479         t.dom.innerHTML = v;
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         e.preventDefault();
1485         
1486         this.fireEvent('click', this, e);
1487     }
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Img
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Img class
1502  * @cfg {Boolean} imgResponsive false | true
1503  * @cfg {String} border rounded | circle | thumbnail
1504  * @cfg {String} src image source
1505  * @cfg {String} alt image alternative text
1506  * @cfg {String} href a tag href
1507  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1508  * @cfg {String} xsUrl xs image source
1509  * @cfg {String} smUrl sm image source
1510  * @cfg {String} mdUrl md image source
1511  * @cfg {String} lgUrl lg image source
1512  * 
1513  * @constructor
1514  * Create a new Input
1515  * @param {Object} config The config object
1516  */
1517
1518 Roo.bootstrap.Img = function(config){
1519     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1520     
1521     this.addEvents({
1522         // img events
1523         /**
1524          * @event click
1525          * The img click event for the img.
1526          * @param {Roo.EventObject} e
1527          */
1528         "click" : true
1529     });
1530 };
1531
1532 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1533     
1534     imgResponsive: true,
1535     border: '',
1536     src: 'about:blank',
1537     href: false,
1538     target: false,
1539     xsUrl: '',
1540     smUrl: '',
1541     mdUrl: '',
1542     lgUrl: '',
1543
1544     getAutoCreate : function()
1545     {   
1546         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1547             return this.createSingleImg();
1548         }
1549         
1550         var cfg = {
1551             tag: 'div',
1552             cls: 'roo-image-responsive-group',
1553             cn: []
1554         };
1555         var _this = this;
1556         
1557         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1558             
1559             if(!_this[size + 'Url']){
1560                 return;
1561             }
1562             
1563             var img = {
1564                 tag: 'img',
1565                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1566                 html: _this.html || cfg.html,
1567                 src: _this[size + 'Url']
1568             };
1569             
1570             img.cls += ' roo-image-responsive-' + size;
1571             
1572             var s = ['xs', 'sm', 'md', 'lg'];
1573             
1574             s.splice(s.indexOf(size), 1);
1575             
1576             Roo.each(s, function(ss){
1577                 img.cls += ' hidden-' + ss;
1578             });
1579             
1580             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1581                 cfg.cls += ' img-' + _this.border;
1582             }
1583             
1584             if(_this.alt){
1585                 cfg.alt = _this.alt;
1586             }
1587             
1588             if(_this.href){
1589                 var a = {
1590                     tag: 'a',
1591                     href: _this.href,
1592                     cn: [
1593                         img
1594                     ]
1595                 };
1596
1597                 if(this.target){
1598                     a.target = _this.target;
1599                 }
1600             }
1601             
1602             cfg.cn.push((_this.href) ? a : img);
1603             
1604         });
1605         
1606         return cfg;
1607     },
1608     
1609     createSingleImg : function()
1610     {
1611         var cfg = {
1612             tag: 'img',
1613             cls: (this.imgResponsive) ? 'img-responsive' : '',
1614             html : null,
1615             src : 'about:blank'  // just incase src get's set to undefined?!?
1616         };
1617         
1618         cfg.html = this.html || cfg.html;
1619         
1620         cfg.src = this.src || cfg.src;
1621         
1622         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1623             cfg.cls += ' img-' + this.border;
1624         }
1625         
1626         if(this.alt){
1627             cfg.alt = this.alt;
1628         }
1629         
1630         if(this.href){
1631             var a = {
1632                 tag: 'a',
1633                 href: this.href,
1634                 cn: [
1635                     cfg
1636                 ]
1637             };
1638             
1639             if(this.target){
1640                 a.target = this.target;
1641             }
1642             
1643         }
1644         
1645         return (this.href) ? a : cfg;
1646     },
1647     
1648     initEvents: function() 
1649     {
1650         if(!this.href){
1651             this.el.on('click', this.onClick, this);
1652         }
1653         
1654     },
1655     
1656     onClick : function(e)
1657     {
1658         Roo.log('img onclick');
1659         this.fireEvent('click', this, e);
1660     },
1661     /**
1662      * Sets the url of the image - used to update it
1663      * @param {String} url the url of the image
1664      */
1665     
1666     setSrc : function(url)
1667     {
1668         this.src =  url;
1669         
1670         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1671             this.el.dom.src =  url;
1672             return;
1673         }
1674         
1675         this.el.select('img', true).first().dom.src =  url;
1676     }
1677     
1678     
1679    
1680 });
1681
1682  /*
1683  * - LGPL
1684  *
1685  * image
1686  * 
1687  */
1688
1689
1690 /**
1691  * @class Roo.bootstrap.Link
1692  * @extends Roo.bootstrap.Component
1693  * Bootstrap Link Class
1694  * @cfg {String} alt image alternative text
1695  * @cfg {String} href a tag href
1696  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1697  * @cfg {String} html the content of the link.
1698  * @cfg {String} anchor name for the anchor link
1699  * @cfg {String} fa - favicon
1700
1701  * @cfg {Boolean} preventDefault (true | false) default false
1702
1703  * 
1704  * @constructor
1705  * Create a new Input
1706  * @param {Object} config The config object
1707  */
1708
1709 Roo.bootstrap.Link = function(config){
1710     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1711     
1712     this.addEvents({
1713         // img events
1714         /**
1715          * @event click
1716          * The img click event for the img.
1717          * @param {Roo.EventObject} e
1718          */
1719         "click" : true
1720     });
1721 };
1722
1723 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1724     
1725     href: false,
1726     target: false,
1727     preventDefault: false,
1728     anchor : false,
1729     alt : false,
1730     fa: false,
1731
1732
1733     getAutoCreate : function()
1734     {
1735         var html = this.html || '';
1736         
1737         if (this.fa !== false) {
1738             html = '<i class="fa fa-' + this.fa + '"></i>';
1739         }
1740         var cfg = {
1741             tag: 'a'
1742         };
1743         // anchor's do not require html/href...
1744         if (this.anchor === false) {
1745             cfg.html = html;
1746             cfg.href = this.href || '#';
1747         } else {
1748             cfg.name = this.anchor;
1749             if (this.html !== false || this.fa !== false) {
1750                 cfg.html = html;
1751             }
1752             if (this.href !== false) {
1753                 cfg.href = this.href;
1754             }
1755         }
1756         
1757         if(this.alt !== false){
1758             cfg.alt = this.alt;
1759         }
1760         
1761         
1762         if(this.target !== false) {
1763             cfg.target = this.target;
1764         }
1765         
1766         return cfg;
1767     },
1768     
1769     initEvents: function() {
1770         
1771         if(!this.href || this.preventDefault){
1772             this.el.on('click', this.onClick, this);
1773         }
1774     },
1775     
1776     onClick : function(e)
1777     {
1778         if(this.preventDefault){
1779             e.preventDefault();
1780         }
1781         //Roo.log('img onclick');
1782         this.fireEvent('click', this, e);
1783     }
1784    
1785 });
1786
1787  /*
1788  * - LGPL
1789  *
1790  * header
1791  * 
1792  */
1793
1794 /**
1795  * @class Roo.bootstrap.Header
1796  * @extends Roo.bootstrap.Component
1797  * Bootstrap Header class
1798  * @cfg {String} html content of header
1799  * @cfg {Number} level (1|2|3|4|5|6) default 1
1800  * 
1801  * @constructor
1802  * Create a new Header
1803  * @param {Object} config The config object
1804  */
1805
1806
1807 Roo.bootstrap.Header  = function(config){
1808     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1809 };
1810
1811 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1812     
1813     //href : false,
1814     html : false,
1815     level : 1,
1816     
1817     
1818     
1819     getAutoCreate : function(){
1820         
1821         
1822         
1823         var cfg = {
1824             tag: 'h' + (1 *this.level),
1825             html: this.html || ''
1826         } ;
1827         
1828         return cfg;
1829     }
1830    
1831 });
1832
1833  
1834
1835  /*
1836  * Based on:
1837  * Ext JS Library 1.1.1
1838  * Copyright(c) 2006-2007, Ext JS, LLC.
1839  *
1840  * Originally Released Under LGPL - original licence link has changed is not relivant.
1841  *
1842  * Fork - LGPL
1843  * <script type="text/javascript">
1844  */
1845  
1846 /**
1847  * @class Roo.bootstrap.MenuMgr
1848  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1849  * @singleton
1850  */
1851 Roo.bootstrap.MenuMgr = function(){
1852    var menus, active, groups = {}, attached = false, lastShow = new Date();
1853
1854    // private - called when first menu is created
1855    function init(){
1856        menus = {};
1857        active = new Roo.util.MixedCollection();
1858        Roo.get(document).addKeyListener(27, function(){
1859            if(active.length > 0){
1860                hideAll();
1861            }
1862        });
1863    }
1864
1865    // private
1866    function hideAll(){
1867        if(active && active.length > 0){
1868            var c = active.clone();
1869            c.each(function(m){
1870                m.hide();
1871            });
1872        }
1873    }
1874
1875    // private
1876    function onHide(m){
1877        active.remove(m);
1878        if(active.length < 1){
1879            Roo.get(document).un("mouseup", onMouseDown);
1880             
1881            attached = false;
1882        }
1883    }
1884
1885    // private
1886    function onShow(m){
1887        var last = active.last();
1888        lastShow = new Date();
1889        active.add(m);
1890        if(!attached){
1891           Roo.get(document).on("mouseup", onMouseDown);
1892            
1893            attached = true;
1894        }
1895        if(m.parentMenu){
1896           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1897           m.parentMenu.activeChild = m;
1898        }else if(last && last.isVisible()){
1899           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1900        }
1901    }
1902
1903    // private
1904    function onBeforeHide(m){
1905        if(m.activeChild){
1906            m.activeChild.hide();
1907        }
1908        if(m.autoHideTimer){
1909            clearTimeout(m.autoHideTimer);
1910            delete m.autoHideTimer;
1911        }
1912    }
1913
1914    // private
1915    function onBeforeShow(m){
1916        var pm = m.parentMenu;
1917        if(!pm && !m.allowOtherMenus){
1918            hideAll();
1919        }else if(pm && pm.activeChild && active != m){
1920            pm.activeChild.hide();
1921        }
1922    }
1923
1924    // private this should really trigger on mouseup..
1925    function onMouseDown(e){
1926         Roo.log("on Mouse Up");
1927         
1928         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1929             Roo.log("MenuManager hideAll");
1930             hideAll();
1931             e.stopEvent();
1932         }
1933         
1934         
1935    }
1936
1937    // private
1938    function onBeforeCheck(mi, state){
1939        if(state){
1940            var g = groups[mi.group];
1941            for(var i = 0, l = g.length; i < l; i++){
1942                if(g[i] != mi){
1943                    g[i].setChecked(false);
1944                }
1945            }
1946        }
1947    }
1948
1949    return {
1950
1951        /**
1952         * Hides all menus that are currently visible
1953         */
1954        hideAll : function(){
1955             hideAll();  
1956        },
1957
1958        // private
1959        register : function(menu){
1960            if(!menus){
1961                init();
1962            }
1963            menus[menu.id] = menu;
1964            menu.on("beforehide", onBeforeHide);
1965            menu.on("hide", onHide);
1966            menu.on("beforeshow", onBeforeShow);
1967            menu.on("show", onShow);
1968            var g = menu.group;
1969            if(g && menu.events["checkchange"]){
1970                if(!groups[g]){
1971                    groups[g] = [];
1972                }
1973                groups[g].push(menu);
1974                menu.on("checkchange", onCheck);
1975            }
1976        },
1977
1978         /**
1979          * Returns a {@link Roo.menu.Menu} object
1980          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1981          * be used to generate and return a new Menu instance.
1982          */
1983        get : function(menu){
1984            if(typeof menu == "string"){ // menu id
1985                return menus[menu];
1986            }else if(menu.events){  // menu instance
1987                return menu;
1988            }
1989            /*else if(typeof menu.length == 'number'){ // array of menu items?
1990                return new Roo.bootstrap.Menu({items:menu});
1991            }else{ // otherwise, must be a config
1992                return new Roo.bootstrap.Menu(menu);
1993            }
1994            */
1995            return false;
1996        },
1997
1998        // private
1999        unregister : function(menu){
2000            delete menus[menu.id];
2001            menu.un("beforehide", onBeforeHide);
2002            menu.un("hide", onHide);
2003            menu.un("beforeshow", onBeforeShow);
2004            menu.un("show", onShow);
2005            var g = menu.group;
2006            if(g && menu.events["checkchange"]){
2007                groups[g].remove(menu);
2008                menu.un("checkchange", onCheck);
2009            }
2010        },
2011
2012        // private
2013        registerCheckable : function(menuItem){
2014            var g = menuItem.group;
2015            if(g){
2016                if(!groups[g]){
2017                    groups[g] = [];
2018                }
2019                groups[g].push(menuItem);
2020                menuItem.on("beforecheckchange", onBeforeCheck);
2021            }
2022        },
2023
2024        // private
2025        unregisterCheckable : function(menuItem){
2026            var g = menuItem.group;
2027            if(g){
2028                groups[g].remove(menuItem);
2029                menuItem.un("beforecheckchange", onBeforeCheck);
2030            }
2031        }
2032    };
2033 }();/*
2034  * - LGPL
2035  *
2036  * menu
2037  * 
2038  */
2039
2040 /**
2041  * @class Roo.bootstrap.Menu
2042  * @extends Roo.bootstrap.Component
2043  * Bootstrap Menu class - container for MenuItems
2044  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2045  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2046  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2047  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2048  * 
2049  * @constructor
2050  * Create a new Menu
2051  * @param {Object} config The config object
2052  */
2053
2054
2055 Roo.bootstrap.Menu = function(config){
2056     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2057     if (this.registerMenu && this.type != 'treeview')  {
2058         Roo.bootstrap.MenuMgr.register(this);
2059     }
2060     
2061     
2062     this.addEvents({
2063         /**
2064          * @event beforeshow
2065          * Fires before this menu is displayed (return false to block)
2066          * @param {Roo.menu.Menu} this
2067          */
2068         beforeshow : true,
2069         /**
2070          * @event beforehide
2071          * Fires before this menu is hidden (return false to block)
2072          * @param {Roo.menu.Menu} this
2073          */
2074         beforehide : true,
2075         /**
2076          * @event show
2077          * Fires after this menu is displayed
2078          * @param {Roo.menu.Menu} this
2079          */
2080         show : true,
2081         /**
2082          * @event hide
2083          * Fires after this menu is hidden
2084          * @param {Roo.menu.Menu} this
2085          */
2086         hide : true,
2087         /**
2088          * @event click
2089          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2090          * @param {Roo.menu.Menu} this
2091          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2092          * @param {Roo.EventObject} e
2093          */
2094         click : true,
2095         /**
2096          * @event mouseover
2097          * Fires when the mouse is hovering over this menu
2098          * @param {Roo.menu.Menu} this
2099          * @param {Roo.EventObject} e
2100          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2101          */
2102         mouseover : true,
2103         /**
2104          * @event mouseout
2105          * Fires when the mouse exits this menu
2106          * @param {Roo.menu.Menu} this
2107          * @param {Roo.EventObject} e
2108          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2109          */
2110         mouseout : true,
2111         /**
2112          * @event itemclick
2113          * Fires when a menu item contained in this menu is clicked
2114          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2115          * @param {Roo.EventObject} e
2116          */
2117         itemclick: true
2118     });
2119     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2120 };
2121
2122 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2123     
2124    /// html : false,
2125     //align : '',
2126     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2127     type: false,
2128     /**
2129      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2130      */
2131     registerMenu : true,
2132     
2133     menuItems :false, // stores the menu items..
2134     
2135     hidden:true,
2136         
2137     parentMenu : false,
2138     
2139     stopEvent : true,
2140     
2141     isLink : false,
2142     
2143     getChildContainer : function() {
2144         return this.el;  
2145     },
2146     
2147     getAutoCreate : function(){
2148          
2149         //if (['right'].indexOf(this.align)!==-1) {
2150         //    cfg.cn[1].cls += ' pull-right'
2151         //}
2152         
2153         
2154         var cfg = {
2155             tag : 'ul',
2156             cls : 'dropdown-menu' ,
2157             style : 'z-index:1000'
2158             
2159         };
2160         
2161         if (this.type === 'submenu') {
2162             cfg.cls = 'submenu active';
2163         }
2164         if (this.type === 'treeview') {
2165             cfg.cls = 'treeview-menu';
2166         }
2167         
2168         return cfg;
2169     },
2170     initEvents : function() {
2171         
2172        // Roo.log("ADD event");
2173        // Roo.log(this.triggerEl.dom);
2174         
2175         this.triggerEl.on('click', this.onTriggerClick, this);
2176         
2177         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2178         
2179         
2180         if (this.triggerEl.hasClass('nav-item')) {
2181             // dropdown toggle on the 'a' in BS4?
2182             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2183         } else {
2184             this.triggerEl.addClass('dropdown-toggle');
2185         }
2186         if (Roo.isTouch) {
2187             this.el.on('touchstart'  , this.onTouch, this);
2188         }
2189         this.el.on('click' , this.onClick, this);
2190
2191         this.el.on("mouseover", this.onMouseOver, this);
2192         this.el.on("mouseout", this.onMouseOut, this);
2193         
2194     },
2195     
2196     findTargetItem : function(e)
2197     {
2198         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2199         if(!t){
2200             return false;
2201         }
2202         //Roo.log(t);         Roo.log(t.id);
2203         if(t && t.id){
2204             //Roo.log(this.menuitems);
2205             return this.menuitems.get(t.id);
2206             
2207             //return this.items.get(t.menuItemId);
2208         }
2209         
2210         return false;
2211     },
2212     
2213     onTouch : function(e) 
2214     {
2215         Roo.log("menu.onTouch");
2216         //e.stopEvent(); this make the user popdown broken
2217         this.onClick(e);
2218     },
2219     
2220     onClick : function(e)
2221     {
2222         Roo.log("menu.onClick");
2223         
2224         var t = this.findTargetItem(e);
2225         if(!t || t.isContainer){
2226             return;
2227         }
2228         Roo.log(e);
2229         /*
2230         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2231             if(t == this.activeItem && t.shouldDeactivate(e)){
2232                 this.activeItem.deactivate();
2233                 delete this.activeItem;
2234                 return;
2235             }
2236             if(t.canActivate){
2237                 this.setActiveItem(t, true);
2238             }
2239             return;
2240             
2241             
2242         }
2243         */
2244        
2245         Roo.log('pass click event');
2246         
2247         t.onClick(e);
2248         
2249         this.fireEvent("click", this, t, e);
2250         
2251         var _this = this;
2252         
2253         if(!t.href.length || t.href == '#'){
2254             (function() { _this.hide(); }).defer(100);
2255         }
2256         
2257     },
2258     
2259     onMouseOver : function(e){
2260         var t  = this.findTargetItem(e);
2261         //Roo.log(t);
2262         //if(t){
2263         //    if(t.canActivate && !t.disabled){
2264         //        this.setActiveItem(t, true);
2265         //    }
2266         //}
2267         
2268         this.fireEvent("mouseover", this, e, t);
2269     },
2270     isVisible : function(){
2271         return !this.hidden;
2272     },
2273     onMouseOut : function(e){
2274         var t  = this.findTargetItem(e);
2275         
2276         //if(t ){
2277         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2278         //        this.activeItem.deactivate();
2279         //        delete this.activeItem;
2280         //    }
2281         //}
2282         this.fireEvent("mouseout", this, e, t);
2283     },
2284     
2285     
2286     /**
2287      * Displays this menu relative to another element
2288      * @param {String/HTMLElement/Roo.Element} element The element to align to
2289      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2290      * the element (defaults to this.defaultAlign)
2291      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2292      */
2293     show : function(el, pos, parentMenu)
2294     {
2295         if (false === this.fireEvent("beforeshow", this)) {
2296             Roo.log("show canceled");
2297             return;
2298         }
2299         this.parentMenu = parentMenu;
2300         if(!this.el){
2301             this.render();
2302         }
2303         
2304         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2305     },
2306      /**
2307      * Displays this menu at a specific xy position
2308      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2309      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2310      */
2311     showAt : function(xy, parentMenu, /* private: */_e){
2312         this.parentMenu = parentMenu;
2313         if(!this.el){
2314             this.render();
2315         }
2316         if(_e !== false){
2317             this.fireEvent("beforeshow", this);
2318             //xy = this.el.adjustForConstraints(xy);
2319         }
2320         
2321         //this.el.show();
2322         this.hideMenuItems();
2323         this.hidden = false;
2324         this.triggerEl.addClass('open');
2325         this.el.addClass('show');
2326         
2327         // reassign x when hitting right
2328         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2329             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2330         }
2331         
2332         // reassign y when hitting bottom
2333         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2334             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2335         }
2336         
2337         // but the list may align on trigger left or trigger top... should it be a properity?
2338         
2339         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2340             this.el.setXY(xy);
2341         }
2342         
2343         this.focus();
2344         this.fireEvent("show", this);
2345     },
2346     
2347     focus : function(){
2348         return;
2349         if(!this.hidden){
2350             this.doFocus.defer(50, this);
2351         }
2352     },
2353
2354     doFocus : function(){
2355         if(!this.hidden){
2356             this.focusEl.focus();
2357         }
2358     },
2359
2360     /**
2361      * Hides this menu and optionally all parent menus
2362      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2363      */
2364     hide : function(deep)
2365     {
2366         if (false === this.fireEvent("beforehide", this)) {
2367             Roo.log("hide canceled");
2368             return;
2369         }
2370         this.hideMenuItems();
2371         if(this.el && this.isVisible()){
2372            
2373             if(this.activeItem){
2374                 this.activeItem.deactivate();
2375                 this.activeItem = null;
2376             }
2377             this.triggerEl.removeClass('open');;
2378             this.el.removeClass('show');
2379             this.hidden = true;
2380             this.fireEvent("hide", this);
2381         }
2382         if(deep === true && this.parentMenu){
2383             this.parentMenu.hide(true);
2384         }
2385     },
2386     
2387     onTriggerClick : function(e)
2388     {
2389         Roo.log('trigger click');
2390         
2391         var target = e.getTarget();
2392         
2393         Roo.log(target.nodeName.toLowerCase());
2394         
2395         if(target.nodeName.toLowerCase() === 'i'){
2396             e.preventDefault();
2397         }
2398         
2399     },
2400     
2401     onTriggerPress  : function(e)
2402     {
2403         Roo.log('trigger press');
2404         //Roo.log(e.getTarget());
2405        // Roo.log(this.triggerEl.dom);
2406        
2407         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2408         var pel = Roo.get(e.getTarget());
2409         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2410             Roo.log('is treeview or dropdown?');
2411             return;
2412         }
2413         
2414         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2415             return;
2416         }
2417         
2418         if (this.isVisible()) {
2419             Roo.log('hide');
2420             this.hide();
2421         } else {
2422             Roo.log('show');
2423             this.show(this.triggerEl, '?', false);
2424         }
2425         
2426         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2427             e.stopEvent();
2428         }
2429         
2430     },
2431        
2432     
2433     hideMenuItems : function()
2434     {
2435         Roo.log("hide Menu Items");
2436         if (!this.el) { 
2437             return;
2438         }
2439         
2440         this.el.select('.open',true).each(function(aa) {
2441             
2442             aa.removeClass('open');
2443          
2444         });
2445     },
2446     addxtypeChild : function (tree, cntr) {
2447         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2448           
2449         this.menuitems.add(comp);
2450         return comp;
2451
2452     },
2453     getEl : function()
2454     {
2455         Roo.log(this.el);
2456         return this.el;
2457     },
2458     
2459     clear : function()
2460     {
2461         this.getEl().dom.innerHTML = '';
2462         this.menuitems.clear();
2463     }
2464 });
2465
2466  
2467  /*
2468  * - LGPL
2469  *
2470  * menu item
2471  * 
2472  */
2473
2474
2475 /**
2476  * @class Roo.bootstrap.MenuItem
2477  * @extends Roo.bootstrap.Component
2478  * Bootstrap MenuItem class
2479  * @cfg {String} html the menu label
2480  * @cfg {String} href the link
2481  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2482  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2483  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2484  * @cfg {String} fa favicon to show on left of menu item.
2485  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2486  * 
2487  * 
2488  * @constructor
2489  * Create a new MenuItem
2490  * @param {Object} config The config object
2491  */
2492
2493
2494 Roo.bootstrap.MenuItem = function(config){
2495     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2496     this.addEvents({
2497         // raw events
2498         /**
2499          * @event click
2500          * The raw click event for the entire grid.
2501          * @param {Roo.bootstrap.MenuItem} this
2502          * @param {Roo.EventObject} e
2503          */
2504         "click" : true
2505     });
2506 };
2507
2508 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2509     
2510     href : false,
2511     html : false,
2512     preventDefault: false,
2513     isContainer : false,
2514     active : false,
2515     fa: false,
2516     
2517     getAutoCreate : function(){
2518         
2519         if(this.isContainer){
2520             return {
2521                 tag: 'li',
2522                 cls: 'dropdown-menu-item '
2523             };
2524         }
2525         var ctag = {
2526             tag: 'span',
2527             html: 'Link'
2528         };
2529         
2530         var anc = {
2531             tag : 'a',
2532             cls : 'dropdown-item',
2533             href : '#',
2534             cn : [  ]
2535         };
2536         
2537         if (this.fa !== false) {
2538             anc.cn.push({
2539                 tag : 'i',
2540                 cls : 'fa fa-' + this.fa
2541             });
2542         }
2543         
2544         anc.cn.push(ctag);
2545         
2546         
2547         var cfg= {
2548             tag: 'li',
2549             cls: 'dropdown-menu-item',
2550             cn: [ anc ]
2551         };
2552         if (this.parent().type == 'treeview') {
2553             cfg.cls = 'treeview-menu';
2554         }
2555         if (this.active) {
2556             cfg.cls += ' active';
2557         }
2558         
2559         
2560         
2561         anc.href = this.href || cfg.cn[0].href ;
2562         ctag.html = this.html || cfg.cn[0].html ;
2563         return cfg;
2564     },
2565     
2566     initEvents: function()
2567     {
2568         if (this.parent().type == 'treeview') {
2569             this.el.select('a').on('click', this.onClick, this);
2570         }
2571         
2572         if (this.menu) {
2573             this.menu.parentType = this.xtype;
2574             this.menu.triggerEl = this.el;
2575             this.menu = this.addxtype(Roo.apply({}, this.menu));
2576         }
2577         
2578     },
2579     onClick : function(e)
2580     {
2581         Roo.log('item on click ');
2582         
2583         if(this.preventDefault){
2584             e.preventDefault();
2585         }
2586         //this.parent().hideMenuItems();
2587         
2588         this.fireEvent('click', this, e);
2589     },
2590     getEl : function()
2591     {
2592         return this.el;
2593     } 
2594 });
2595
2596  
2597
2598  /*
2599  * - LGPL
2600  *
2601  * menu separator
2602  * 
2603  */
2604
2605
2606 /**
2607  * @class Roo.bootstrap.MenuSeparator
2608  * @extends Roo.bootstrap.Component
2609  * Bootstrap MenuSeparator class
2610  * 
2611  * @constructor
2612  * Create a new MenuItem
2613  * @param {Object} config The config object
2614  */
2615
2616
2617 Roo.bootstrap.MenuSeparator = function(config){
2618     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2619 };
2620
2621 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2622     
2623     getAutoCreate : function(){
2624         var cfg = {
2625             cls: 'divider',
2626             tag : 'li'
2627         };
2628         
2629         return cfg;
2630     }
2631    
2632 });
2633
2634  
2635
2636  
2637 /*
2638 * Licence: LGPL
2639 */
2640
2641 /**
2642  * @class Roo.bootstrap.Modal
2643  * @extends Roo.bootstrap.Component
2644  * Bootstrap Modal class
2645  * @cfg {String} title Title of dialog
2646  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2647  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2648  * @cfg {Boolean} specificTitle default false
2649  * @cfg {Array} buttons Array of buttons or standard button set..
2650  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2651  * @cfg {Boolean} animate default true
2652  * @cfg {Boolean} allow_close default true
2653  * @cfg {Boolean} fitwindow default false
2654  * @cfg {String} size (sm|lg) default empty
2655  * @cfg {Number} max_width set the max width of modal
2656  *
2657  *
2658  * @constructor
2659  * Create a new Modal Dialog
2660  * @param {Object} config The config object
2661  */
2662
2663 Roo.bootstrap.Modal = function(config){
2664     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2665     this.addEvents({
2666         // raw events
2667         /**
2668          * @event btnclick
2669          * The raw btnclick event for the button
2670          * @param {Roo.EventObject} e
2671          */
2672         "btnclick" : true,
2673         /**
2674          * @event resize
2675          * Fire when dialog resize
2676          * @param {Roo.bootstrap.Modal} this
2677          * @param {Roo.EventObject} e
2678          */
2679         "resize" : true
2680     });
2681     this.buttons = this.buttons || [];
2682
2683     if (this.tmpl) {
2684         this.tmpl = Roo.factory(this.tmpl);
2685     }
2686
2687 };
2688
2689 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2690
2691     title : 'test dialog',
2692
2693     buttons : false,
2694
2695     // set on load...
2696
2697     html: false,
2698
2699     tmp: false,
2700
2701     specificTitle: false,
2702
2703     buttonPosition: 'right',
2704
2705     allow_close : true,
2706
2707     animate : true,
2708
2709     fitwindow: false,
2710     
2711      // private
2712     dialogEl: false,
2713     bodyEl:  false,
2714     footerEl:  false,
2715     titleEl:  false,
2716     closeEl:  false,
2717
2718     size: '',
2719     
2720     max_width: 0,
2721     
2722     max_height: 0,
2723     
2724     fit_content: false,
2725
2726     onRender : function(ct, position)
2727     {
2728         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2729
2730         if(!this.el){
2731             var cfg = Roo.apply({},  this.getAutoCreate());
2732             cfg.id = Roo.id();
2733             //if(!cfg.name){
2734             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2735             //}
2736             //if (!cfg.name.length) {
2737             //    delete cfg.name;
2738            // }
2739             if (this.cls) {
2740                 cfg.cls += ' ' + this.cls;
2741             }
2742             if (this.style) {
2743                 cfg.style = this.style;
2744             }
2745             this.el = Roo.get(document.body).createChild(cfg, position);
2746         }
2747         //var type = this.el.dom.type;
2748
2749
2750         if(this.tabIndex !== undefined){
2751             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2752         }
2753
2754         this.dialogEl = this.el.select('.modal-dialog',true).first();
2755         this.bodyEl = this.el.select('.modal-body',true).first();
2756         this.closeEl = this.el.select('.modal-header .close', true).first();
2757         this.headerEl = this.el.select('.modal-header',true).first();
2758         this.titleEl = this.el.select('.modal-title',true).first();
2759         this.footerEl = this.el.select('.modal-footer',true).first();
2760
2761         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2762         
2763         //this.el.addClass("x-dlg-modal");
2764
2765         if (this.buttons.length) {
2766             Roo.each(this.buttons, function(bb) {
2767                 var b = Roo.apply({}, bb);
2768                 b.xns = b.xns || Roo.bootstrap;
2769                 b.xtype = b.xtype || 'Button';
2770                 if (typeof(b.listeners) == 'undefined') {
2771                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2772                 }
2773
2774                 var btn = Roo.factory(b);
2775
2776                 btn.render(this.getButtonContainer());
2777
2778             },this);
2779         }
2780         // render the children.
2781         var nitems = [];
2782
2783         if(typeof(this.items) != 'undefined'){
2784             var items = this.items;
2785             delete this.items;
2786
2787             for(var i =0;i < items.length;i++) {
2788                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2789             }
2790         }
2791
2792         this.items = nitems;
2793
2794         // where are these used - they used to be body/close/footer
2795
2796
2797         this.initEvents();
2798         //this.el.addClass([this.fieldClass, this.cls]);
2799
2800     },
2801
2802     getAutoCreate : function()
2803     {
2804         var bdy = {
2805                 cls : 'modal-body',
2806                 html : this.html || ''
2807         };
2808
2809         var title = {
2810             tag: 'h4',
2811             cls : 'modal-title',
2812             html : this.title
2813         };
2814
2815         if(this.specificTitle){
2816             title = this.title;
2817
2818         }
2819
2820         var header = [];
2821         if (this.allow_close && Roo.bootstrap.version == 3) {
2822             header.push({
2823                 tag: 'button',
2824                 cls : 'close',
2825                 html : '&times'
2826             });
2827         }
2828
2829         header.push(title);
2830
2831         if (this.allow_close && Roo.bootstrap.version == 4) {
2832             header.push({
2833                 tag: 'button',
2834                 cls : 'close',
2835                 html : '&times'
2836             });
2837         }
2838         
2839         var size = '';
2840
2841         if(this.size.length){
2842             size = 'modal-' + this.size;
2843         }
2844         
2845         var footer = Roo.bootstrap.version == 3 ?
2846             {
2847                 cls : 'modal-footer',
2848                 cn : [
2849                     {
2850                         tag: 'div',
2851                         cls: 'btn-' + this.buttonPosition
2852                     }
2853                 ]
2854
2855             } :
2856             {  // BS4 uses mr-auto on left buttons....
2857                 cls : 'modal-footer'
2858             };
2859
2860             
2861
2862         
2863         
2864         var modal = {
2865             cls: "modal",
2866              cn : [
2867                 {
2868                     cls: "modal-dialog " + size,
2869                     cn : [
2870                         {
2871                             cls : "modal-content",
2872                             cn : [
2873                                 {
2874                                     cls : 'modal-header',
2875                                     cn : header
2876                                 },
2877                                 bdy,
2878                                 footer
2879                             ]
2880
2881                         }
2882                     ]
2883
2884                 }
2885             ]
2886         };
2887
2888         if(this.animate){
2889             modal.cls += ' fade';
2890         }
2891
2892         return modal;
2893
2894     },
2895     getChildContainer : function() {
2896
2897          return this.bodyEl;
2898
2899     },
2900     getButtonContainer : function() {
2901         
2902          return Roo.bootstrap.version == 4 ?
2903             this.el.select('.modal-footer',true).first()
2904             : this.el.select('.modal-footer div',true).first();
2905
2906     },
2907     initEvents : function()
2908     {
2909         if (this.allow_close) {
2910             this.closeEl.on('click', this.hide, this);
2911         }
2912         Roo.EventManager.onWindowResize(this.resize, this, true);
2913
2914
2915     },
2916   
2917
2918     resize : function()
2919     {
2920         this.maskEl.setSize(
2921             Roo.lib.Dom.getViewWidth(true),
2922             Roo.lib.Dom.getViewHeight(true)
2923         );
2924         
2925         if (this.fitwindow) {
2926             
2927            
2928             this.setSize(
2929                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2930                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2931             );
2932             return;
2933         }
2934         
2935         if(this.max_width !== 0) {
2936             
2937             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2938             
2939             if(this.height) {
2940                 this.setSize(w, this.height);
2941                 return;
2942             }
2943             
2944             if(this.max_height) {
2945                 this.setSize(w,Math.min(
2946                     this.max_height,
2947                     Roo.lib.Dom.getViewportHeight(true) - 60
2948                 ));
2949                 
2950                 return;
2951             }
2952             
2953             if(!this.fit_content) {
2954                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2955                 return;
2956             }
2957             
2958             this.setSize(w, Math.min(
2959                 60 +
2960                 this.headerEl.getHeight() + 
2961                 this.footerEl.getHeight() + 
2962                 this.getChildHeight(this.bodyEl.dom.childNodes),
2963                 Roo.lib.Dom.getViewportHeight(true) - 60)
2964             );
2965         }
2966         
2967     },
2968
2969     setSize : function(w,h)
2970     {
2971         if (!w && !h) {
2972             return;
2973         }
2974         
2975         this.resizeTo(w,h);
2976     },
2977
2978     show : function() {
2979
2980         if (!this.rendered) {
2981             this.render();
2982         }
2983
2984         //this.el.setStyle('display', 'block');
2985         this.el.removeClass('hideing');
2986         this.el.dom.style.display='block';
2987         
2988         Roo.get(document.body).addClass('modal-open');
2989  
2990         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2991             
2992             (function(){
2993                 this.el.addClass('show');
2994                 this.el.addClass('in');
2995             }).defer(50, this);
2996         }else{
2997             this.el.addClass('show');
2998             this.el.addClass('in');
2999         }
3000
3001         // not sure how we can show data in here..
3002         //if (this.tmpl) {
3003         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3004         //}
3005
3006         Roo.get(document.body).addClass("x-body-masked");
3007         
3008         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3009         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3010         this.maskEl.dom.style.display = 'block';
3011         this.maskEl.addClass('show');
3012         
3013         
3014         this.resize();
3015         
3016         this.fireEvent('show', this);
3017
3018         // set zindex here - otherwise it appears to be ignored...
3019         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3020
3021         (function () {
3022             this.items.forEach( function(e) {
3023                 e.layout ? e.layout() : false;
3024
3025             });
3026         }).defer(100,this);
3027
3028     },
3029     hide : function()
3030     {
3031         if(this.fireEvent("beforehide", this) !== false){
3032             
3033             this.maskEl.removeClass('show');
3034             
3035             this.maskEl.dom.style.display = '';
3036             Roo.get(document.body).removeClass("x-body-masked");
3037             this.el.removeClass('in');
3038             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3039
3040             if(this.animate){ // why
3041                 this.el.addClass('hideing');
3042                 this.el.removeClass('show');
3043                 (function(){
3044                     if (!this.el.hasClass('hideing')) {
3045                         return; // it's been shown again...
3046                     }
3047                     
3048                     this.el.dom.style.display='';
3049
3050                     Roo.get(document.body).removeClass('modal-open');
3051                     this.el.removeClass('hideing');
3052                 }).defer(150,this);
3053                 
3054             }else{
3055                 this.el.removeClass('show');
3056                 this.el.dom.style.display='';
3057                 Roo.get(document.body).removeClass('modal-open');
3058
3059             }
3060             this.fireEvent('hide', this);
3061         }
3062     },
3063     isVisible : function()
3064     {
3065         
3066         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3067         
3068     },
3069
3070     addButton : function(str, cb)
3071     {
3072
3073
3074         var b = Roo.apply({}, { html : str } );
3075         b.xns = b.xns || Roo.bootstrap;
3076         b.xtype = b.xtype || 'Button';
3077         if (typeof(b.listeners) == 'undefined') {
3078             b.listeners = { click : cb.createDelegate(this)  };
3079         }
3080
3081         var btn = Roo.factory(b);
3082
3083         btn.render(this.getButtonContainer());
3084
3085         return btn;
3086
3087     },
3088
3089     setDefaultButton : function(btn)
3090     {
3091         //this.el.select('.modal-footer').()
3092     },
3093
3094     resizeTo: function(w,h)
3095     {
3096         this.dialogEl.setWidth(w);
3097         
3098         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3099
3100         this.bodyEl.setHeight(h - diff);
3101         
3102         this.fireEvent('resize', this);
3103     },
3104     
3105     setContentSize  : function(w, h)
3106     {
3107
3108     },
3109     onButtonClick: function(btn,e)
3110     {
3111         //Roo.log([a,b,c]);
3112         this.fireEvent('btnclick', btn.name, e);
3113     },
3114      /**
3115      * Set the title of the Dialog
3116      * @param {String} str new Title
3117      */
3118     setTitle: function(str) {
3119         this.titleEl.dom.innerHTML = str;
3120     },
3121     /**
3122      * Set the body of the Dialog
3123      * @param {String} str new Title
3124      */
3125     setBody: function(str) {
3126         this.bodyEl.dom.innerHTML = str;
3127     },
3128     /**
3129      * Set the body of the Dialog using the template
3130      * @param {Obj} data - apply this data to the template and replace the body contents.
3131      */
3132     applyBody: function(obj)
3133     {
3134         if (!this.tmpl) {
3135             Roo.log("Error - using apply Body without a template");
3136             //code
3137         }
3138         this.tmpl.overwrite(this.bodyEl, obj);
3139     },
3140     
3141     getChildHeight : function(child_nodes)
3142     {
3143         if(
3144             !child_nodes ||
3145             child_nodes.length == 0
3146         ) {
3147             return;
3148         }
3149         
3150         var child_height = 0;
3151         
3152         for(var i = 0; i < child_nodes.length; i++) {
3153             
3154             /*
3155             * for modal with tabs...
3156             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3157                 
3158                 var layout_childs = child_nodes[i].childNodes;
3159                 
3160                 for(var j = 0; j < layout_childs.length; j++) {
3161                     
3162                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3163                         
3164                         var layout_body_childs = layout_childs[j].childNodes;
3165                         
3166                         for(var k = 0; k < layout_body_childs.length; k++) {
3167                             
3168                             if(layout_body_childs[k].classList.contains('navbar')) {
3169                                 child_height += layout_body_childs[k].offsetHeight;
3170                                 continue;
3171                             }
3172                             
3173                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3174                                 
3175                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3176                                 
3177                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3178                                     
3179                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3180                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3181                                         continue;
3182                                     }
3183                                     
3184                                 }
3185                                 
3186                             }
3187                             
3188                         }
3189                     }
3190                 }
3191                 continue;
3192             }
3193             */
3194             
3195             child_height += child_nodes[i].offsetHeight;
3196             // Roo.log(child_nodes[i].offsetHeight);
3197         }
3198         
3199         return child_height;
3200     }
3201
3202 });
3203
3204
3205 Roo.apply(Roo.bootstrap.Modal,  {
3206     /**
3207          * Button config that displays a single OK button
3208          * @type Object
3209          */
3210         OK :  [{
3211             name : 'ok',
3212             weight : 'primary',
3213             html : 'OK'
3214         }],
3215         /**
3216          * Button config that displays Yes and No buttons
3217          * @type Object
3218          */
3219         YESNO : [
3220             {
3221                 name  : 'no',
3222                 html : 'No'
3223             },
3224             {
3225                 name  :'yes',
3226                 weight : 'primary',
3227                 html : 'Yes'
3228             }
3229         ],
3230
3231         /**
3232          * Button config that displays OK and Cancel buttons
3233          * @type Object
3234          */
3235         OKCANCEL : [
3236             {
3237                name : 'cancel',
3238                 html : 'Cancel'
3239             },
3240             {
3241                 name : 'ok',
3242                 weight : 'primary',
3243                 html : 'OK'
3244             }
3245         ],
3246         /**
3247          * Button config that displays Yes, No and Cancel buttons
3248          * @type Object
3249          */
3250         YESNOCANCEL : [
3251             {
3252                 name : 'yes',
3253                 weight : 'primary',
3254                 html : 'Yes'
3255             },
3256             {
3257                 name : 'no',
3258                 html : 'No'
3259             },
3260             {
3261                 name : 'cancel',
3262                 html : 'Cancel'
3263             }
3264         ],
3265         
3266         zIndex : 10001
3267 });
3268 /*
3269  * - LGPL
3270  *
3271  * messagebox - can be used as a replace
3272  * 
3273  */
3274 /**
3275  * @class Roo.MessageBox
3276  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3277  * Example usage:
3278  *<pre><code>
3279 // Basic alert:
3280 Roo.Msg.alert('Status', 'Changes saved successfully.');
3281
3282 // Prompt for user data:
3283 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3284     if (btn == 'ok'){
3285         // process text value...
3286     }
3287 });
3288
3289 // Show a dialog using config options:
3290 Roo.Msg.show({
3291    title:'Save Changes?',
3292    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3293    buttons: Roo.Msg.YESNOCANCEL,
3294    fn: processResult,
3295    animEl: 'elId'
3296 });
3297 </code></pre>
3298  * @singleton
3299  */
3300 Roo.bootstrap.MessageBox = function(){
3301     var dlg, opt, mask, waitTimer;
3302     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3303     var buttons, activeTextEl, bwidth;
3304
3305     
3306     // private
3307     var handleButton = function(button){
3308         dlg.hide();
3309         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3310     };
3311
3312     // private
3313     var handleHide = function(){
3314         if(opt && opt.cls){
3315             dlg.el.removeClass(opt.cls);
3316         }
3317         //if(waitTimer){
3318         //    Roo.TaskMgr.stop(waitTimer);
3319         //    waitTimer = null;
3320         //}
3321     };
3322
3323     // private
3324     var updateButtons = function(b){
3325         var width = 0;
3326         if(!b){
3327             buttons["ok"].hide();
3328             buttons["cancel"].hide();
3329             buttons["yes"].hide();
3330             buttons["no"].hide();
3331             dlg.footerEl.hide();
3332             
3333             return width;
3334         }
3335         dlg.footerEl.show();
3336         for(var k in buttons){
3337             if(typeof buttons[k] != "function"){
3338                 if(b[k]){
3339                     buttons[k].show();
3340                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3341                     width += buttons[k].el.getWidth()+15;
3342                 }else{
3343                     buttons[k].hide();
3344                 }
3345             }
3346         }
3347         return width;
3348     };
3349
3350     // private
3351     var handleEsc = function(d, k, e){
3352         if(opt && opt.closable !== false){
3353             dlg.hide();
3354         }
3355         if(e){
3356             e.stopEvent();
3357         }
3358     };
3359
3360     return {
3361         /**
3362          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3363          * @return {Roo.BasicDialog} The BasicDialog element
3364          */
3365         getDialog : function(){
3366            if(!dlg){
3367                 dlg = new Roo.bootstrap.Modal( {
3368                     //draggable: true,
3369                     //resizable:false,
3370                     //constraintoviewport:false,
3371                     //fixedcenter:true,
3372                     //collapsible : false,
3373                     //shim:true,
3374                     //modal: true,
3375                 //    width: 'auto',
3376                   //  height:100,
3377                     //buttonAlign:"center",
3378                     closeClick : function(){
3379                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3380                             handleButton("no");
3381                         }else{
3382                             handleButton("cancel");
3383                         }
3384                     }
3385                 });
3386                 dlg.render();
3387                 dlg.on("hide", handleHide);
3388                 mask = dlg.mask;
3389                 //dlg.addKeyListener(27, handleEsc);
3390                 buttons = {};
3391                 this.buttons = buttons;
3392                 var bt = this.buttonText;
3393                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3394                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3395                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3396                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3397                 //Roo.log(buttons);
3398                 bodyEl = dlg.bodyEl.createChild({
3399
3400                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3401                         '<textarea class="roo-mb-textarea"></textarea>' +
3402                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3403                 });
3404                 msgEl = bodyEl.dom.firstChild;
3405                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3406                 textboxEl.enableDisplayMode();
3407                 textboxEl.addKeyListener([10,13], function(){
3408                     if(dlg.isVisible() && opt && opt.buttons){
3409                         if(opt.buttons.ok){
3410                             handleButton("ok");
3411                         }else if(opt.buttons.yes){
3412                             handleButton("yes");
3413                         }
3414                     }
3415                 });
3416                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3417                 textareaEl.enableDisplayMode();
3418                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3419                 progressEl.enableDisplayMode();
3420                 
3421                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3422                 var pf = progressEl.dom.firstChild;
3423                 if (pf) {
3424                     pp = Roo.get(pf.firstChild);
3425                     pp.setHeight(pf.offsetHeight);
3426                 }
3427                 
3428             }
3429             return dlg;
3430         },
3431
3432         /**
3433          * Updates the message box body text
3434          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3435          * the XHTML-compliant non-breaking space character '&amp;#160;')
3436          * @return {Roo.MessageBox} This message box
3437          */
3438         updateText : function(text)
3439         {
3440             if(!dlg.isVisible() && !opt.width){
3441                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3442                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3443             }
3444             msgEl.innerHTML = text || '&#160;';
3445       
3446             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3447             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3448             var w = Math.max(
3449                     Math.min(opt.width || cw , this.maxWidth), 
3450                     Math.max(opt.minWidth || this.minWidth, bwidth)
3451             );
3452             if(opt.prompt){
3453                 activeTextEl.setWidth(w);
3454             }
3455             if(dlg.isVisible()){
3456                 dlg.fixedcenter = false;
3457             }
3458             // to big, make it scroll. = But as usual stupid IE does not support
3459             // !important..
3460             
3461             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3462                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3463                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3464             } else {
3465                 bodyEl.dom.style.height = '';
3466                 bodyEl.dom.style.overflowY = '';
3467             }
3468             if (cw > w) {
3469                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3470             } else {
3471                 bodyEl.dom.style.overflowX = '';
3472             }
3473             
3474             dlg.setContentSize(w, bodyEl.getHeight());
3475             if(dlg.isVisible()){
3476                 dlg.fixedcenter = true;
3477             }
3478             return this;
3479         },
3480
3481         /**
3482          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3483          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3484          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3485          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3486          * @return {Roo.MessageBox} This message box
3487          */
3488         updateProgress : function(value, text){
3489             if(text){
3490                 this.updateText(text);
3491             }
3492             
3493             if (pp) { // weird bug on my firefox - for some reason this is not defined
3494                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3495                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3496             }
3497             return this;
3498         },        
3499
3500         /**
3501          * Returns true if the message box is currently displayed
3502          * @return {Boolean} True if the message box is visible, else false
3503          */
3504         isVisible : function(){
3505             return dlg && dlg.isVisible();  
3506         },
3507
3508         /**
3509          * Hides the message box if it is displayed
3510          */
3511         hide : function(){
3512             if(this.isVisible()){
3513                 dlg.hide();
3514             }  
3515         },
3516
3517         /**
3518          * Displays a new message box, or reinitializes an existing message box, based on the config options
3519          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3520          * The following config object properties are supported:
3521          * <pre>
3522 Property    Type             Description
3523 ----------  ---------------  ------------------------------------------------------------------------------------
3524 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3525                                    closes (defaults to undefined)
3526 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3527                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3528 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3529                                    progress and wait dialogs will ignore this property and always hide the
3530                                    close button as they can only be closed programmatically.
3531 cls               String           A custom CSS class to apply to the message box element
3532 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3533                                    displayed (defaults to 75)
3534 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3535                                    function will be btn (the name of the button that was clicked, if applicable,
3536                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3537                                    Progress and wait dialogs will ignore this option since they do not respond to
3538                                    user actions and can only be closed programmatically, so any required function
3539                                    should be called by the same code after it closes the dialog.
3540 icon              String           A CSS class that provides a background image to be used as an icon for
3541                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3542 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3543 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3544 modal             Boolean          False to allow user interaction with the page while the message box is
3545                                    displayed (defaults to true)
3546 msg               String           A string that will replace the existing message box body text (defaults
3547                                    to the XHTML-compliant non-breaking space character '&#160;')
3548 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3549 progress          Boolean          True to display a progress bar (defaults to false)
3550 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3551 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3552 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3553 title             String           The title text
3554 value             String           The string value to set into the active textbox element if displayed
3555 wait              Boolean          True to display a progress bar (defaults to false)
3556 width             Number           The width of the dialog in pixels
3557 </pre>
3558          *
3559          * Example usage:
3560          * <pre><code>
3561 Roo.Msg.show({
3562    title: 'Address',
3563    msg: 'Please enter your address:',
3564    width: 300,
3565    buttons: Roo.MessageBox.OKCANCEL,
3566    multiline: true,
3567    fn: saveAddress,
3568    animEl: 'addAddressBtn'
3569 });
3570 </code></pre>
3571          * @param {Object} config Configuration options
3572          * @return {Roo.MessageBox} This message box
3573          */
3574         show : function(options)
3575         {
3576             
3577             // this causes nightmares if you show one dialog after another
3578             // especially on callbacks..
3579              
3580             if(this.isVisible()){
3581                 
3582                 this.hide();
3583                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3584                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3585                 Roo.log("New Dialog Message:" +  options.msg )
3586                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3587                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3588                 
3589             }
3590             var d = this.getDialog();
3591             opt = options;
3592             d.setTitle(opt.title || "&#160;");
3593             d.closeEl.setDisplayed(opt.closable !== false);
3594             activeTextEl = textboxEl;
3595             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3596             if(opt.prompt){
3597                 if(opt.multiline){
3598                     textboxEl.hide();
3599                     textareaEl.show();
3600                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3601                         opt.multiline : this.defaultTextHeight);
3602                     activeTextEl = textareaEl;
3603                 }else{
3604                     textboxEl.show();
3605                     textareaEl.hide();
3606                 }
3607             }else{
3608                 textboxEl.hide();
3609                 textareaEl.hide();
3610             }
3611             progressEl.setDisplayed(opt.progress === true);
3612             if (opt.progress) {
3613                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3614             }
3615             this.updateProgress(0);
3616             activeTextEl.dom.value = opt.value || "";
3617             if(opt.prompt){
3618                 dlg.setDefaultButton(activeTextEl);
3619             }else{
3620                 var bs = opt.buttons;
3621                 var db = null;
3622                 if(bs && bs.ok){
3623                     db = buttons["ok"];
3624                 }else if(bs && bs.yes){
3625                     db = buttons["yes"];
3626                 }
3627                 dlg.setDefaultButton(db);
3628             }
3629             bwidth = updateButtons(opt.buttons);
3630             this.updateText(opt.msg);
3631             if(opt.cls){
3632                 d.el.addClass(opt.cls);
3633             }
3634             d.proxyDrag = opt.proxyDrag === true;
3635             d.modal = opt.modal !== false;
3636             d.mask = opt.modal !== false ? mask : false;
3637             if(!d.isVisible()){
3638                 // force it to the end of the z-index stack so it gets a cursor in FF
3639                 document.body.appendChild(dlg.el.dom);
3640                 d.animateTarget = null;
3641                 d.show(options.animEl);
3642             }
3643             return this;
3644         },
3645
3646         /**
3647          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3648          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3649          * and closing the message box when the process is complete.
3650          * @param {String} title The title bar text
3651          * @param {String} msg The message box body text
3652          * @return {Roo.MessageBox} This message box
3653          */
3654         progress : function(title, msg){
3655             this.show({
3656                 title : title,
3657                 msg : msg,
3658                 buttons: false,
3659                 progress:true,
3660                 closable:false,
3661                 minWidth: this.minProgressWidth,
3662                 modal : true
3663             });
3664             return this;
3665         },
3666
3667         /**
3668          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3669          * If a callback function is passed it will be called after the user clicks the button, and the
3670          * id of the button that was clicked will be passed as the only parameter to the callback
3671          * (could also be the top-right close button).
3672          * @param {String} title The title bar text
3673          * @param {String} msg The message box body text
3674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3675          * @param {Object} scope (optional) The scope of the callback function
3676          * @return {Roo.MessageBox} This message box
3677          */
3678         alert : function(title, msg, fn, scope)
3679         {
3680             this.show({
3681                 title : title,
3682                 msg : msg,
3683                 buttons: this.OK,
3684                 fn: fn,
3685                 closable : false,
3686                 scope : scope,
3687                 modal : true
3688             });
3689             return this;
3690         },
3691
3692         /**
3693          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3694          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3695          * You are responsible for closing the message box when the process is complete.
3696          * @param {String} msg The message box body text
3697          * @param {String} title (optional) The title bar text
3698          * @return {Roo.MessageBox} This message box
3699          */
3700         wait : function(msg, title){
3701             this.show({
3702                 title : title,
3703                 msg : msg,
3704                 buttons: false,
3705                 closable:false,
3706                 progress:true,
3707                 modal:true,
3708                 width:300,
3709                 wait:true
3710             });
3711             waitTimer = Roo.TaskMgr.start({
3712                 run: function(i){
3713                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3714                 },
3715                 interval: 1000
3716             });
3717             return this;
3718         },
3719
3720         /**
3721          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3722          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3723          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3724          * @param {String} title The title bar text
3725          * @param {String} msg The message box body text
3726          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3727          * @param {Object} scope (optional) The scope of the callback function
3728          * @return {Roo.MessageBox} This message box
3729          */
3730         confirm : function(title, msg, fn, scope){
3731             this.show({
3732                 title : title,
3733                 msg : msg,
3734                 buttons: this.YESNO,
3735                 fn: fn,
3736                 scope : scope,
3737                 modal : true
3738             });
3739             return this;
3740         },
3741
3742         /**
3743          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3744          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3745          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3746          * (could also be the top-right close button) and the text that was entered will be passed as the two
3747          * parameters to the callback.
3748          * @param {String} title The title bar text
3749          * @param {String} msg The message box body text
3750          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3751          * @param {Object} scope (optional) The scope of the callback function
3752          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3753          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3754          * @return {Roo.MessageBox} This message box
3755          */
3756         prompt : function(title, msg, fn, scope, multiline){
3757             this.show({
3758                 title : title,
3759                 msg : msg,
3760                 buttons: this.OKCANCEL,
3761                 fn: fn,
3762                 minWidth:250,
3763                 scope : scope,
3764                 prompt:true,
3765                 multiline: multiline,
3766                 modal : true
3767             });
3768             return this;
3769         },
3770
3771         /**
3772          * Button config that displays a single OK button
3773          * @type Object
3774          */
3775         OK : {ok:true},
3776         /**
3777          * Button config that displays Yes and No buttons
3778          * @type Object
3779          */
3780         YESNO : {yes:true, no:true},
3781         /**
3782          * Button config that displays OK and Cancel buttons
3783          * @type Object
3784          */
3785         OKCANCEL : {ok:true, cancel:true},
3786         /**
3787          * Button config that displays Yes, No and Cancel buttons
3788          * @type Object
3789          */
3790         YESNOCANCEL : {yes:true, no:true, cancel:true},
3791
3792         /**
3793          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3794          * @type Number
3795          */
3796         defaultTextHeight : 75,
3797         /**
3798          * The maximum width in pixels of the message box (defaults to 600)
3799          * @type Number
3800          */
3801         maxWidth : 600,
3802         /**
3803          * The minimum width in pixels of the message box (defaults to 100)
3804          * @type Number
3805          */
3806         minWidth : 100,
3807         /**
3808          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3809          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3810          * @type Number
3811          */
3812         minProgressWidth : 250,
3813         /**
3814          * An object containing the default button text strings that can be overriden for localized language support.
3815          * Supported properties are: ok, cancel, yes and no.
3816          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3817          * @type Object
3818          */
3819         buttonText : {
3820             ok : "OK",
3821             cancel : "Cancel",
3822             yes : "Yes",
3823             no : "No"
3824         }
3825     };
3826 }();
3827
3828 /**
3829  * Shorthand for {@link Roo.MessageBox}
3830  */
3831 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3832 Roo.Msg = Roo.Msg || Roo.MessageBox;
3833 /*
3834  * - LGPL
3835  *
3836  * navbar
3837  * 
3838  */
3839
3840 /**
3841  * @class Roo.bootstrap.Navbar
3842  * @extends Roo.bootstrap.Component
3843  * Bootstrap Navbar class
3844
3845  * @constructor
3846  * Create a new Navbar
3847  * @param {Object} config The config object
3848  */
3849
3850
3851 Roo.bootstrap.Navbar = function(config){
3852     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3853     this.addEvents({
3854         // raw events
3855         /**
3856          * @event beforetoggle
3857          * Fire before toggle the menu
3858          * @param {Roo.EventObject} e
3859          */
3860         "beforetoggle" : true
3861     });
3862 };
3863
3864 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3865     
3866     
3867    
3868     // private
3869     navItems : false,
3870     loadMask : false,
3871     
3872     
3873     getAutoCreate : function(){
3874         
3875         
3876         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3877         
3878     },
3879     
3880     initEvents :function ()
3881     {
3882         //Roo.log(this.el.select('.navbar-toggle',true));
3883         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3884         
3885         var mark = {
3886             tag: "div",
3887             cls:"x-dlg-mask"
3888         };
3889         
3890         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3891         
3892         var size = this.el.getSize();
3893         this.maskEl.setSize(size.width, size.height);
3894         this.maskEl.enableDisplayMode("block");
3895         this.maskEl.hide();
3896         
3897         if(this.loadMask){
3898             this.maskEl.show();
3899         }
3900     },
3901     
3902     
3903     getChildContainer : function()
3904     {
3905         if (this.el && this.el.select('.collapse').getCount()) {
3906             return this.el.select('.collapse',true).first();
3907         }
3908         
3909         return this.el;
3910     },
3911     
3912     mask : function()
3913     {
3914         this.maskEl.show();
3915     },
3916     
3917     unmask : function()
3918     {
3919         this.maskEl.hide();
3920     },
3921     onToggle : function()
3922     {
3923         
3924         if(this.fireEvent('beforetoggle', this) === false){
3925             return;
3926         }
3927         var ce = this.el.select('.navbar-collapse',true).first();
3928       
3929         if (!ce.hasClass('show')) {
3930            this.expand();
3931         } else {
3932             this.collapse();
3933         }
3934         
3935         
3936     
3937     },
3938     /**
3939      * Expand the navbar pulldown 
3940      */
3941     expand : function ()
3942     {
3943        
3944         var ce = this.el.select('.navbar-collapse',true).first();
3945         if (ce.hasClass('collapsing')) {
3946             return;
3947         }
3948         ce.dom.style.height = '';
3949                // show it...
3950         ce.addClass('in'); // old...
3951         ce.removeClass('collapse');
3952         ce.addClass('show');
3953         var h = ce.getHeight();
3954         Roo.log(h);
3955         ce.removeClass('show');
3956         // at this point we should be able to see it..
3957         ce.addClass('collapsing');
3958         
3959         ce.setHeight(0); // resize it ...
3960         ce.on('transitionend', function() {
3961             //Roo.log('done transition');
3962             ce.removeClass('collapsing');
3963             ce.addClass('show');
3964             ce.removeClass('collapse');
3965
3966             ce.dom.style.height = '';
3967         }, this, { single: true} );
3968         ce.setHeight(h);
3969         ce.dom.scrollTop = 0;
3970     },
3971     /**
3972      * Collapse the navbar pulldown 
3973      */
3974     collapse : function()
3975     {
3976          var ce = this.el.select('.navbar-collapse',true).first();
3977        
3978         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3979             // it's collapsed or collapsing..
3980             return;
3981         }
3982         ce.removeClass('in'); // old...
3983         ce.setHeight(ce.getHeight());
3984         ce.removeClass('show');
3985         ce.addClass('collapsing');
3986         
3987         ce.on('transitionend', function() {
3988             ce.dom.style.height = '';
3989             ce.removeClass('collapsing');
3990             ce.addClass('collapse');
3991         }, this, { single: true} );
3992         ce.setHeight(0);
3993     }
3994     
3995     
3996     
3997 });
3998
3999
4000
4001  
4002
4003  /*
4004  * - LGPL
4005  *
4006  * navbar
4007  * 
4008  */
4009
4010 /**
4011  * @class Roo.bootstrap.NavSimplebar
4012  * @extends Roo.bootstrap.Navbar
4013  * Bootstrap Sidebar class
4014  *
4015  * @cfg {Boolean} inverse is inverted color
4016  * 
4017  * @cfg {String} type (nav | pills | tabs)
4018  * @cfg {Boolean} arrangement stacked | justified
4019  * @cfg {String} align (left | right) alignment
4020  * 
4021  * @cfg {Boolean} main (true|false) main nav bar? default false
4022  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4023  * 
4024  * @cfg {String} tag (header|footer|nav|div) default is nav 
4025
4026  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4027  * 
4028  * 
4029  * @constructor
4030  * Create a new Sidebar
4031  * @param {Object} config The config object
4032  */
4033
4034
4035 Roo.bootstrap.NavSimplebar = function(config){
4036     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4037 };
4038
4039 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4040     
4041     inverse: false,
4042     
4043     type: false,
4044     arrangement: '',
4045     align : false,
4046     
4047     weight : 'light',
4048     
4049     main : false,
4050     
4051     
4052     tag : false,
4053     
4054     
4055     getAutoCreate : function(){
4056         
4057         
4058         var cfg = {
4059             tag : this.tag || 'div',
4060             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4061         };
4062         if (['light','white'].indexOf(this.weight) > -1) {
4063             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4064         }
4065         cfg.cls += ' bg-' + this.weight;
4066         
4067         if (this.inverse) {
4068             cfg.cls += ' navbar-inverse';
4069             
4070         }
4071         
4072         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4073         
4074         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4075             return cfg;
4076         }
4077         
4078         
4079     
4080         
4081         cfg.cn = [
4082             {
4083                 cls: 'nav nav-' + this.xtype,
4084                 tag : 'ul'
4085             }
4086         ];
4087         
4088          
4089         this.type = this.type || 'nav';
4090         if (['tabs','pills'].indexOf(this.type) != -1) {
4091             cfg.cn[0].cls += ' nav-' + this.type
4092         
4093         
4094         } else {
4095             if (this.type!=='nav') {
4096                 Roo.log('nav type must be nav/tabs/pills')
4097             }
4098             cfg.cn[0].cls += ' navbar-nav'
4099         }
4100         
4101         
4102         
4103         
4104         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4105             cfg.cn[0].cls += ' nav-' + this.arrangement;
4106         }
4107         
4108         
4109         if (this.align === 'right') {
4110             cfg.cn[0].cls += ' navbar-right';
4111         }
4112         
4113         
4114         
4115         
4116         return cfg;
4117     
4118         
4119     }
4120     
4121     
4122     
4123 });
4124
4125
4126
4127  
4128
4129  
4130        /*
4131  * - LGPL
4132  *
4133  * navbar
4134  * navbar-fixed-top
4135  * navbar-expand-md  fixed-top 
4136  */
4137
4138 /**
4139  * @class Roo.bootstrap.NavHeaderbar
4140  * @extends Roo.bootstrap.NavSimplebar
4141  * Bootstrap Sidebar class
4142  *
4143  * @cfg {String} brand what is brand
4144  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4145  * @cfg {String} brand_href href of the brand
4146  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4147  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4148  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4149  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4150  * 
4151  * @constructor
4152  * Create a new Sidebar
4153  * @param {Object} config The config object
4154  */
4155
4156
4157 Roo.bootstrap.NavHeaderbar = function(config){
4158     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4159       
4160 };
4161
4162 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4163     
4164     position: '',
4165     brand: '',
4166     brand_href: false,
4167     srButton : true,
4168     autohide : false,
4169     desktopCenter : false,
4170    
4171     
4172     getAutoCreate : function(){
4173         
4174         var   cfg = {
4175             tag: this.nav || 'nav',
4176             cls: 'navbar navbar-expand-md',
4177             role: 'navigation',
4178             cn: []
4179         };
4180         
4181         var cn = cfg.cn;
4182         if (this.desktopCenter) {
4183             cn.push({cls : 'container', cn : []});
4184             cn = cn[0].cn;
4185         }
4186         
4187         if(this.srButton){
4188             var btn = {
4189                 tag: 'button',
4190                 type: 'button',
4191                 cls: 'navbar-toggle navbar-toggler',
4192                 'data-toggle': 'collapse',
4193                 cn: [
4194                     {
4195                         tag: 'span',
4196                         cls: 'sr-only',
4197                         html: 'Toggle navigation'
4198                     },
4199                     {
4200                         tag: 'span',
4201                         cls: 'icon-bar navbar-toggler-icon'
4202                     },
4203                     {
4204                         tag: 'span',
4205                         cls: 'icon-bar'
4206                     },
4207                     {
4208                         tag: 'span',
4209                         cls: 'icon-bar'
4210                     }
4211                 ]
4212             };
4213             
4214             cn.push( Roo.bootstrap.version == 4 ? btn : {
4215                 tag: 'div',
4216                 cls: 'navbar-header',
4217                 cn: [
4218                     btn
4219                 ]
4220             });
4221         }
4222         
4223         cn.push({
4224             tag: 'div',
4225             cls: 'collapse navbar-collapse',
4226             cn : []
4227         });
4228         
4229         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4230         
4231         if (['light','white'].indexOf(this.weight) > -1) {
4232             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4233         }
4234         cfg.cls += ' bg-' + this.weight;
4235         
4236         
4237         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4238             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4239             
4240             // tag can override this..
4241             
4242             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4243         }
4244         
4245         if (this.brand !== '') {
4246             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4247             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4248                 tag: 'a',
4249                 href: this.brand_href ? this.brand_href : '#',
4250                 cls: 'navbar-brand',
4251                 cn: [
4252                 this.brand
4253                 ]
4254             });
4255         }
4256         
4257         if(this.main){
4258             cfg.cls += ' main-nav';
4259         }
4260         
4261         
4262         return cfg;
4263
4264         
4265     },
4266     getHeaderChildContainer : function()
4267     {
4268         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4269             return this.el.select('.navbar-header',true).first();
4270         }
4271         
4272         return this.getChildContainer();
4273     },
4274     
4275     
4276     initEvents : function()
4277     {
4278         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4279         
4280         if (this.autohide) {
4281             
4282             var prevScroll = 0;
4283             var ft = this.el;
4284             
4285             Roo.get(document).on('scroll',function(e) {
4286                 var ns = Roo.get(document).getScroll().top;
4287                 var os = prevScroll;
4288                 prevScroll = ns;
4289                 
4290                 if(ns > os){
4291                     ft.removeClass('slideDown');
4292                     ft.addClass('slideUp');
4293                     return;
4294                 }
4295                 ft.removeClass('slideUp');
4296                 ft.addClass('slideDown');
4297                  
4298               
4299           },this);
4300         }
4301     }    
4302     
4303 });
4304
4305
4306
4307  
4308
4309  /*
4310  * - LGPL
4311  *
4312  * navbar
4313  * 
4314  */
4315
4316 /**
4317  * @class Roo.bootstrap.NavSidebar
4318  * @extends Roo.bootstrap.Navbar
4319  * Bootstrap Sidebar class
4320  * 
4321  * @constructor
4322  * Create a new Sidebar
4323  * @param {Object} config The config object
4324  */
4325
4326
4327 Roo.bootstrap.NavSidebar = function(config){
4328     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4329 };
4330
4331 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4332     
4333     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4334     
4335     getAutoCreate : function(){
4336         
4337         
4338         return  {
4339             tag: 'div',
4340             cls: 'sidebar sidebar-nav'
4341         };
4342     
4343         
4344     }
4345     
4346     
4347     
4348 });
4349
4350
4351
4352  
4353
4354  /*
4355  * - LGPL
4356  *
4357  * nav group
4358  * 
4359  */
4360
4361 /**
4362  * @class Roo.bootstrap.NavGroup
4363  * @extends Roo.bootstrap.Component
4364  * Bootstrap NavGroup class
4365  * @cfg {String} align (left|right)
4366  * @cfg {Boolean} inverse
4367  * @cfg {String} type (nav|pills|tab) default nav
4368  * @cfg {String} navId - reference Id for navbar.
4369
4370  * 
4371  * @constructor
4372  * Create a new nav group
4373  * @param {Object} config The config object
4374  */
4375
4376 Roo.bootstrap.NavGroup = function(config){
4377     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4378     this.navItems = [];
4379    
4380     Roo.bootstrap.NavGroup.register(this);
4381      this.addEvents({
4382         /**
4383              * @event changed
4384              * Fires when the active item changes
4385              * @param {Roo.bootstrap.NavGroup} this
4386              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4387              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4388          */
4389         'changed': true
4390      });
4391     
4392 };
4393
4394 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4395     
4396     align: '',
4397     inverse: false,
4398     form: false,
4399     type: 'nav',
4400     navId : '',
4401     // private
4402     
4403     navItems : false, 
4404     
4405     getAutoCreate : function()
4406     {
4407         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4408         
4409         cfg = {
4410             tag : 'ul',
4411             cls: 'nav' 
4412         };
4413         if (Roo.bootstrap.version == 4) {
4414             if (['tabs','pills'].indexOf(this.type) != -1) {
4415                 cfg.cls += ' nav-' + this.type; 
4416             } else {
4417                 cfg.cls += ' navbar-nav';
4418             }
4419         } else {
4420             if (['tabs','pills'].indexOf(this.type) != -1) {
4421                 cfg.cls += ' nav-' + this.type
4422             } else {
4423                 if (this.type !== 'nav') {
4424                     Roo.log('nav type must be nav/tabs/pills')
4425                 }
4426                 cfg.cls += ' navbar-nav'
4427             }
4428         }
4429         
4430         if (this.parent() && this.parent().sidebar) {
4431             cfg = {
4432                 tag: 'ul',
4433                 cls: 'dashboard-menu sidebar-menu'
4434             };
4435             
4436             return cfg;
4437         }
4438         
4439         if (this.form === true) {
4440             cfg = {
4441                 tag: 'form',
4442                 cls: 'navbar-form form-inline'
4443             };
4444             
4445             if (this.align === 'right') {
4446                 cfg.cls += ' navbar-right ml-md-auto';
4447             } else {
4448                 cfg.cls += ' navbar-left';
4449             }
4450         }
4451         
4452         if (this.align === 'right') {
4453             cfg.cls += ' navbar-right ml-md-auto';
4454         } else {
4455             cfg.cls += ' mr-auto';
4456         }
4457         
4458         if (this.inverse) {
4459             cfg.cls += ' navbar-inverse';
4460             
4461         }
4462         
4463         
4464         return cfg;
4465     },
4466     /**
4467     * sets the active Navigation item
4468     * @param {Roo.bootstrap.NavItem} the new current navitem
4469     */
4470     setActiveItem : function(item)
4471     {
4472         var prev = false;
4473         Roo.each(this.navItems, function(v){
4474             if (v == item) {
4475                 return ;
4476             }
4477             if (v.isActive()) {
4478                 v.setActive(false, true);
4479                 prev = v;
4480                 
4481             }
4482             
4483         });
4484
4485         item.setActive(true, true);
4486         this.fireEvent('changed', this, item, prev);
4487         
4488         
4489     },
4490     /**
4491     * gets the active Navigation item
4492     * @return {Roo.bootstrap.NavItem} the current navitem
4493     */
4494     getActive : function()
4495     {
4496         
4497         var prev = false;
4498         Roo.each(this.navItems, function(v){
4499             
4500             if (v.isActive()) {
4501                 prev = v;
4502                 
4503             }
4504             
4505         });
4506         return prev;
4507     },
4508     
4509     indexOfNav : function()
4510     {
4511         
4512         var prev = false;
4513         Roo.each(this.navItems, function(v,i){
4514             
4515             if (v.isActive()) {
4516                 prev = i;
4517                 
4518             }
4519             
4520         });
4521         return prev;
4522     },
4523     /**
4524     * adds a Navigation item
4525     * @param {Roo.bootstrap.NavItem} the navitem to add
4526     */
4527     addItem : function(cfg)
4528     {
4529         if (this.form && Roo.bootstrap.version == 4) {
4530             cfg.tag = 'div';
4531         }
4532         var cn = new Roo.bootstrap.NavItem(cfg);
4533         this.register(cn);
4534         cn.parentId = this.id;
4535         cn.onRender(this.el, null);
4536         return cn;
4537     },
4538     /**
4539     * register a Navigation item
4540     * @param {Roo.bootstrap.NavItem} the navitem to add
4541     */
4542     register : function(item)
4543     {
4544         this.navItems.push( item);
4545         item.navId = this.navId;
4546     
4547     },
4548     
4549     /**
4550     * clear all the Navigation item
4551     */
4552    
4553     clearAll : function()
4554     {
4555         this.navItems = [];
4556         this.el.dom.innerHTML = '';
4557     },
4558     
4559     getNavItem: function(tabId)
4560     {
4561         var ret = false;
4562         Roo.each(this.navItems, function(e) {
4563             if (e.tabId == tabId) {
4564                ret =  e;
4565                return false;
4566             }
4567             return true;
4568             
4569         });
4570         return ret;
4571     },
4572     
4573     setActiveNext : function()
4574     {
4575         var i = this.indexOfNav(this.getActive());
4576         if (i > this.navItems.length) {
4577             return;
4578         }
4579         this.setActiveItem(this.navItems[i+1]);
4580     },
4581     setActivePrev : function()
4582     {
4583         var i = this.indexOfNav(this.getActive());
4584         if (i  < 1) {
4585             return;
4586         }
4587         this.setActiveItem(this.navItems[i-1]);
4588     },
4589     clearWasActive : function(except) {
4590         Roo.each(this.navItems, function(e) {
4591             if (e.tabId != except.tabId && e.was_active) {
4592                e.was_active = false;
4593                return false;
4594             }
4595             return true;
4596             
4597         });
4598     },
4599     getWasActive : function ()
4600     {
4601         var r = false;
4602         Roo.each(this.navItems, function(e) {
4603             if (e.was_active) {
4604                r = e;
4605                return false;
4606             }
4607             return true;
4608             
4609         });
4610         return r;
4611     }
4612     
4613     
4614 });
4615
4616  
4617 Roo.apply(Roo.bootstrap.NavGroup, {
4618     
4619     groups: {},
4620      /**
4621     * register a Navigation Group
4622     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4623     */
4624     register : function(navgrp)
4625     {
4626         this.groups[navgrp.navId] = navgrp;
4627         
4628     },
4629     /**
4630     * fetch a Navigation Group based on the navigation ID
4631     * @param {string} the navgroup to add
4632     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4633     */
4634     get: function(navId) {
4635         if (typeof(this.groups[navId]) == 'undefined') {
4636             return false;
4637             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4638         }
4639         return this.groups[navId] ;
4640     }
4641     
4642     
4643     
4644 });
4645
4646  /*
4647  * - LGPL
4648  *
4649  * row
4650  * 
4651  */
4652
4653 /**
4654  * @class Roo.bootstrap.NavItem
4655  * @extends Roo.bootstrap.Component
4656  * Bootstrap Navbar.NavItem class
4657  * @cfg {String} href  link to
4658  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4659
4660  * @cfg {String} html content of button
4661  * @cfg {String} badge text inside badge
4662  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4663  * @cfg {String} glyphicon DEPRICATED - use fa
4664  * @cfg {String} icon DEPRICATED - use fa
4665  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4666  * @cfg {Boolean} active Is item active
4667  * @cfg {Boolean} disabled Is item disabled
4668  
4669  * @cfg {Boolean} preventDefault (true | false) default false
4670  * @cfg {String} tabId the tab that this item activates.
4671  * @cfg {String} tagtype (a|span) render as a href or span?
4672  * @cfg {Boolean} animateRef (true|false) link to element default false  
4673   
4674  * @constructor
4675  * Create a new Navbar Item
4676  * @param {Object} config The config object
4677  */
4678 Roo.bootstrap.NavItem = function(config){
4679     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4680     this.addEvents({
4681         // raw events
4682         /**
4683          * @event click
4684          * The raw click event for the entire grid.
4685          * @param {Roo.EventObject} e
4686          */
4687         "click" : true,
4688          /**
4689             * @event changed
4690             * Fires when the active item active state changes
4691             * @param {Roo.bootstrap.NavItem} this
4692             * @param {boolean} state the new state
4693              
4694          */
4695         'changed': true,
4696         /**
4697             * @event scrollto
4698             * Fires when scroll to element
4699             * @param {Roo.bootstrap.NavItem} this
4700             * @param {Object} options
4701             * @param {Roo.EventObject} e
4702              
4703          */
4704         'scrollto': true
4705     });
4706    
4707 };
4708
4709 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4710     
4711     href: false,
4712     html: '',
4713     badge: '',
4714     icon: false,
4715     fa : false,
4716     glyphicon: false,
4717     active: false,
4718     preventDefault : false,
4719     tabId : false,
4720     tagtype : 'a',
4721     tag: 'li',
4722     disabled : false,
4723     animateRef : false,
4724     was_active : false,
4725     button_weight : '',
4726     button_outline : false,
4727     
4728     navLink: false,
4729     
4730     getAutoCreate : function(){
4731          
4732         var cfg = {
4733             tag: this.tag,
4734             cls: 'nav-item'
4735         };
4736         
4737         if (this.active) {
4738             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4739         }
4740         if (this.disabled) {
4741             cfg.cls += ' disabled';
4742         }
4743         
4744         // BS4 only?
4745         if (this.button_weight.length) {
4746             cfg.tag = this.href ? 'a' : 'button';
4747             cfg.html = this.html || '';
4748             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4749             if (this.href) {
4750                 cfg.href = this.href;
4751             }
4752             if (this.fa) {
4753                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4754             }
4755             
4756             // menu .. should add dropdown-menu class - so no need for carat..
4757             
4758             if (this.badge !== '') {
4759                  
4760                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4761             }
4762             return cfg;
4763         }
4764         
4765         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4766             cfg.cn = [
4767                 {
4768                     tag: this.tagtype,
4769                     href : this.href || "#",
4770                     html: this.html || ''
4771                 }
4772             ];
4773             if (this.tagtype == 'a') {
4774                 cfg.cn[0].cls = 'nav-link';
4775             }
4776             if (this.icon) {
4777                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4778             }
4779             if (this.fa) {
4780                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4781             }
4782             if(this.glyphicon) {
4783                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4784             }
4785             
4786             if (this.menu) {
4787                 
4788                 cfg.cn[0].html += " <span class='caret'></span>";
4789              
4790             }
4791             
4792             if (this.badge !== '') {
4793                  
4794                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4795             }
4796         }
4797         
4798         
4799         
4800         return cfg;
4801     },
4802     onRender : function(ct, position)
4803     {
4804        // Roo.log("Call onRender: " + this.xtype);
4805         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4806             this.tag = 'div';
4807         }
4808         
4809         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4810         this.navLink = this.el.select('.nav-link',true).first();
4811         return ret;
4812     },
4813       
4814     
4815     initEvents: function() 
4816     {
4817         if (typeof (this.menu) != 'undefined') {
4818             this.menu.parentType = this.xtype;
4819             this.menu.triggerEl = this.el;
4820             this.menu = this.addxtype(Roo.apply({}, this.menu));
4821         }
4822         
4823         this.el.select('a',true).on('click', this.onClick, this);
4824         
4825         if(this.tagtype == 'span'){
4826             this.el.select('span',true).on('click', this.onClick, this);
4827         }
4828        
4829         // at this point parent should be available..
4830         this.parent().register(this);
4831     },
4832     
4833     onClick : function(e)
4834     {
4835         if (e.getTarget('.dropdown-menu-item')) {
4836             // did you click on a menu itemm.... - then don't trigger onclick..
4837             return;
4838         }
4839         
4840         if(
4841                 this.preventDefault || 
4842                 this.href == '#' 
4843         ){
4844             Roo.log("NavItem - prevent Default?");
4845             e.preventDefault();
4846         }
4847         
4848         if (this.disabled) {
4849             return;
4850         }
4851         
4852         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4853         if (tg && tg.transition) {
4854             Roo.log("waiting for the transitionend");
4855             return;
4856         }
4857         
4858         
4859         
4860         //Roo.log("fire event clicked");
4861         if(this.fireEvent('click', this, e) === false){
4862             return;
4863         };
4864         
4865         if(this.tagtype == 'span'){
4866             return;
4867         }
4868         
4869         //Roo.log(this.href);
4870         var ael = this.el.select('a',true).first();
4871         //Roo.log(ael);
4872         
4873         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4874             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4875             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4876                 return; // ignore... - it's a 'hash' to another page.
4877             }
4878             Roo.log("NavItem - prevent Default?");
4879             e.preventDefault();
4880             this.scrollToElement(e);
4881         }
4882         
4883         
4884         var p =  this.parent();
4885    
4886         if (['tabs','pills'].indexOf(p.type)!==-1) {
4887             if (typeof(p.setActiveItem) !== 'undefined') {
4888                 p.setActiveItem(this);
4889             }
4890         }
4891         
4892         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4893         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4894             // remove the collapsed menu expand...
4895             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4896         }
4897     },
4898     
4899     isActive: function () {
4900         return this.active
4901     },
4902     setActive : function(state, fire, is_was_active)
4903     {
4904         if (this.active && !state && this.navId) {
4905             this.was_active = true;
4906             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4907             if (nv) {
4908                 nv.clearWasActive(this);
4909             }
4910             
4911         }
4912         this.active = state;
4913         
4914         if (!state ) {
4915             this.el.removeClass('active');
4916             this.navLink ? this.navLink.removeClass('active') : false;
4917         } else if (!this.el.hasClass('active')) {
4918             
4919             this.el.addClass('active');
4920             if (Roo.bootstrap.version == 4 && this.navLink ) {
4921                 this.navLink.addClass('active');
4922             }
4923             
4924         }
4925         if (fire) {
4926             this.fireEvent('changed', this, state);
4927         }
4928         
4929         // show a panel if it's registered and related..
4930         
4931         if (!this.navId || !this.tabId || !state || is_was_active) {
4932             return;
4933         }
4934         
4935         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4936         if (!tg) {
4937             return;
4938         }
4939         var pan = tg.getPanelByName(this.tabId);
4940         if (!pan) {
4941             return;
4942         }
4943         // if we can not flip to new panel - go back to old nav highlight..
4944         if (false == tg.showPanel(pan)) {
4945             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4946             if (nv) {
4947                 var onav = nv.getWasActive();
4948                 if (onav) {
4949                     onav.setActive(true, false, true);
4950                 }
4951             }
4952             
4953         }
4954         
4955         
4956         
4957     },
4958      // this should not be here...
4959     setDisabled : function(state)
4960     {
4961         this.disabled = state;
4962         if (!state ) {
4963             this.el.removeClass('disabled');
4964         } else if (!this.el.hasClass('disabled')) {
4965             this.el.addClass('disabled');
4966         }
4967         
4968     },
4969     
4970     /**
4971      * Fetch the element to display the tooltip on.
4972      * @return {Roo.Element} defaults to this.el
4973      */
4974     tooltipEl : function()
4975     {
4976         return this.el.select('' + this.tagtype + '', true).first();
4977     },
4978     
4979     scrollToElement : function(e)
4980     {
4981         var c = document.body;
4982         
4983         /*
4984          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4985          */
4986         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4987             c = document.documentElement;
4988         }
4989         
4990         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4991         
4992         if(!target){
4993             return;
4994         }
4995
4996         var o = target.calcOffsetsTo(c);
4997         
4998         var options = {
4999             target : target,
5000             value : o[1]
5001         };
5002         
5003         this.fireEvent('scrollto', this, options, e);
5004         
5005         Roo.get(c).scrollTo('top', options.value, true);
5006         
5007         return;
5008     }
5009 });
5010  
5011
5012  /*
5013  * - LGPL
5014  *
5015  * sidebar item
5016  *
5017  *  li
5018  *    <span> icon </span>
5019  *    <span> text </span>
5020  *    <span>badge </span>
5021  */
5022
5023 /**
5024  * @class Roo.bootstrap.NavSidebarItem
5025  * @extends Roo.bootstrap.NavItem
5026  * Bootstrap Navbar.NavSidebarItem class
5027  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5028  * {Boolean} open is the menu open
5029  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5030  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5031  * {String} buttonSize (sm|md|lg)the extra classes for the button
5032  * {Boolean} showArrow show arrow next to the text (default true)
5033  * @constructor
5034  * Create a new Navbar Button
5035  * @param {Object} config The config object
5036  */
5037 Roo.bootstrap.NavSidebarItem = function(config){
5038     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5039     this.addEvents({
5040         // raw events
5041         /**
5042          * @event click
5043          * The raw click event for the entire grid.
5044          * @param {Roo.EventObject} e
5045          */
5046         "click" : true,
5047          /**
5048             * @event changed
5049             * Fires when the active item active state changes
5050             * @param {Roo.bootstrap.NavSidebarItem} this
5051             * @param {boolean} state the new state
5052              
5053          */
5054         'changed': true
5055     });
5056    
5057 };
5058
5059 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5060     
5061     badgeWeight : 'default',
5062     
5063     open: false,
5064     
5065     buttonView : false,
5066     
5067     buttonWeight : 'default',
5068     
5069     buttonSize : 'md',
5070     
5071     showArrow : true,
5072     
5073     getAutoCreate : function(){
5074         
5075         
5076         var a = {
5077                 tag: 'a',
5078                 href : this.href || '#',
5079                 cls: '',
5080                 html : '',
5081                 cn : []
5082         };
5083         
5084         if(this.buttonView){
5085             a = {
5086                 tag: 'button',
5087                 href : this.href || '#',
5088                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5089                 html : this.html,
5090                 cn : []
5091             };
5092         }
5093         
5094         var cfg = {
5095             tag: 'li',
5096             cls: '',
5097             cn: [ a ]
5098         };
5099         
5100         if (this.active) {
5101             cfg.cls += ' active';
5102         }
5103         
5104         if (this.disabled) {
5105             cfg.cls += ' disabled';
5106         }
5107         if (this.open) {
5108             cfg.cls += ' open x-open';
5109         }
5110         // left icon..
5111         if (this.glyphicon || this.icon) {
5112             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5113             a.cn.push({ tag : 'i', cls : c }) ;
5114         }
5115         
5116         if(!this.buttonView){
5117             var span = {
5118                 tag: 'span',
5119                 html : this.html || ''
5120             };
5121
5122             a.cn.push(span);
5123             
5124         }
5125         
5126         if (this.badge !== '') {
5127             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5128         }
5129         
5130         if (this.menu) {
5131             
5132             if(this.showArrow){
5133                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5134             }
5135             
5136             a.cls += ' dropdown-toggle treeview' ;
5137         }
5138         
5139         return cfg;
5140     },
5141     
5142     initEvents : function()
5143     { 
5144         if (typeof (this.menu) != 'undefined') {
5145             this.menu.parentType = this.xtype;
5146             this.menu.triggerEl = this.el;
5147             this.menu = this.addxtype(Roo.apply({}, this.menu));
5148         }
5149         
5150         this.el.on('click', this.onClick, this);
5151         
5152         if(this.badge !== ''){
5153             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5154         }
5155         
5156     },
5157     
5158     onClick : function(e)
5159     {
5160         if(this.disabled){
5161             e.preventDefault();
5162             return;
5163         }
5164         
5165         if(this.preventDefault){
5166             e.preventDefault();
5167         }
5168         
5169         this.fireEvent('click', this, e);
5170     },
5171     
5172     disable : function()
5173     {
5174         this.setDisabled(true);
5175     },
5176     
5177     enable : function()
5178     {
5179         this.setDisabled(false);
5180     },
5181     
5182     setDisabled : function(state)
5183     {
5184         if(this.disabled == state){
5185             return;
5186         }
5187         
5188         this.disabled = state;
5189         
5190         if (state) {
5191             this.el.addClass('disabled');
5192             return;
5193         }
5194         
5195         this.el.removeClass('disabled');
5196         
5197         return;
5198     },
5199     
5200     setActive : function(state)
5201     {
5202         if(this.active == state){
5203             return;
5204         }
5205         
5206         this.active = state;
5207         
5208         if (state) {
5209             this.el.addClass('active');
5210             return;
5211         }
5212         
5213         this.el.removeClass('active');
5214         
5215         return;
5216     },
5217     
5218     isActive: function () 
5219     {
5220         return this.active;
5221     },
5222     
5223     setBadge : function(str)
5224     {
5225         if(!this.badgeEl){
5226             return;
5227         }
5228         
5229         this.badgeEl.dom.innerHTML = str;
5230     }
5231     
5232    
5233      
5234  
5235 });
5236  
5237
5238  /*
5239  * - LGPL
5240  *
5241  * row
5242  * 
5243  */
5244
5245 /**
5246  * @class Roo.bootstrap.Row
5247  * @extends Roo.bootstrap.Component
5248  * Bootstrap Row class (contains columns...)
5249  * 
5250  * @constructor
5251  * Create a new Row
5252  * @param {Object} config The config object
5253  */
5254
5255 Roo.bootstrap.Row = function(config){
5256     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5257 };
5258
5259 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5260     
5261     getAutoCreate : function(){
5262        return {
5263             cls: 'row clearfix'
5264        };
5265     }
5266     
5267     
5268 });
5269
5270  
5271
5272  /*
5273  * - LGPL
5274  *
5275  * element
5276  * 
5277  */
5278
5279 /**
5280  * @class Roo.bootstrap.Element
5281  * @extends Roo.bootstrap.Component
5282  * Bootstrap Element class
5283  * @cfg {String} html contents of the element
5284  * @cfg {String} tag tag of the element
5285  * @cfg {String} cls class of the element
5286  * @cfg {Boolean} preventDefault (true|false) default false
5287  * @cfg {Boolean} clickable (true|false) default false
5288  * 
5289  * @constructor
5290  * Create a new Element
5291  * @param {Object} config The config object
5292  */
5293
5294 Roo.bootstrap.Element = function(config){
5295     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5296     
5297     this.addEvents({
5298         // raw events
5299         /**
5300          * @event click
5301          * When a element is chick
5302          * @param {Roo.bootstrap.Element} this
5303          * @param {Roo.EventObject} e
5304          */
5305         "click" : true
5306     });
5307 };
5308
5309 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5310     
5311     tag: 'div',
5312     cls: '',
5313     html: '',
5314     preventDefault: false, 
5315     clickable: false,
5316     
5317     getAutoCreate : function(){
5318         
5319         var cfg = {
5320             tag: this.tag,
5321             // cls: this.cls, double assign in parent class Component.js :: onRender
5322             html: this.html
5323         };
5324         
5325         return cfg;
5326     },
5327     
5328     initEvents: function() 
5329     {
5330         Roo.bootstrap.Element.superclass.initEvents.call(this);
5331         
5332         if(this.clickable){
5333             this.el.on('click', this.onClick, this);
5334         }
5335         
5336     },
5337     
5338     onClick : function(e)
5339     {
5340         if(this.preventDefault){
5341             e.preventDefault();
5342         }
5343         
5344         this.fireEvent('click', this, e);
5345     },
5346     
5347     getValue : function()
5348     {
5349         return this.el.dom.innerHTML;
5350     },
5351     
5352     setValue : function(value)
5353     {
5354         this.el.dom.innerHTML = value;
5355     }
5356    
5357 });
5358
5359  
5360
5361  /*
5362  * - LGPL
5363  *
5364  * pagination
5365  * 
5366  */
5367
5368 /**
5369  * @class Roo.bootstrap.Pagination
5370  * @extends Roo.bootstrap.Component
5371  * Bootstrap Pagination class
5372  * @cfg {String} size xs | sm | md | lg
5373  * @cfg {Boolean} inverse false | true
5374  * 
5375  * @constructor
5376  * Create a new Pagination
5377  * @param {Object} config The config object
5378  */
5379
5380 Roo.bootstrap.Pagination = function(config){
5381     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5382 };
5383
5384 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5385     
5386     cls: false,
5387     size: false,
5388     inverse: false,
5389     
5390     getAutoCreate : function(){
5391         var cfg = {
5392             tag: 'ul',
5393                 cls: 'pagination'
5394         };
5395         if (this.inverse) {
5396             cfg.cls += ' inverse';
5397         }
5398         if (this.html) {
5399             cfg.html=this.html;
5400         }
5401         if (this.cls) {
5402             cfg.cls += " " + this.cls;
5403         }
5404         return cfg;
5405     }
5406    
5407 });
5408
5409  
5410
5411  /*
5412  * - LGPL
5413  *
5414  * Pagination item
5415  * 
5416  */
5417
5418
5419 /**
5420  * @class Roo.bootstrap.PaginationItem
5421  * @extends Roo.bootstrap.Component
5422  * Bootstrap PaginationItem class
5423  * @cfg {String} html text
5424  * @cfg {String} href the link
5425  * @cfg {Boolean} preventDefault (true | false) default true
5426  * @cfg {Boolean} active (true | false) default false
5427  * @cfg {Boolean} disabled default false
5428  * 
5429  * 
5430  * @constructor
5431  * Create a new PaginationItem
5432  * @param {Object} config The config object
5433  */
5434
5435
5436 Roo.bootstrap.PaginationItem = function(config){
5437     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5438     this.addEvents({
5439         // raw events
5440         /**
5441          * @event click
5442          * The raw click event for the entire grid.
5443          * @param {Roo.EventObject} e
5444          */
5445         "click" : true
5446     });
5447 };
5448
5449 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5450     
5451     href : false,
5452     html : false,
5453     preventDefault: true,
5454     active : false,
5455     cls : false,
5456     disabled: false,
5457     
5458     getAutoCreate : function(){
5459         var cfg= {
5460             tag: 'li',
5461             cn: [
5462                 {
5463                     tag : 'a',
5464                     href : this.href ? this.href : '#',
5465                     html : this.html ? this.html : ''
5466                 }
5467             ]
5468         };
5469         
5470         if(this.cls){
5471             cfg.cls = this.cls;
5472         }
5473         
5474         if(this.disabled){
5475             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5476         }
5477         
5478         if(this.active){
5479             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5480         }
5481         
5482         return cfg;
5483     },
5484     
5485     initEvents: function() {
5486         
5487         this.el.on('click', this.onClick, this);
5488         
5489     },
5490     onClick : function(e)
5491     {
5492         Roo.log('PaginationItem on click ');
5493         if(this.preventDefault){
5494             e.preventDefault();
5495         }
5496         
5497         if(this.disabled){
5498             return;
5499         }
5500         
5501         this.fireEvent('click', this, e);
5502     }
5503    
5504 });
5505
5506  
5507
5508  /*
5509  * - LGPL
5510  *
5511  * slider
5512  * 
5513  */
5514
5515
5516 /**
5517  * @class Roo.bootstrap.Slider
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Slider class
5520  *    
5521  * @constructor
5522  * Create a new Slider
5523  * @param {Object} config The config object
5524  */
5525
5526 Roo.bootstrap.Slider = function(config){
5527     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5528 };
5529
5530 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5531     
5532     getAutoCreate : function(){
5533         
5534         var cfg = {
5535             tag: 'div',
5536             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5537             cn: [
5538                 {
5539                     tag: 'a',
5540                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5541                 }
5542             ]
5543         };
5544         
5545         return cfg;
5546     }
5547    
5548 });
5549
5550  /*
5551  * Based on:
5552  * Ext JS Library 1.1.1
5553  * Copyright(c) 2006-2007, Ext JS, LLC.
5554  *
5555  * Originally Released Under LGPL - original licence link has changed is not relivant.
5556  *
5557  * Fork - LGPL
5558  * <script type="text/javascript">
5559  */
5560  
5561
5562 /**
5563  * @class Roo.grid.ColumnModel
5564  * @extends Roo.util.Observable
5565  * This is the default implementation of a ColumnModel used by the Grid. It defines
5566  * the columns in the grid.
5567  * <br>Usage:<br>
5568  <pre><code>
5569  var colModel = new Roo.grid.ColumnModel([
5570         {header: "Ticker", width: 60, sortable: true, locked: true},
5571         {header: "Company Name", width: 150, sortable: true},
5572         {header: "Market Cap.", width: 100, sortable: true},
5573         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5574         {header: "Employees", width: 100, sortable: true, resizable: false}
5575  ]);
5576  </code></pre>
5577  * <p>
5578  
5579  * The config options listed for this class are options which may appear in each
5580  * individual column definition.
5581  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5582  * @constructor
5583  * @param {Object} config An Array of column config objects. See this class's
5584  * config objects for details.
5585 */
5586 Roo.grid.ColumnModel = function(config){
5587         /**
5588      * The config passed into the constructor
5589      */
5590     this.config = config;
5591     this.lookup = {};
5592
5593     // if no id, create one
5594     // if the column does not have a dataIndex mapping,
5595     // map it to the order it is in the config
5596     for(var i = 0, len = config.length; i < len; i++){
5597         var c = config[i];
5598         if(typeof c.dataIndex == "undefined"){
5599             c.dataIndex = i;
5600         }
5601         if(typeof c.renderer == "string"){
5602             c.renderer = Roo.util.Format[c.renderer];
5603         }
5604         if(typeof c.id == "undefined"){
5605             c.id = Roo.id();
5606         }
5607         if(c.editor && c.editor.xtype){
5608             c.editor  = Roo.factory(c.editor, Roo.grid);
5609         }
5610         if(c.editor && c.editor.isFormField){
5611             c.editor = new Roo.grid.GridEditor(c.editor);
5612         }
5613         this.lookup[c.id] = c;
5614     }
5615
5616     /**
5617      * The width of columns which have no width specified (defaults to 100)
5618      * @type Number
5619      */
5620     this.defaultWidth = 100;
5621
5622     /**
5623      * Default sortable of columns which have no sortable specified (defaults to false)
5624      * @type Boolean
5625      */
5626     this.defaultSortable = false;
5627
5628     this.addEvents({
5629         /**
5630              * @event widthchange
5631              * Fires when the width of a column changes.
5632              * @param {ColumnModel} this
5633              * @param {Number} columnIndex The column index
5634              * @param {Number} newWidth The new width
5635              */
5636             "widthchange": true,
5637         /**
5638              * @event headerchange
5639              * Fires when the text of a header changes.
5640              * @param {ColumnModel} this
5641              * @param {Number} columnIndex The column index
5642              * @param {Number} newText The new header text
5643              */
5644             "headerchange": true,
5645         /**
5646              * @event hiddenchange
5647              * Fires when a column is hidden or "unhidden".
5648              * @param {ColumnModel} this
5649              * @param {Number} columnIndex The column index
5650              * @param {Boolean} hidden true if hidden, false otherwise
5651              */
5652             "hiddenchange": true,
5653             /**
5654          * @event columnmoved
5655          * Fires when a column is moved.
5656          * @param {ColumnModel} this
5657          * @param {Number} oldIndex
5658          * @param {Number} newIndex
5659          */
5660         "columnmoved" : true,
5661         /**
5662          * @event columlockchange
5663          * Fires when a column's locked state is changed
5664          * @param {ColumnModel} this
5665          * @param {Number} colIndex
5666          * @param {Boolean} locked true if locked
5667          */
5668         "columnlockchange" : true
5669     });
5670     Roo.grid.ColumnModel.superclass.constructor.call(this);
5671 };
5672 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5673     /**
5674      * @cfg {String} header The header text to display in the Grid view.
5675      */
5676     /**
5677      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5678      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5679      * specified, the column's index is used as an index into the Record's data Array.
5680      */
5681     /**
5682      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5683      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5684      */
5685     /**
5686      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5687      * Defaults to the value of the {@link #defaultSortable} property.
5688      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5689      */
5690     /**
5691      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5692      */
5693     /**
5694      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5695      */
5696     /**
5697      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5698      */
5699     /**
5700      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5701      */
5702     /**
5703      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5704      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5705      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5706      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5707      */
5708        /**
5709      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5710      */
5711     /**
5712      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5713      */
5714     /**
5715      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5716      */
5717     /**
5718      * @cfg {String} cursor (Optional)
5719      */
5720     /**
5721      * @cfg {String} tooltip (Optional)
5722      */
5723     /**
5724      * @cfg {Number} xs (Optional)
5725      */
5726     /**
5727      * @cfg {Number} sm (Optional)
5728      */
5729     /**
5730      * @cfg {Number} md (Optional)
5731      */
5732     /**
5733      * @cfg {Number} lg (Optional)
5734      */
5735     /**
5736      * Returns the id of the column at the specified index.
5737      * @param {Number} index The column index
5738      * @return {String} the id
5739      */
5740     getColumnId : function(index){
5741         return this.config[index].id;
5742     },
5743
5744     /**
5745      * Returns the column for a specified id.
5746      * @param {String} id The column id
5747      * @return {Object} the column
5748      */
5749     getColumnById : function(id){
5750         return this.lookup[id];
5751     },
5752
5753     
5754     /**
5755      * Returns the column for a specified dataIndex.
5756      * @param {String} dataIndex The column dataIndex
5757      * @return {Object|Boolean} the column or false if not found
5758      */
5759     getColumnByDataIndex: function(dataIndex){
5760         var index = this.findColumnIndex(dataIndex);
5761         return index > -1 ? this.config[index] : false;
5762     },
5763     
5764     /**
5765      * Returns the index for a specified column id.
5766      * @param {String} id The column id
5767      * @return {Number} the index, or -1 if not found
5768      */
5769     getIndexById : function(id){
5770         for(var i = 0, len = this.config.length; i < len; i++){
5771             if(this.config[i].id == id){
5772                 return i;
5773             }
5774         }
5775         return -1;
5776     },
5777     
5778     /**
5779      * Returns the index for a specified column dataIndex.
5780      * @param {String} dataIndex The column dataIndex
5781      * @return {Number} the index, or -1 if not found
5782      */
5783     
5784     findColumnIndex : function(dataIndex){
5785         for(var i = 0, len = this.config.length; i < len; i++){
5786             if(this.config[i].dataIndex == dataIndex){
5787                 return i;
5788             }
5789         }
5790         return -1;
5791     },
5792     
5793     
5794     moveColumn : function(oldIndex, newIndex){
5795         var c = this.config[oldIndex];
5796         this.config.splice(oldIndex, 1);
5797         this.config.splice(newIndex, 0, c);
5798         this.dataMap = null;
5799         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5800     },
5801
5802     isLocked : function(colIndex){
5803         return this.config[colIndex].locked === true;
5804     },
5805
5806     setLocked : function(colIndex, value, suppressEvent){
5807         if(this.isLocked(colIndex) == value){
5808             return;
5809         }
5810         this.config[colIndex].locked = value;
5811         if(!suppressEvent){
5812             this.fireEvent("columnlockchange", this, colIndex, value);
5813         }
5814     },
5815
5816     getTotalLockedWidth : function(){
5817         var totalWidth = 0;
5818         for(var i = 0; i < this.config.length; i++){
5819             if(this.isLocked(i) && !this.isHidden(i)){
5820                 this.totalWidth += this.getColumnWidth(i);
5821             }
5822         }
5823         return totalWidth;
5824     },
5825
5826     getLockedCount : function(){
5827         for(var i = 0, len = this.config.length; i < len; i++){
5828             if(!this.isLocked(i)){
5829                 return i;
5830             }
5831         }
5832         
5833         return this.config.length;
5834     },
5835
5836     /**
5837      * Returns the number of columns.
5838      * @return {Number}
5839      */
5840     getColumnCount : function(visibleOnly){
5841         if(visibleOnly === true){
5842             var c = 0;
5843             for(var i = 0, len = this.config.length; i < len; i++){
5844                 if(!this.isHidden(i)){
5845                     c++;
5846                 }
5847             }
5848             return c;
5849         }
5850         return this.config.length;
5851     },
5852
5853     /**
5854      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5855      * @param {Function} fn
5856      * @param {Object} scope (optional)
5857      * @return {Array} result
5858      */
5859     getColumnsBy : function(fn, scope){
5860         var r = [];
5861         for(var i = 0, len = this.config.length; i < len; i++){
5862             var c = this.config[i];
5863             if(fn.call(scope||this, c, i) === true){
5864                 r[r.length] = c;
5865             }
5866         }
5867         return r;
5868     },
5869
5870     /**
5871      * Returns true if the specified column is sortable.
5872      * @param {Number} col The column index
5873      * @return {Boolean}
5874      */
5875     isSortable : function(col){
5876         if(typeof this.config[col].sortable == "undefined"){
5877             return this.defaultSortable;
5878         }
5879         return this.config[col].sortable;
5880     },
5881
5882     /**
5883      * Returns the rendering (formatting) function defined for the column.
5884      * @param {Number} col The column index.
5885      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5886      */
5887     getRenderer : function(col){
5888         if(!this.config[col].renderer){
5889             return Roo.grid.ColumnModel.defaultRenderer;
5890         }
5891         return this.config[col].renderer;
5892     },
5893
5894     /**
5895      * Sets the rendering (formatting) function for a column.
5896      * @param {Number} col The column index
5897      * @param {Function} fn The function to use to process the cell's raw data
5898      * to return HTML markup for the grid view. The render function is called with
5899      * the following parameters:<ul>
5900      * <li>Data value.</li>
5901      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5902      * <li>css A CSS style string to apply to the table cell.</li>
5903      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5904      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5905      * <li>Row index</li>
5906      * <li>Column index</li>
5907      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5908      */
5909     setRenderer : function(col, fn){
5910         this.config[col].renderer = fn;
5911     },
5912
5913     /**
5914      * Returns the width for the specified column.
5915      * @param {Number} col The column index
5916      * @return {Number}
5917      */
5918     getColumnWidth : function(col){
5919         return this.config[col].width * 1 || this.defaultWidth;
5920     },
5921
5922     /**
5923      * Sets the width for a column.
5924      * @param {Number} col The column index
5925      * @param {Number} width The new width
5926      */
5927     setColumnWidth : function(col, width, suppressEvent){
5928         this.config[col].width = width;
5929         this.totalWidth = null;
5930         if(!suppressEvent){
5931              this.fireEvent("widthchange", this, col, width);
5932         }
5933     },
5934
5935     /**
5936      * Returns the total width of all columns.
5937      * @param {Boolean} includeHidden True to include hidden column widths
5938      * @return {Number}
5939      */
5940     getTotalWidth : function(includeHidden){
5941         if(!this.totalWidth){
5942             this.totalWidth = 0;
5943             for(var i = 0, len = this.config.length; i < len; i++){
5944                 if(includeHidden || !this.isHidden(i)){
5945                     this.totalWidth += this.getColumnWidth(i);
5946                 }
5947             }
5948         }
5949         return this.totalWidth;
5950     },
5951
5952     /**
5953      * Returns the header for the specified column.
5954      * @param {Number} col The column index
5955      * @return {String}
5956      */
5957     getColumnHeader : function(col){
5958         return this.config[col].header;
5959     },
5960
5961     /**
5962      * Sets the header for a column.
5963      * @param {Number} col The column index
5964      * @param {String} header The new header
5965      */
5966     setColumnHeader : function(col, header){
5967         this.config[col].header = header;
5968         this.fireEvent("headerchange", this, col, header);
5969     },
5970
5971     /**
5972      * Returns the tooltip for the specified column.
5973      * @param {Number} col The column index
5974      * @return {String}
5975      */
5976     getColumnTooltip : function(col){
5977             return this.config[col].tooltip;
5978     },
5979     /**
5980      * Sets the tooltip for a column.
5981      * @param {Number} col The column index
5982      * @param {String} tooltip The new tooltip
5983      */
5984     setColumnTooltip : function(col, tooltip){
5985             this.config[col].tooltip = tooltip;
5986     },
5987
5988     /**
5989      * Returns the dataIndex for the specified column.
5990      * @param {Number} col The column index
5991      * @return {Number}
5992      */
5993     getDataIndex : function(col){
5994         return this.config[col].dataIndex;
5995     },
5996
5997     /**
5998      * Sets the dataIndex for a column.
5999      * @param {Number} col The column index
6000      * @param {Number} dataIndex The new dataIndex
6001      */
6002     setDataIndex : function(col, dataIndex){
6003         this.config[col].dataIndex = dataIndex;
6004     },
6005
6006     
6007     
6008     /**
6009      * Returns true if the cell is editable.
6010      * @param {Number} colIndex The column index
6011      * @param {Number} rowIndex The row index - this is nto actually used..?
6012      * @return {Boolean}
6013      */
6014     isCellEditable : function(colIndex, rowIndex){
6015         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6016     },
6017
6018     /**
6019      * Returns the editor defined for the cell/column.
6020      * return false or null to disable editing.
6021      * @param {Number} colIndex The column index
6022      * @param {Number} rowIndex The row index
6023      * @return {Object}
6024      */
6025     getCellEditor : function(colIndex, rowIndex){
6026         return this.config[colIndex].editor;
6027     },
6028
6029     /**
6030      * Sets if a column is editable.
6031      * @param {Number} col The column index
6032      * @param {Boolean} editable True if the column is editable
6033      */
6034     setEditable : function(col, editable){
6035         this.config[col].editable = editable;
6036     },
6037
6038
6039     /**
6040      * Returns true if the column is hidden.
6041      * @param {Number} colIndex The column index
6042      * @return {Boolean}
6043      */
6044     isHidden : function(colIndex){
6045         return this.config[colIndex].hidden;
6046     },
6047
6048
6049     /**
6050      * Returns true if the column width cannot be changed
6051      */
6052     isFixed : function(colIndex){
6053         return this.config[colIndex].fixed;
6054     },
6055
6056     /**
6057      * Returns true if the column can be resized
6058      * @return {Boolean}
6059      */
6060     isResizable : function(colIndex){
6061         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6062     },
6063     /**
6064      * Sets if a column is hidden.
6065      * @param {Number} colIndex The column index
6066      * @param {Boolean} hidden True if the column is hidden
6067      */
6068     setHidden : function(colIndex, hidden){
6069         this.config[colIndex].hidden = hidden;
6070         this.totalWidth = null;
6071         this.fireEvent("hiddenchange", this, colIndex, hidden);
6072     },
6073
6074     /**
6075      * Sets the editor for a column.
6076      * @param {Number} col The column index
6077      * @param {Object} editor The editor object
6078      */
6079     setEditor : function(col, editor){
6080         this.config[col].editor = editor;
6081     }
6082 });
6083
6084 Roo.grid.ColumnModel.defaultRenderer = function(value)
6085 {
6086     if(typeof value == "object") {
6087         return value;
6088     }
6089         if(typeof value == "string" && value.length < 1){
6090             return "&#160;";
6091         }
6092     
6093         return String.format("{0}", value);
6094 };
6095
6096 // Alias for backwards compatibility
6097 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6098 /*
6099  * Based on:
6100  * Ext JS Library 1.1.1
6101  * Copyright(c) 2006-2007, Ext JS, LLC.
6102  *
6103  * Originally Released Under LGPL - original licence link has changed is not relivant.
6104  *
6105  * Fork - LGPL
6106  * <script type="text/javascript">
6107  */
6108  
6109 /**
6110  * @class Roo.LoadMask
6111  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6112  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6113  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6114  * element's UpdateManager load indicator and will be destroyed after the initial load.
6115  * @constructor
6116  * Create a new LoadMask
6117  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6118  * @param {Object} config The config object
6119  */
6120 Roo.LoadMask = function(el, config){
6121     this.el = Roo.get(el);
6122     Roo.apply(this, config);
6123     if(this.store){
6124         this.store.on('beforeload', this.onBeforeLoad, this);
6125         this.store.on('load', this.onLoad, this);
6126         this.store.on('loadexception', this.onLoadException, this);
6127         this.removeMask = false;
6128     }else{
6129         var um = this.el.getUpdateManager();
6130         um.showLoadIndicator = false; // disable the default indicator
6131         um.on('beforeupdate', this.onBeforeLoad, this);
6132         um.on('update', this.onLoad, this);
6133         um.on('failure', this.onLoad, this);
6134         this.removeMask = true;
6135     }
6136 };
6137
6138 Roo.LoadMask.prototype = {
6139     /**
6140      * @cfg {Boolean} removeMask
6141      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6142      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6143      */
6144     /**
6145      * @cfg {String} msg
6146      * The text to display in a centered loading message box (defaults to 'Loading...')
6147      */
6148     msg : 'Loading...',
6149     /**
6150      * @cfg {String} msgCls
6151      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6152      */
6153     msgCls : 'x-mask-loading',
6154
6155     /**
6156      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6157      * @type Boolean
6158      */
6159     disabled: false,
6160
6161     /**
6162      * Disables the mask to prevent it from being displayed
6163      */
6164     disable : function(){
6165        this.disabled = true;
6166     },
6167
6168     /**
6169      * Enables the mask so that it can be displayed
6170      */
6171     enable : function(){
6172         this.disabled = false;
6173     },
6174     
6175     onLoadException : function()
6176     {
6177         Roo.log(arguments);
6178         
6179         if (typeof(arguments[3]) != 'undefined') {
6180             Roo.MessageBox.alert("Error loading",arguments[3]);
6181         } 
6182         /*
6183         try {
6184             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6185                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6186             }   
6187         } catch(e) {
6188             
6189         }
6190         */
6191     
6192         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6193     },
6194     // private
6195     onLoad : function()
6196     {
6197         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6198     },
6199
6200     // private
6201     onBeforeLoad : function(){
6202         if(!this.disabled){
6203             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6204         }
6205     },
6206
6207     // private
6208     destroy : function(){
6209         if(this.store){
6210             this.store.un('beforeload', this.onBeforeLoad, this);
6211             this.store.un('load', this.onLoad, this);
6212             this.store.un('loadexception', this.onLoadException, this);
6213         }else{
6214             var um = this.el.getUpdateManager();
6215             um.un('beforeupdate', this.onBeforeLoad, this);
6216             um.un('update', this.onLoad, this);
6217             um.un('failure', this.onLoad, this);
6218         }
6219     }
6220 };/*
6221  * - LGPL
6222  *
6223  * table
6224  * 
6225  */
6226
6227 /**
6228  * @class Roo.bootstrap.Table
6229  * @extends Roo.bootstrap.Component
6230  * Bootstrap Table class
6231  * @cfg {String} cls table class
6232  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6233  * @cfg {String} bgcolor Specifies the background color for a table
6234  * @cfg {Number} border Specifies whether the table cells should have borders or not
6235  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6236  * @cfg {Number} cellspacing Specifies the space between cells
6237  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6238  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6239  * @cfg {String} sortable Specifies that the table should be sortable
6240  * @cfg {String} summary Specifies a summary of the content of a table
6241  * @cfg {Number} width Specifies the width of a table
6242  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6243  * 
6244  * @cfg {boolean} striped Should the rows be alternative striped
6245  * @cfg {boolean} bordered Add borders to the table
6246  * @cfg {boolean} hover Add hover highlighting
6247  * @cfg {boolean} condensed Format condensed
6248  * @cfg {boolean} responsive Format condensed
6249  * @cfg {Boolean} loadMask (true|false) default false
6250  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6251  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6252  * @cfg {Boolean} rowSelection (true|false) default false
6253  * @cfg {Boolean} cellSelection (true|false) default false
6254  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6255  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6256  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6257  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6258  
6259  * 
6260  * @constructor
6261  * Create a new Table
6262  * @param {Object} config The config object
6263  */
6264
6265 Roo.bootstrap.Table = function(config){
6266     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6267     
6268   
6269     
6270     // BC...
6271     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6272     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6273     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6274     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6275     
6276     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6277     if (this.sm) {
6278         this.sm.grid = this;
6279         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6280         this.sm = this.selModel;
6281         this.sm.xmodule = this.xmodule || false;
6282     }
6283     
6284     if (this.cm && typeof(this.cm.config) == 'undefined') {
6285         this.colModel = new Roo.grid.ColumnModel(this.cm);
6286         this.cm = this.colModel;
6287         this.cm.xmodule = this.xmodule || false;
6288     }
6289     if (this.store) {
6290         this.store= Roo.factory(this.store, Roo.data);
6291         this.ds = this.store;
6292         this.ds.xmodule = this.xmodule || false;
6293          
6294     }
6295     if (this.footer && this.store) {
6296         this.footer.dataSource = this.ds;
6297         this.footer = Roo.factory(this.footer);
6298     }
6299     
6300     /** @private */
6301     this.addEvents({
6302         /**
6303          * @event cellclick
6304          * Fires when a cell is clicked
6305          * @param {Roo.bootstrap.Table} this
6306          * @param {Roo.Element} el
6307          * @param {Number} rowIndex
6308          * @param {Number} columnIndex
6309          * @param {Roo.EventObject} e
6310          */
6311         "cellclick" : true,
6312         /**
6313          * @event celldblclick
6314          * Fires when a cell is double clicked
6315          * @param {Roo.bootstrap.Table} this
6316          * @param {Roo.Element} el
6317          * @param {Number} rowIndex
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "celldblclick" : true,
6322         /**
6323          * @event rowclick
6324          * Fires when a row is clicked
6325          * @param {Roo.bootstrap.Table} this
6326          * @param {Roo.Element} el
6327          * @param {Number} rowIndex
6328          * @param {Roo.EventObject} e
6329          */
6330         "rowclick" : true,
6331         /**
6332          * @event rowdblclick
6333          * Fires when a row is double clicked
6334          * @param {Roo.bootstrap.Table} this
6335          * @param {Roo.Element} el
6336          * @param {Number} rowIndex
6337          * @param {Roo.EventObject} e
6338          */
6339         "rowdblclick" : true,
6340         /**
6341          * @event mouseover
6342          * Fires when a mouseover occur
6343          * @param {Roo.bootstrap.Table} this
6344          * @param {Roo.Element} el
6345          * @param {Number} rowIndex
6346          * @param {Number} columnIndex
6347          * @param {Roo.EventObject} e
6348          */
6349         "mouseover" : true,
6350         /**
6351          * @event mouseout
6352          * Fires when a mouseout occur
6353          * @param {Roo.bootstrap.Table} this
6354          * @param {Roo.Element} el
6355          * @param {Number} rowIndex
6356          * @param {Number} columnIndex
6357          * @param {Roo.EventObject} e
6358          */
6359         "mouseout" : true,
6360         /**
6361          * @event rowclass
6362          * Fires when a row is rendered, so you can change add a style to it.
6363          * @param {Roo.bootstrap.Table} this
6364          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6365          */
6366         'rowclass' : true,
6367           /**
6368          * @event rowsrendered
6369          * Fires when all the  rows have been rendered
6370          * @param {Roo.bootstrap.Table} this
6371          */
6372         'rowsrendered' : true,
6373         /**
6374          * @event contextmenu
6375          * The raw contextmenu event for the entire grid.
6376          * @param {Roo.EventObject} e
6377          */
6378         "contextmenu" : true,
6379         /**
6380          * @event rowcontextmenu
6381          * Fires when a row is right clicked
6382          * @param {Roo.bootstrap.Table} this
6383          * @param {Number} rowIndex
6384          * @param {Roo.EventObject} e
6385          */
6386         "rowcontextmenu" : true,
6387         /**
6388          * @event cellcontextmenu
6389          * Fires when a cell is right clicked
6390          * @param {Roo.bootstrap.Table} this
6391          * @param {Number} rowIndex
6392          * @param {Number} cellIndex
6393          * @param {Roo.EventObject} e
6394          */
6395          "cellcontextmenu" : true,
6396          /**
6397          * @event headercontextmenu
6398          * Fires when a header is right clicked
6399          * @param {Roo.bootstrap.Table} this
6400          * @param {Number} columnIndex
6401          * @param {Roo.EventObject} e
6402          */
6403         "headercontextmenu" : true
6404     });
6405 };
6406
6407 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6408     
6409     cls: false,
6410     align: false,
6411     bgcolor: false,
6412     border: false,
6413     cellpadding: false,
6414     cellspacing: false,
6415     frame: false,
6416     rules: false,
6417     sortable: false,
6418     summary: false,
6419     width: false,
6420     striped : false,
6421     scrollBody : false,
6422     bordered: false,
6423     hover:  false,
6424     condensed : false,
6425     responsive : false,
6426     sm : false,
6427     cm : false,
6428     store : false,
6429     loadMask : false,
6430     footerShow : true,
6431     headerShow : true,
6432   
6433     rowSelection : false,
6434     cellSelection : false,
6435     layout : false,
6436     
6437     // Roo.Element - the tbody
6438     mainBody: false,
6439     // Roo.Element - thead element
6440     mainHead: false,
6441     
6442     container: false, // used by gridpanel...
6443     
6444     lazyLoad : false,
6445     
6446     CSS : Roo.util.CSS,
6447     
6448     auto_hide_footer : false,
6449     
6450     getAutoCreate : function()
6451     {
6452         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6453         
6454         cfg = {
6455             tag: 'table',
6456             cls : 'table',
6457             cn : []
6458         };
6459         if (this.scrollBody) {
6460             cfg.cls += ' table-body-fixed';
6461         }    
6462         if (this.striped) {
6463             cfg.cls += ' table-striped';
6464         }
6465         
6466         if (this.hover) {
6467             cfg.cls += ' table-hover';
6468         }
6469         if (this.bordered) {
6470             cfg.cls += ' table-bordered';
6471         }
6472         if (this.condensed) {
6473             cfg.cls += ' table-condensed';
6474         }
6475         if (this.responsive) {
6476             cfg.cls += ' table-responsive';
6477         }
6478         
6479         if (this.cls) {
6480             cfg.cls+=  ' ' +this.cls;
6481         }
6482         
6483         // this lot should be simplifed...
6484         var _t = this;
6485         var cp = [
6486             'align',
6487             'bgcolor',
6488             'border',
6489             'cellpadding',
6490             'cellspacing',
6491             'frame',
6492             'rules',
6493             'sortable',
6494             'summary',
6495             'width'
6496         ].forEach(function(k) {
6497             if (_t[k]) {
6498                 cfg[k] = _t[k];
6499             }
6500         });
6501         
6502         
6503         if (this.layout) {
6504             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6505         }
6506         
6507         if(this.store || this.cm){
6508             if(this.headerShow){
6509                 cfg.cn.push(this.renderHeader());
6510             }
6511             
6512             cfg.cn.push(this.renderBody());
6513             
6514             if(this.footerShow){
6515                 cfg.cn.push(this.renderFooter());
6516             }
6517             // where does this come from?
6518             //cfg.cls+=  ' TableGrid';
6519         }
6520         
6521         return { cn : [ cfg ] };
6522     },
6523     
6524     initEvents : function()
6525     {   
6526         if(!this.store || !this.cm){
6527             return;
6528         }
6529         if (this.selModel) {
6530             this.selModel.initEvents();
6531         }
6532         
6533         
6534         //Roo.log('initEvents with ds!!!!');
6535         
6536         this.mainBody = this.el.select('tbody', true).first();
6537         this.mainHead = this.el.select('thead', true).first();
6538         this.mainFoot = this.el.select('tfoot', true).first();
6539         
6540         
6541         
6542         var _this = this;
6543         
6544         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6545             e.on('click', _this.sort, _this);
6546         });
6547         
6548         this.mainBody.on("click", this.onClick, this);
6549         this.mainBody.on("dblclick", this.onDblClick, this);
6550         
6551         // why is this done????? = it breaks dialogs??
6552         //this.parent().el.setStyle('position', 'relative');
6553         
6554         
6555         if (this.footer) {
6556             this.footer.parentId = this.id;
6557             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6558             
6559             if(this.lazyLoad){
6560                 this.el.select('tfoot tr td').first().addClass('hide');
6561             }
6562         } 
6563         
6564         if(this.loadMask) {
6565             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6566         }
6567         
6568         this.store.on('load', this.onLoad, this);
6569         this.store.on('beforeload', this.onBeforeLoad, this);
6570         this.store.on('update', this.onUpdate, this);
6571         this.store.on('add', this.onAdd, this);
6572         this.store.on("clear", this.clear, this);
6573         
6574         this.el.on("contextmenu", this.onContextMenu, this);
6575         
6576         this.mainBody.on('scroll', this.onBodyScroll, this);
6577         
6578         this.cm.on("headerchange", this.onHeaderChange, this);
6579         
6580         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6581         
6582     },
6583     
6584     onContextMenu : function(e, t)
6585     {
6586         this.processEvent("contextmenu", e);
6587     },
6588     
6589     processEvent : function(name, e)
6590     {
6591         if (name != 'touchstart' ) {
6592             this.fireEvent(name, e);    
6593         }
6594         
6595         var t = e.getTarget();
6596         
6597         var cell = Roo.get(t);
6598         
6599         if(!cell){
6600             return;
6601         }
6602         
6603         if(cell.findParent('tfoot', false, true)){
6604             return;
6605         }
6606         
6607         if(cell.findParent('thead', false, true)){
6608             
6609             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6610                 cell = Roo.get(t).findParent('th', false, true);
6611                 if (!cell) {
6612                     Roo.log("failed to find th in thead?");
6613                     Roo.log(e.getTarget());
6614                     return;
6615                 }
6616             }
6617             
6618             var cellIndex = cell.dom.cellIndex;
6619             
6620             var ename = name == 'touchstart' ? 'click' : name;
6621             this.fireEvent("header" + ename, this, cellIndex, e);
6622             
6623             return;
6624         }
6625         
6626         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6627             cell = Roo.get(t).findParent('td', false, true);
6628             if (!cell) {
6629                 Roo.log("failed to find th in tbody?");
6630                 Roo.log(e.getTarget());
6631                 return;
6632             }
6633         }
6634         
6635         var row = cell.findParent('tr', false, true);
6636         var cellIndex = cell.dom.cellIndex;
6637         var rowIndex = row.dom.rowIndex - 1;
6638         
6639         if(row !== false){
6640             
6641             this.fireEvent("row" + name, this, rowIndex, e);
6642             
6643             if(cell !== false){
6644             
6645                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6646             }
6647         }
6648         
6649     },
6650     
6651     onMouseover : function(e, el)
6652     {
6653         var cell = Roo.get(el);
6654         
6655         if(!cell){
6656             return;
6657         }
6658         
6659         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6660             cell = cell.findParent('td', false, true);
6661         }
6662         
6663         var row = cell.findParent('tr', false, true);
6664         var cellIndex = cell.dom.cellIndex;
6665         var rowIndex = row.dom.rowIndex - 1; // start from 0
6666         
6667         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6668         
6669     },
6670     
6671     onMouseout : function(e, el)
6672     {
6673         var cell = Roo.get(el);
6674         
6675         if(!cell){
6676             return;
6677         }
6678         
6679         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6680             cell = cell.findParent('td', false, true);
6681         }
6682         
6683         var row = cell.findParent('tr', false, true);
6684         var cellIndex = cell.dom.cellIndex;
6685         var rowIndex = row.dom.rowIndex - 1; // start from 0
6686         
6687         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6688         
6689     },
6690     
6691     onClick : function(e, el)
6692     {
6693         var cell = Roo.get(el);
6694         
6695         if(!cell || (!this.cellSelection && !this.rowSelection)){
6696             return;
6697         }
6698         
6699         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6700             cell = cell.findParent('td', false, true);
6701         }
6702         
6703         if(!cell || typeof(cell) == 'undefined'){
6704             return;
6705         }
6706         
6707         var row = cell.findParent('tr', false, true);
6708         
6709         if(!row || typeof(row) == 'undefined'){
6710             return;
6711         }
6712         
6713         var cellIndex = cell.dom.cellIndex;
6714         var rowIndex = this.getRowIndex(row);
6715         
6716         // why??? - should these not be based on SelectionModel?
6717         if(this.cellSelection){
6718             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6719         }
6720         
6721         if(this.rowSelection){
6722             this.fireEvent('rowclick', this, row, rowIndex, e);
6723         }
6724         
6725         
6726     },
6727         
6728     onDblClick : function(e,el)
6729     {
6730         var cell = Roo.get(el);
6731         
6732         if(!cell || (!this.cellSelection && !this.rowSelection)){
6733             return;
6734         }
6735         
6736         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6737             cell = cell.findParent('td', false, true);
6738         }
6739         
6740         if(!cell || typeof(cell) == 'undefined'){
6741             return;
6742         }
6743         
6744         var row = cell.findParent('tr', false, true);
6745         
6746         if(!row || typeof(row) == 'undefined'){
6747             return;
6748         }
6749         
6750         var cellIndex = cell.dom.cellIndex;
6751         var rowIndex = this.getRowIndex(row);
6752         
6753         if(this.cellSelection){
6754             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6755         }
6756         
6757         if(this.rowSelection){
6758             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6759         }
6760     },
6761     
6762     sort : function(e,el)
6763     {
6764         var col = Roo.get(el);
6765         
6766         if(!col.hasClass('sortable')){
6767             return;
6768         }
6769         
6770         var sort = col.attr('sort');
6771         var dir = 'ASC';
6772         
6773         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6774             dir = 'DESC';
6775         }
6776         
6777         this.store.sortInfo = {field : sort, direction : dir};
6778         
6779         if (this.footer) {
6780             Roo.log("calling footer first");
6781             this.footer.onClick('first');
6782         } else {
6783         
6784             this.store.load({ params : { start : 0 } });
6785         }
6786     },
6787     
6788     renderHeader : function()
6789     {
6790         var header = {
6791             tag: 'thead',
6792             cn : []
6793         };
6794         
6795         var cm = this.cm;
6796         this.totalWidth = 0;
6797         
6798         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6799             
6800             var config = cm.config[i];
6801             
6802             var c = {
6803                 tag: 'th',
6804                 cls : 'x-hcol-' + i,
6805                 style : '',
6806                 html: cm.getColumnHeader(i)
6807             };
6808             
6809             var hh = '';
6810             
6811             if(typeof(config.sortable) != 'undefined' && config.sortable){
6812                 c.cls = 'sortable';
6813                 c.html = '<i class="glyphicon"></i>' + c.html;
6814             }
6815             
6816             // could use BS4 hidden-..-down 
6817             
6818             if(typeof(config.lgHeader) != 'undefined'){
6819                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6820             }
6821             
6822             if(typeof(config.mdHeader) != 'undefined'){
6823                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6824             }
6825             
6826             if(typeof(config.smHeader) != 'undefined'){
6827                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6828             }
6829             
6830             if(typeof(config.xsHeader) != 'undefined'){
6831                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6832             }
6833             
6834             if(hh.length){
6835                 c.html = hh;
6836             }
6837             
6838             if(typeof(config.tooltip) != 'undefined'){
6839                 c.tooltip = config.tooltip;
6840             }
6841             
6842             if(typeof(config.colspan) != 'undefined'){
6843                 c.colspan = config.colspan;
6844             }
6845             
6846             if(typeof(config.hidden) != 'undefined' && config.hidden){
6847                 c.style += ' display:none;';
6848             }
6849             
6850             if(typeof(config.dataIndex) != 'undefined'){
6851                 c.sort = config.dataIndex;
6852             }
6853             
6854            
6855             
6856             if(typeof(config.align) != 'undefined' && config.align.length){
6857                 c.style += ' text-align:' + config.align + ';';
6858             }
6859             
6860             if(typeof(config.width) != 'undefined'){
6861                 c.style += ' width:' + config.width + 'px;';
6862                 this.totalWidth += config.width;
6863             } else {
6864                 this.totalWidth += 100; // assume minimum of 100 per column?
6865             }
6866             
6867             if(typeof(config.cls) != 'undefined'){
6868                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6869             }
6870             
6871             ['xs','sm','md','lg'].map(function(size){
6872                 
6873                 if(typeof(config[size]) == 'undefined'){
6874                     return;
6875                 }
6876                  
6877                 if (!config[size]) { // 0 = hidden
6878                     // BS 4 '0' is treated as hide that column and below.
6879                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6880                     return;
6881                 }
6882                 
6883                 c.cls += ' col-' + size + '-' + config[size] + (
6884                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6885                 );
6886                 
6887                 
6888             });
6889             
6890             header.cn.push(c)
6891         }
6892         
6893         return header;
6894     },
6895     
6896     renderBody : function()
6897     {
6898         var body = {
6899             tag: 'tbody',
6900             cn : [
6901                 {
6902                     tag: 'tr',
6903                     cn : [
6904                         {
6905                             tag : 'td',
6906                             colspan :  this.cm.getColumnCount()
6907                         }
6908                     ]
6909                 }
6910             ]
6911         };
6912         
6913         return body;
6914     },
6915     
6916     renderFooter : function()
6917     {
6918         var footer = {
6919             tag: 'tfoot',
6920             cn : [
6921                 {
6922                     tag: 'tr',
6923                     cn : [
6924                         {
6925                             tag : 'td',
6926                             colspan :  this.cm.getColumnCount()
6927                         }
6928                     ]
6929                 }
6930             ]
6931         };
6932         
6933         return footer;
6934     },
6935     
6936     
6937     
6938     onLoad : function()
6939     {
6940 //        Roo.log('ds onload');
6941         this.clear();
6942         
6943         var _this = this;
6944         var cm = this.cm;
6945         var ds = this.store;
6946         
6947         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6948             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6949             if (_this.store.sortInfo) {
6950                     
6951                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6952                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6953                 }
6954                 
6955                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6956                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6957                 }
6958             }
6959         });
6960         
6961         var tbody =  this.mainBody;
6962               
6963         if(ds.getCount() > 0){
6964             ds.data.each(function(d,rowIndex){
6965                 var row =  this.renderRow(cm, ds, rowIndex);
6966                 
6967                 tbody.createChild(row);
6968                 
6969                 var _this = this;
6970                 
6971                 if(row.cellObjects.length){
6972                     Roo.each(row.cellObjects, function(r){
6973                         _this.renderCellObject(r);
6974                     })
6975                 }
6976                 
6977             }, this);
6978         }
6979         
6980         var tfoot = this.el.select('tfoot', true).first();
6981         
6982         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6983             
6984             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6985             
6986             var total = this.ds.getTotalCount();
6987             
6988             if(this.footer.pageSize < total){
6989                 this.mainFoot.show();
6990             }
6991         }
6992         
6993         Roo.each(this.el.select('tbody td', true).elements, function(e){
6994             e.on('mouseover', _this.onMouseover, _this);
6995         });
6996         
6997         Roo.each(this.el.select('tbody td', true).elements, function(e){
6998             e.on('mouseout', _this.onMouseout, _this);
6999         });
7000         this.fireEvent('rowsrendered', this);
7001         
7002         this.autoSize();
7003     },
7004     
7005     
7006     onUpdate : function(ds,record)
7007     {
7008         this.refreshRow(record);
7009         this.autoSize();
7010     },
7011     
7012     onRemove : function(ds, record, index, isUpdate){
7013         if(isUpdate !== true){
7014             this.fireEvent("beforerowremoved", this, index, record);
7015         }
7016         var bt = this.mainBody.dom;
7017         
7018         var rows = this.el.select('tbody > tr', true).elements;
7019         
7020         if(typeof(rows[index]) != 'undefined'){
7021             bt.removeChild(rows[index].dom);
7022         }
7023         
7024 //        if(bt.rows[index]){
7025 //            bt.removeChild(bt.rows[index]);
7026 //        }
7027         
7028         if(isUpdate !== true){
7029             //this.stripeRows(index);
7030             //this.syncRowHeights(index, index);
7031             //this.layout();
7032             this.fireEvent("rowremoved", this, index, record);
7033         }
7034     },
7035     
7036     onAdd : function(ds, records, rowIndex)
7037     {
7038         //Roo.log('on Add called');
7039         // - note this does not handle multiple adding very well..
7040         var bt = this.mainBody.dom;
7041         for (var i =0 ; i < records.length;i++) {
7042             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7043             //Roo.log(records[i]);
7044             //Roo.log(this.store.getAt(rowIndex+i));
7045             this.insertRow(this.store, rowIndex + i, false);
7046             return;
7047         }
7048         
7049     },
7050     
7051     
7052     refreshRow : function(record){
7053         var ds = this.store, index;
7054         if(typeof record == 'number'){
7055             index = record;
7056             record = ds.getAt(index);
7057         }else{
7058             index = ds.indexOf(record);
7059         }
7060         this.insertRow(ds, index, true);
7061         this.autoSize();
7062         this.onRemove(ds, record, index+1, true);
7063         this.autoSize();
7064         //this.syncRowHeights(index, index);
7065         //this.layout();
7066         this.fireEvent("rowupdated", this, index, record);
7067     },
7068     
7069     insertRow : function(dm, rowIndex, isUpdate){
7070         
7071         if(!isUpdate){
7072             this.fireEvent("beforerowsinserted", this, rowIndex);
7073         }
7074             //var s = this.getScrollState();
7075         var row = this.renderRow(this.cm, this.store, rowIndex);
7076         // insert before rowIndex..
7077         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7078         
7079         var _this = this;
7080                 
7081         if(row.cellObjects.length){
7082             Roo.each(row.cellObjects, function(r){
7083                 _this.renderCellObject(r);
7084             })
7085         }
7086             
7087         if(!isUpdate){
7088             this.fireEvent("rowsinserted", this, rowIndex);
7089             //this.syncRowHeights(firstRow, lastRow);
7090             //this.stripeRows(firstRow);
7091             //this.layout();
7092         }
7093         
7094     },
7095     
7096     
7097     getRowDom : function(rowIndex)
7098     {
7099         var rows = this.el.select('tbody > tr', true).elements;
7100         
7101         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7102         
7103     },
7104     // returns the object tree for a tr..
7105   
7106     
7107     renderRow : function(cm, ds, rowIndex) 
7108     {
7109         var d = ds.getAt(rowIndex);
7110         
7111         var row = {
7112             tag : 'tr',
7113             cls : 'x-row-' + rowIndex,
7114             cn : []
7115         };
7116             
7117         var cellObjects = [];
7118         
7119         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7120             var config = cm.config[i];
7121             
7122             var renderer = cm.getRenderer(i);
7123             var value = '';
7124             var id = false;
7125             
7126             if(typeof(renderer) !== 'undefined'){
7127                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7128             }
7129             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7130             // and are rendered into the cells after the row is rendered - using the id for the element.
7131             
7132             if(typeof(value) === 'object'){
7133                 id = Roo.id();
7134                 cellObjects.push({
7135                     container : id,
7136                     cfg : value 
7137                 })
7138             }
7139             
7140             var rowcfg = {
7141                 record: d,
7142                 rowIndex : rowIndex,
7143                 colIndex : i,
7144                 rowClass : ''
7145             };
7146
7147             this.fireEvent('rowclass', this, rowcfg);
7148             
7149             var td = {
7150                 tag: 'td',
7151                 cls : rowcfg.rowClass + ' x-col-' + i,
7152                 style: '',
7153                 html: (typeof(value) === 'object') ? '' : value
7154             };
7155             
7156             if (id) {
7157                 td.id = id;
7158             }
7159             
7160             if(typeof(config.colspan) != 'undefined'){
7161                 td.colspan = config.colspan;
7162             }
7163             
7164             if(typeof(config.hidden) != 'undefined' && config.hidden){
7165                 td.style += ' display:none;';
7166             }
7167             
7168             if(typeof(config.align) != 'undefined' && config.align.length){
7169                 td.style += ' text-align:' + config.align + ';';
7170             }
7171             if(typeof(config.valign) != 'undefined' && config.valign.length){
7172                 td.style += ' vertical-align:' + config.valign + ';';
7173             }
7174             
7175             if(typeof(config.width) != 'undefined'){
7176                 td.style += ' width:' +  config.width + 'px;';
7177             }
7178             
7179             if(typeof(config.cursor) != 'undefined'){
7180                 td.style += ' cursor:' +  config.cursor + ';';
7181             }
7182             
7183             if(typeof(config.cls) != 'undefined'){
7184                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7185             }
7186             
7187             ['xs','sm','md','lg'].map(function(size){
7188                 
7189                 if(typeof(config[size]) == 'undefined'){
7190                     return;
7191                 }
7192                 
7193                 
7194                   
7195                 if (!config[size]) { // 0 = hidden
7196                     // BS 4 '0' is treated as hide that column and below.
7197                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7198                     return;
7199                 }
7200                 
7201                 td.cls += ' col-' + size + '-' + config[size] + (
7202                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7203                 );
7204                  
7205
7206             });
7207             
7208             row.cn.push(td);
7209            
7210         }
7211         
7212         row.cellObjects = cellObjects;
7213         
7214         return row;
7215           
7216     },
7217     
7218     
7219     
7220     onBeforeLoad : function()
7221     {
7222         
7223     },
7224      /**
7225      * Remove all rows
7226      */
7227     clear : function()
7228     {
7229         this.el.select('tbody', true).first().dom.innerHTML = '';
7230     },
7231     /**
7232      * Show or hide a row.
7233      * @param {Number} rowIndex to show or hide
7234      * @param {Boolean} state hide
7235      */
7236     setRowVisibility : function(rowIndex, state)
7237     {
7238         var bt = this.mainBody.dom;
7239         
7240         var rows = this.el.select('tbody > tr', true).elements;
7241         
7242         if(typeof(rows[rowIndex]) == 'undefined'){
7243             return;
7244         }
7245         rows[rowIndex].dom.style.display = state ? '' : 'none';
7246     },
7247     
7248     
7249     getSelectionModel : function(){
7250         if(!this.selModel){
7251             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7252         }
7253         return this.selModel;
7254     },
7255     /*
7256      * Render the Roo.bootstrap object from renderder
7257      */
7258     renderCellObject : function(r)
7259     {
7260         var _this = this;
7261         
7262         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7263         
7264         var t = r.cfg.render(r.container);
7265         
7266         if(r.cfg.cn){
7267             Roo.each(r.cfg.cn, function(c){
7268                 var child = {
7269                     container: t.getChildContainer(),
7270                     cfg: c
7271                 };
7272                 _this.renderCellObject(child);
7273             })
7274         }
7275     },
7276     
7277     getRowIndex : function(row)
7278     {
7279         var rowIndex = -1;
7280         
7281         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7282             if(el != row){
7283                 return;
7284             }
7285             
7286             rowIndex = index;
7287         });
7288         
7289         return rowIndex;
7290     },
7291      /**
7292      * Returns the grid's underlying element = used by panel.Grid
7293      * @return {Element} The element
7294      */
7295     getGridEl : function(){
7296         return this.el;
7297     },
7298      /**
7299      * Forces a resize - used by panel.Grid
7300      * @return {Element} The element
7301      */
7302     autoSize : function()
7303     {
7304         //var ctr = Roo.get(this.container.dom.parentElement);
7305         var ctr = Roo.get(this.el.dom);
7306         
7307         var thd = this.getGridEl().select('thead',true).first();
7308         var tbd = this.getGridEl().select('tbody', true).first();
7309         var tfd = this.getGridEl().select('tfoot', true).first();
7310         
7311         var cw = ctr.getWidth();
7312         
7313         if (tbd) {
7314             
7315             tbd.setSize(ctr.getWidth(),
7316                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7317             );
7318             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7319             cw -= barsize;
7320         }
7321         cw = Math.max(cw, this.totalWidth);
7322         this.getGridEl().select('tr',true).setWidth(cw);
7323         // resize 'expandable coloumn?
7324         
7325         return; // we doe not have a view in this design..
7326         
7327     },
7328     onBodyScroll: function()
7329     {
7330         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7331         if(this.mainHead){
7332             this.mainHead.setStyle({
7333                 'position' : 'relative',
7334                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7335             });
7336         }
7337         
7338         if(this.lazyLoad){
7339             
7340             var scrollHeight = this.mainBody.dom.scrollHeight;
7341             
7342             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7343             
7344             var height = this.mainBody.getHeight();
7345             
7346             if(scrollHeight - height == scrollTop) {
7347                 
7348                 var total = this.ds.getTotalCount();
7349                 
7350                 if(this.footer.cursor + this.footer.pageSize < total){
7351                     
7352                     this.footer.ds.load({
7353                         params : {
7354                             start : this.footer.cursor + this.footer.pageSize,
7355                             limit : this.footer.pageSize
7356                         },
7357                         add : true
7358                     });
7359                 }
7360             }
7361             
7362         }
7363     },
7364     
7365     onHeaderChange : function()
7366     {
7367         var header = this.renderHeader();
7368         var table = this.el.select('table', true).first();
7369         
7370         this.mainHead.remove();
7371         this.mainHead = table.createChild(header, this.mainBody, false);
7372     },
7373     
7374     onHiddenChange : function(colModel, colIndex, hidden)
7375     {
7376         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7377         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7378         
7379         this.CSS.updateRule(thSelector, "display", "");
7380         this.CSS.updateRule(tdSelector, "display", "");
7381         
7382         if(hidden){
7383             this.CSS.updateRule(thSelector, "display", "none");
7384             this.CSS.updateRule(tdSelector, "display", "none");
7385         }
7386         
7387         this.onHeaderChange();
7388         this.onLoad();
7389     },
7390     
7391     setColumnWidth: function(col_index, width)
7392     {
7393         // width = "md-2 xs-2..."
7394         if(!this.colModel.config[col_index]) {
7395             return;
7396         }
7397         
7398         var w = width.split(" ");
7399         
7400         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7401         
7402         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7403         
7404         
7405         for(var j = 0; j < w.length; j++) {
7406             
7407             if(!w[j]) {
7408                 continue;
7409             }
7410             
7411             var size_cls = w[j].split("-");
7412             
7413             if(!Number.isInteger(size_cls[1] * 1)) {
7414                 continue;
7415             }
7416             
7417             if(!this.colModel.config[col_index][size_cls[0]]) {
7418                 continue;
7419             }
7420             
7421             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7422                 continue;
7423             }
7424             
7425             h_row[0].classList.replace(
7426                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7427                 "col-"+size_cls[0]+"-"+size_cls[1]
7428             );
7429             
7430             for(var i = 0; i < rows.length; i++) {
7431                 
7432                 var size_cls = w[j].split("-");
7433                 
7434                 if(!Number.isInteger(size_cls[1] * 1)) {
7435                     continue;
7436                 }
7437                 
7438                 if(!this.colModel.config[col_index][size_cls[0]]) {
7439                     continue;
7440                 }
7441                 
7442                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7443                     continue;
7444                 }
7445                 
7446                 rows[i].classList.replace(
7447                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7448                     "col-"+size_cls[0]+"-"+size_cls[1]
7449                 );
7450             }
7451             
7452             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7453         }
7454     }
7455 });
7456
7457  
7458
7459  /*
7460  * - LGPL
7461  *
7462  * table cell
7463  * 
7464  */
7465
7466 /**
7467  * @class Roo.bootstrap.TableCell
7468  * @extends Roo.bootstrap.Component
7469  * Bootstrap TableCell class
7470  * @cfg {String} html cell contain text
7471  * @cfg {String} cls cell class
7472  * @cfg {String} tag cell tag (td|th) default td
7473  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7474  * @cfg {String} align Aligns the content in a cell
7475  * @cfg {String} axis Categorizes cells
7476  * @cfg {String} bgcolor Specifies the background color of a cell
7477  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7478  * @cfg {Number} colspan Specifies the number of columns a cell should span
7479  * @cfg {String} headers Specifies one or more header cells a cell is related to
7480  * @cfg {Number} height Sets the height of a cell
7481  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7482  * @cfg {Number} rowspan Sets the number of rows a cell should span
7483  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7484  * @cfg {String} valign Vertical aligns the content in a cell
7485  * @cfg {Number} width Specifies the width of a cell
7486  * 
7487  * @constructor
7488  * Create a new TableCell
7489  * @param {Object} config The config object
7490  */
7491
7492 Roo.bootstrap.TableCell = function(config){
7493     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7494 };
7495
7496 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7497     
7498     html: false,
7499     cls: false,
7500     tag: false,
7501     abbr: false,
7502     align: false,
7503     axis: false,
7504     bgcolor: false,
7505     charoff: false,
7506     colspan: false,
7507     headers: false,
7508     height: false,
7509     nowrap: false,
7510     rowspan: false,
7511     scope: false,
7512     valign: false,
7513     width: false,
7514     
7515     
7516     getAutoCreate : function(){
7517         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7518         
7519         cfg = {
7520             tag: 'td'
7521         };
7522         
7523         if(this.tag){
7524             cfg.tag = this.tag;
7525         }
7526         
7527         if (this.html) {
7528             cfg.html=this.html
7529         }
7530         if (this.cls) {
7531             cfg.cls=this.cls
7532         }
7533         if (this.abbr) {
7534             cfg.abbr=this.abbr
7535         }
7536         if (this.align) {
7537             cfg.align=this.align
7538         }
7539         if (this.axis) {
7540             cfg.axis=this.axis
7541         }
7542         if (this.bgcolor) {
7543             cfg.bgcolor=this.bgcolor
7544         }
7545         if (this.charoff) {
7546             cfg.charoff=this.charoff
7547         }
7548         if (this.colspan) {
7549             cfg.colspan=this.colspan
7550         }
7551         if (this.headers) {
7552             cfg.headers=this.headers
7553         }
7554         if (this.height) {
7555             cfg.height=this.height
7556         }
7557         if (this.nowrap) {
7558             cfg.nowrap=this.nowrap
7559         }
7560         if (this.rowspan) {
7561             cfg.rowspan=this.rowspan
7562         }
7563         if (this.scope) {
7564             cfg.scope=this.scope
7565         }
7566         if (this.valign) {
7567             cfg.valign=this.valign
7568         }
7569         if (this.width) {
7570             cfg.width=this.width
7571         }
7572         
7573         
7574         return cfg;
7575     }
7576    
7577 });
7578
7579  
7580
7581  /*
7582  * - LGPL
7583  *
7584  * table row
7585  * 
7586  */
7587
7588 /**
7589  * @class Roo.bootstrap.TableRow
7590  * @extends Roo.bootstrap.Component
7591  * Bootstrap TableRow class
7592  * @cfg {String} cls row class
7593  * @cfg {String} align Aligns the content in a table row
7594  * @cfg {String} bgcolor Specifies a background color for a table row
7595  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7596  * @cfg {String} valign Vertical aligns the content in a table row
7597  * 
7598  * @constructor
7599  * Create a new TableRow
7600  * @param {Object} config The config object
7601  */
7602
7603 Roo.bootstrap.TableRow = function(config){
7604     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7605 };
7606
7607 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7608     
7609     cls: false,
7610     align: false,
7611     bgcolor: false,
7612     charoff: false,
7613     valign: false,
7614     
7615     getAutoCreate : function(){
7616         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7617         
7618         cfg = {
7619             tag: 'tr'
7620         };
7621             
7622         if(this.cls){
7623             cfg.cls = this.cls;
7624         }
7625         if(this.align){
7626             cfg.align = this.align;
7627         }
7628         if(this.bgcolor){
7629             cfg.bgcolor = this.bgcolor;
7630         }
7631         if(this.charoff){
7632             cfg.charoff = this.charoff;
7633         }
7634         if(this.valign){
7635             cfg.valign = this.valign;
7636         }
7637         
7638         return cfg;
7639     }
7640    
7641 });
7642
7643  
7644
7645  /*
7646  * - LGPL
7647  *
7648  * table body
7649  * 
7650  */
7651
7652 /**
7653  * @class Roo.bootstrap.TableBody
7654  * @extends Roo.bootstrap.Component
7655  * Bootstrap TableBody class
7656  * @cfg {String} cls element class
7657  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7658  * @cfg {String} align Aligns the content inside the element
7659  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7660  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7661  * 
7662  * @constructor
7663  * Create a new TableBody
7664  * @param {Object} config The config object
7665  */
7666
7667 Roo.bootstrap.TableBody = function(config){
7668     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7669 };
7670
7671 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7672     
7673     cls: false,
7674     tag: false,
7675     align: false,
7676     charoff: false,
7677     valign: false,
7678     
7679     getAutoCreate : function(){
7680         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7681         
7682         cfg = {
7683             tag: 'tbody'
7684         };
7685             
7686         if (this.cls) {
7687             cfg.cls=this.cls
7688         }
7689         if(this.tag){
7690             cfg.tag = this.tag;
7691         }
7692         
7693         if(this.align){
7694             cfg.align = this.align;
7695         }
7696         if(this.charoff){
7697             cfg.charoff = this.charoff;
7698         }
7699         if(this.valign){
7700             cfg.valign = this.valign;
7701         }
7702         
7703         return cfg;
7704     }
7705     
7706     
7707 //    initEvents : function()
7708 //    {
7709 //        
7710 //        if(!this.store){
7711 //            return;
7712 //        }
7713 //        
7714 //        this.store = Roo.factory(this.store, Roo.data);
7715 //        this.store.on('load', this.onLoad, this);
7716 //        
7717 //        this.store.load();
7718 //        
7719 //    },
7720 //    
7721 //    onLoad: function () 
7722 //    {   
7723 //        this.fireEvent('load', this);
7724 //    }
7725 //    
7726 //   
7727 });
7728
7729  
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741
7742 // as we use this in bootstrap.
7743 Roo.namespace('Roo.form');
7744  /**
7745  * @class Roo.form.Action
7746  * Internal Class used to handle form actions
7747  * @constructor
7748  * @param {Roo.form.BasicForm} el The form element or its id
7749  * @param {Object} config Configuration options
7750  */
7751
7752  
7753  
7754 // define the action interface
7755 Roo.form.Action = function(form, options){
7756     this.form = form;
7757     this.options = options || {};
7758 };
7759 /**
7760  * Client Validation Failed
7761  * @const 
7762  */
7763 Roo.form.Action.CLIENT_INVALID = 'client';
7764 /**
7765  * Server Validation Failed
7766  * @const 
7767  */
7768 Roo.form.Action.SERVER_INVALID = 'server';
7769  /**
7770  * Connect to Server Failed
7771  * @const 
7772  */
7773 Roo.form.Action.CONNECT_FAILURE = 'connect';
7774 /**
7775  * Reading Data from Server Failed
7776  * @const 
7777  */
7778 Roo.form.Action.LOAD_FAILURE = 'load';
7779
7780 Roo.form.Action.prototype = {
7781     type : 'default',
7782     failureType : undefined,
7783     response : undefined,
7784     result : undefined,
7785
7786     // interface method
7787     run : function(options){
7788
7789     },
7790
7791     // interface method
7792     success : function(response){
7793
7794     },
7795
7796     // interface method
7797     handleResponse : function(response){
7798
7799     },
7800
7801     // default connection failure
7802     failure : function(response){
7803         
7804         this.response = response;
7805         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7806         this.form.afterAction(this, false);
7807     },
7808
7809     processResponse : function(response){
7810         this.response = response;
7811         if(!response.responseText){
7812             return true;
7813         }
7814         this.result = this.handleResponse(response);
7815         return this.result;
7816     },
7817
7818     // utility functions used internally
7819     getUrl : function(appendParams){
7820         var url = this.options.url || this.form.url || this.form.el.dom.action;
7821         if(appendParams){
7822             var p = this.getParams();
7823             if(p){
7824                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7825             }
7826         }
7827         return url;
7828     },
7829
7830     getMethod : function(){
7831         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7832     },
7833
7834     getParams : function(){
7835         var bp = this.form.baseParams;
7836         var p = this.options.params;
7837         if(p){
7838             if(typeof p == "object"){
7839                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7840             }else if(typeof p == 'string' && bp){
7841                 p += '&' + Roo.urlEncode(bp);
7842             }
7843         }else if(bp){
7844             p = Roo.urlEncode(bp);
7845         }
7846         return p;
7847     },
7848
7849     createCallback : function(){
7850         return {
7851             success: this.success,
7852             failure: this.failure,
7853             scope: this,
7854             timeout: (this.form.timeout*1000),
7855             upload: this.form.fileUpload ? this.success : undefined
7856         };
7857     }
7858 };
7859
7860 Roo.form.Action.Submit = function(form, options){
7861     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7862 };
7863
7864 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7865     type : 'submit',
7866
7867     haveProgress : false,
7868     uploadComplete : false,
7869     
7870     // uploadProgress indicator.
7871     uploadProgress : function()
7872     {
7873         if (!this.form.progressUrl) {
7874             return;
7875         }
7876         
7877         if (!this.haveProgress) {
7878             Roo.MessageBox.progress("Uploading", "Uploading");
7879         }
7880         if (this.uploadComplete) {
7881            Roo.MessageBox.hide();
7882            return;
7883         }
7884         
7885         this.haveProgress = true;
7886    
7887         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7888         
7889         var c = new Roo.data.Connection();
7890         c.request({
7891             url : this.form.progressUrl,
7892             params: {
7893                 id : uid
7894             },
7895             method: 'GET',
7896             success : function(req){
7897                //console.log(data);
7898                 var rdata = false;
7899                 var edata;
7900                 try  {
7901                    rdata = Roo.decode(req.responseText)
7902                 } catch (e) {
7903                     Roo.log("Invalid data from server..");
7904                     Roo.log(edata);
7905                     return;
7906                 }
7907                 if (!rdata || !rdata.success) {
7908                     Roo.log(rdata);
7909                     Roo.MessageBox.alert(Roo.encode(rdata));
7910                     return;
7911                 }
7912                 var data = rdata.data;
7913                 
7914                 if (this.uploadComplete) {
7915                    Roo.MessageBox.hide();
7916                    return;
7917                 }
7918                    
7919                 if (data){
7920                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7921                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7922                     );
7923                 }
7924                 this.uploadProgress.defer(2000,this);
7925             },
7926        
7927             failure: function(data) {
7928                 Roo.log('progress url failed ');
7929                 Roo.log(data);
7930             },
7931             scope : this
7932         });
7933            
7934     },
7935     
7936     
7937     run : function()
7938     {
7939         // run get Values on the form, so it syncs any secondary forms.
7940         this.form.getValues();
7941         
7942         var o = this.options;
7943         var method = this.getMethod();
7944         var isPost = method == 'POST';
7945         if(o.clientValidation === false || this.form.isValid()){
7946             
7947             if (this.form.progressUrl) {
7948                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7949                     (new Date() * 1) + '' + Math.random());
7950                     
7951             } 
7952             
7953             
7954             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7955                 form:this.form.el.dom,
7956                 url:this.getUrl(!isPost),
7957                 method: method,
7958                 params:isPost ? this.getParams() : null,
7959                 isUpload: this.form.fileUpload,
7960                 formData : this.form.formData
7961             }));
7962             
7963             this.uploadProgress();
7964
7965         }else if (o.clientValidation !== false){ // client validation failed
7966             this.failureType = Roo.form.Action.CLIENT_INVALID;
7967             this.form.afterAction(this, false);
7968         }
7969     },
7970
7971     success : function(response)
7972     {
7973         this.uploadComplete= true;
7974         if (this.haveProgress) {
7975             Roo.MessageBox.hide();
7976         }
7977         
7978         
7979         var result = this.processResponse(response);
7980         if(result === true || result.success){
7981             this.form.afterAction(this, true);
7982             return;
7983         }
7984         if(result.errors){
7985             this.form.markInvalid(result.errors);
7986             this.failureType = Roo.form.Action.SERVER_INVALID;
7987         }
7988         this.form.afterAction(this, false);
7989     },
7990     failure : function(response)
7991     {
7992         this.uploadComplete= true;
7993         if (this.haveProgress) {
7994             Roo.MessageBox.hide();
7995         }
7996         
7997         this.response = response;
7998         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7999         this.form.afterAction(this, false);
8000     },
8001     
8002     handleResponse : function(response){
8003         if(this.form.errorReader){
8004             var rs = this.form.errorReader.read(response);
8005             var errors = [];
8006             if(rs.records){
8007                 for(var i = 0, len = rs.records.length; i < len; i++) {
8008                     var r = rs.records[i];
8009                     errors[i] = r.data;
8010                 }
8011             }
8012             if(errors.length < 1){
8013                 errors = null;
8014             }
8015             return {
8016                 success : rs.success,
8017                 errors : errors
8018             };
8019         }
8020         var ret = false;
8021         try {
8022             ret = Roo.decode(response.responseText);
8023         } catch (e) {
8024             ret = {
8025                 success: false,
8026                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8027                 errors : []
8028             };
8029         }
8030         return ret;
8031         
8032     }
8033 });
8034
8035
8036 Roo.form.Action.Load = function(form, options){
8037     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8038     this.reader = this.form.reader;
8039 };
8040
8041 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8042     type : 'load',
8043
8044     run : function(){
8045         
8046         Roo.Ajax.request(Roo.apply(
8047                 this.createCallback(), {
8048                     method:this.getMethod(),
8049                     url:this.getUrl(false),
8050                     params:this.getParams()
8051         }));
8052     },
8053
8054     success : function(response){
8055         
8056         var result = this.processResponse(response);
8057         if(result === true || !result.success || !result.data){
8058             this.failureType = Roo.form.Action.LOAD_FAILURE;
8059             this.form.afterAction(this, false);
8060             return;
8061         }
8062         this.form.clearInvalid();
8063         this.form.setValues(result.data);
8064         this.form.afterAction(this, true);
8065     },
8066
8067     handleResponse : function(response){
8068         if(this.form.reader){
8069             var rs = this.form.reader.read(response);
8070             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8071             return {
8072                 success : rs.success,
8073                 data : data
8074             };
8075         }
8076         return Roo.decode(response.responseText);
8077     }
8078 });
8079
8080 Roo.form.Action.ACTION_TYPES = {
8081     'load' : Roo.form.Action.Load,
8082     'submit' : Roo.form.Action.Submit
8083 };/*
8084  * - LGPL
8085  *
8086  * form
8087  *
8088  */
8089
8090 /**
8091  * @class Roo.bootstrap.Form
8092  * @extends Roo.bootstrap.Component
8093  * Bootstrap Form class
8094  * @cfg {String} method  GET | POST (default POST)
8095  * @cfg {String} labelAlign top | left (default top)
8096  * @cfg {String} align left  | right - for navbars
8097  * @cfg {Boolean} loadMask load mask when submit (default true)
8098
8099  *
8100  * @constructor
8101  * Create a new Form
8102  * @param {Object} config The config object
8103  */
8104
8105
8106 Roo.bootstrap.Form = function(config){
8107     
8108     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8109     
8110     Roo.bootstrap.Form.popover.apply();
8111     
8112     this.addEvents({
8113         /**
8114          * @event clientvalidation
8115          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8116          * @param {Form} this
8117          * @param {Boolean} valid true if the form has passed client-side validation
8118          */
8119         clientvalidation: true,
8120         /**
8121          * @event beforeaction
8122          * Fires before any action is performed. Return false to cancel the action.
8123          * @param {Form} this
8124          * @param {Action} action The action to be performed
8125          */
8126         beforeaction: true,
8127         /**
8128          * @event actionfailed
8129          * Fires when an action fails.
8130          * @param {Form} this
8131          * @param {Action} action The action that failed
8132          */
8133         actionfailed : true,
8134         /**
8135          * @event actioncomplete
8136          * Fires when an action is completed.
8137          * @param {Form} this
8138          * @param {Action} action The action that completed
8139          */
8140         actioncomplete : true
8141     });
8142 };
8143
8144 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8145
8146      /**
8147      * @cfg {String} method
8148      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8149      */
8150     method : 'POST',
8151     /**
8152      * @cfg {String} url
8153      * The URL to use for form actions if one isn't supplied in the action options.
8154      */
8155     /**
8156      * @cfg {Boolean} fileUpload
8157      * Set to true if this form is a file upload.
8158      */
8159
8160     /**
8161      * @cfg {Object} baseParams
8162      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8163      */
8164
8165     /**
8166      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8167      */
8168     timeout: 30,
8169     /**
8170      * @cfg {Sting} align (left|right) for navbar forms
8171      */
8172     align : 'left',
8173
8174     // private
8175     activeAction : null,
8176
8177     /**
8178      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8179      * element by passing it or its id or mask the form itself by passing in true.
8180      * @type Mixed
8181      */
8182     waitMsgTarget : false,
8183
8184     loadMask : true,
8185     
8186     /**
8187      * @cfg {Boolean} errorMask (true|false) default false
8188      */
8189     errorMask : false,
8190     
8191     /**
8192      * @cfg {Number} maskOffset Default 100
8193      */
8194     maskOffset : 100,
8195     
8196     /**
8197      * @cfg {Boolean} maskBody
8198      */
8199     maskBody : false,
8200
8201     getAutoCreate : function(){
8202
8203         var cfg = {
8204             tag: 'form',
8205             method : this.method || 'POST',
8206             id : this.id || Roo.id(),
8207             cls : ''
8208         };
8209         if (this.parent().xtype.match(/^Nav/)) {
8210             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8211
8212         }
8213
8214         if (this.labelAlign == 'left' ) {
8215             cfg.cls += ' form-horizontal';
8216         }
8217
8218
8219         return cfg;
8220     },
8221     initEvents : function()
8222     {
8223         this.el.on('submit', this.onSubmit, this);
8224         // this was added as random key presses on the form where triggering form submit.
8225         this.el.on('keypress', function(e) {
8226             if (e.getCharCode() != 13) {
8227                 return true;
8228             }
8229             // we might need to allow it for textareas.. and some other items.
8230             // check e.getTarget().
8231
8232             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8233                 return true;
8234             }
8235
8236             Roo.log("keypress blocked");
8237
8238             e.preventDefault();
8239             return false;
8240         });
8241         
8242     },
8243     // private
8244     onSubmit : function(e){
8245         e.stopEvent();
8246     },
8247
8248      /**
8249      * Returns true if client-side validation on the form is successful.
8250      * @return Boolean
8251      */
8252     isValid : function(){
8253         var items = this.getItems();
8254         var valid = true;
8255         var target = false;
8256         
8257         items.each(function(f){
8258             
8259             if(f.validate()){
8260                 return;
8261             }
8262             
8263             Roo.log('invalid field: ' + f.name);
8264             
8265             valid = false;
8266
8267             if(!target && f.el.isVisible(true)){
8268                 target = f;
8269             }
8270            
8271         });
8272         
8273         if(this.errorMask && !valid){
8274             Roo.bootstrap.Form.popover.mask(this, target);
8275         }
8276         
8277         return valid;
8278     },
8279     
8280     /**
8281      * Returns true if any fields in this form have changed since their original load.
8282      * @return Boolean
8283      */
8284     isDirty : function(){
8285         var dirty = false;
8286         var items = this.getItems();
8287         items.each(function(f){
8288            if(f.isDirty()){
8289                dirty = true;
8290                return false;
8291            }
8292            return true;
8293         });
8294         return dirty;
8295     },
8296      /**
8297      * Performs a predefined action (submit or load) or custom actions you define on this form.
8298      * @param {String} actionName The name of the action type
8299      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8300      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8301      * accept other config options):
8302      * <pre>
8303 Property          Type             Description
8304 ----------------  ---------------  ----------------------------------------------------------------------------------
8305 url               String           The url for the action (defaults to the form's url)
8306 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8307 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8308 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8309                                    validate the form on the client (defaults to false)
8310      * </pre>
8311      * @return {BasicForm} this
8312      */
8313     doAction : function(action, options){
8314         if(typeof action == 'string'){
8315             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8316         }
8317         if(this.fireEvent('beforeaction', this, action) !== false){
8318             this.beforeAction(action);
8319             action.run.defer(100, action);
8320         }
8321         return this;
8322     },
8323
8324     // private
8325     beforeAction : function(action){
8326         var o = action.options;
8327         
8328         if(this.loadMask){
8329             
8330             if(this.maskBody){
8331                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8332             } else {
8333                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8334             }
8335         }
8336         // not really supported yet.. ??
8337
8338         //if(this.waitMsgTarget === true){
8339         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8340         //}else if(this.waitMsgTarget){
8341         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8342         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8343         //}else {
8344         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8345        // }
8346
8347     },
8348
8349     // private
8350     afterAction : function(action, success){
8351         this.activeAction = null;
8352         var o = action.options;
8353
8354         if(this.loadMask){
8355             
8356             if(this.maskBody){
8357                 Roo.get(document.body).unmask();
8358             } else {
8359                 this.el.unmask();
8360             }
8361         }
8362         
8363         //if(this.waitMsgTarget === true){
8364 //            this.el.unmask();
8365         //}else if(this.waitMsgTarget){
8366         //    this.waitMsgTarget.unmask();
8367         //}else{
8368         //    Roo.MessageBox.updateProgress(1);
8369         //    Roo.MessageBox.hide();
8370        // }
8371         //
8372         if(success){
8373             if(o.reset){
8374                 this.reset();
8375             }
8376             Roo.callback(o.success, o.scope, [this, action]);
8377             this.fireEvent('actioncomplete', this, action);
8378
8379         }else{
8380
8381             // failure condition..
8382             // we have a scenario where updates need confirming.
8383             // eg. if a locking scenario exists..
8384             // we look for { errors : { needs_confirm : true }} in the response.
8385             if (
8386                 (typeof(action.result) != 'undefined')  &&
8387                 (typeof(action.result.errors) != 'undefined')  &&
8388                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8389            ){
8390                 var _t = this;
8391                 Roo.log("not supported yet");
8392                  /*
8393
8394                 Roo.MessageBox.confirm(
8395                     "Change requires confirmation",
8396                     action.result.errorMsg,
8397                     function(r) {
8398                         if (r != 'yes') {
8399                             return;
8400                         }
8401                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8402                     }
8403
8404                 );
8405                 */
8406
8407
8408                 return;
8409             }
8410
8411             Roo.callback(o.failure, o.scope, [this, action]);
8412             // show an error message if no failed handler is set..
8413             if (!this.hasListener('actionfailed')) {
8414                 Roo.log("need to add dialog support");
8415                 /*
8416                 Roo.MessageBox.alert("Error",
8417                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8418                         action.result.errorMsg :
8419                         "Saving Failed, please check your entries or try again"
8420                 );
8421                 */
8422             }
8423
8424             this.fireEvent('actionfailed', this, action);
8425         }
8426
8427     },
8428     /**
8429      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8430      * @param {String} id The value to search for
8431      * @return Field
8432      */
8433     findField : function(id){
8434         var items = this.getItems();
8435         var field = items.get(id);
8436         if(!field){
8437              items.each(function(f){
8438                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8439                     field = f;
8440                     return false;
8441                 }
8442                 return true;
8443             });
8444         }
8445         return field || null;
8446     },
8447      /**
8448      * Mark fields in this form invalid in bulk.
8449      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8450      * @return {BasicForm} this
8451      */
8452     markInvalid : function(errors){
8453         if(errors instanceof Array){
8454             for(var i = 0, len = errors.length; i < len; i++){
8455                 var fieldError = errors[i];
8456                 var f = this.findField(fieldError.id);
8457                 if(f){
8458                     f.markInvalid(fieldError.msg);
8459                 }
8460             }
8461         }else{
8462             var field, id;
8463             for(id in errors){
8464                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8465                     field.markInvalid(errors[id]);
8466                 }
8467             }
8468         }
8469         //Roo.each(this.childForms || [], function (f) {
8470         //    f.markInvalid(errors);
8471         //});
8472
8473         return this;
8474     },
8475
8476     /**
8477      * Set values for fields in this form in bulk.
8478      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8479      * @return {BasicForm} this
8480      */
8481     setValues : function(values){
8482         if(values instanceof Array){ // array of objects
8483             for(var i = 0, len = values.length; i < len; i++){
8484                 var v = values[i];
8485                 var f = this.findField(v.id);
8486                 if(f){
8487                     f.setValue(v.value);
8488                     if(this.trackResetOnLoad){
8489                         f.originalValue = f.getValue();
8490                     }
8491                 }
8492             }
8493         }else{ // object hash
8494             var field, id;
8495             for(id in values){
8496                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8497
8498                     if (field.setFromData &&
8499                         field.valueField &&
8500                         field.displayField &&
8501                         // combos' with local stores can
8502                         // be queried via setValue()
8503                         // to set their value..
8504                         (field.store && !field.store.isLocal)
8505                         ) {
8506                         // it's a combo
8507                         var sd = { };
8508                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8509                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8510                         field.setFromData(sd);
8511
8512                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8513                         
8514                         field.setFromData(values);
8515                         
8516                     } else {
8517                         field.setValue(values[id]);
8518                     }
8519
8520
8521                     if(this.trackResetOnLoad){
8522                         field.originalValue = field.getValue();
8523                     }
8524                 }
8525             }
8526         }
8527
8528         //Roo.each(this.childForms || [], function (f) {
8529         //    f.setValues(values);
8530         //});
8531
8532         return this;
8533     },
8534
8535     /**
8536      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8537      * they are returned as an array.
8538      * @param {Boolean} asString
8539      * @return {Object}
8540      */
8541     getValues : function(asString){
8542         //if (this.childForms) {
8543             // copy values from the child forms
8544         //    Roo.each(this.childForms, function (f) {
8545         //        this.setValues(f.getValues());
8546         //    }, this);
8547         //}
8548
8549
8550
8551         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8552         if(asString === true){
8553             return fs;
8554         }
8555         return Roo.urlDecode(fs);
8556     },
8557
8558     /**
8559      * Returns the fields in this form as an object with key/value pairs.
8560      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8561      * @return {Object}
8562      */
8563     getFieldValues : function(with_hidden)
8564     {
8565         var items = this.getItems();
8566         var ret = {};
8567         items.each(function(f){
8568             
8569             if (!f.getName()) {
8570                 return;
8571             }
8572             
8573             var v = f.getValue();
8574             
8575             if (f.inputType =='radio') {
8576                 if (typeof(ret[f.getName()]) == 'undefined') {
8577                     ret[f.getName()] = ''; // empty..
8578                 }
8579
8580                 if (!f.el.dom.checked) {
8581                     return;
8582
8583                 }
8584                 v = f.el.dom.value;
8585
8586             }
8587             
8588             if(f.xtype == 'MoneyField'){
8589                 ret[f.currencyName] = f.getCurrency();
8590             }
8591
8592             // not sure if this supported any more..
8593             if ((typeof(v) == 'object') && f.getRawValue) {
8594                 v = f.getRawValue() ; // dates..
8595             }
8596             // combo boxes where name != hiddenName...
8597             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8598                 ret[f.name] = f.getRawValue();
8599             }
8600             ret[f.getName()] = v;
8601         });
8602
8603         return ret;
8604     },
8605
8606     /**
8607      * Clears all invalid messages in this form.
8608      * @return {BasicForm} this
8609      */
8610     clearInvalid : function(){
8611         var items = this.getItems();
8612
8613         items.each(function(f){
8614            f.clearInvalid();
8615         });
8616
8617         return this;
8618     },
8619
8620     /**
8621      * Resets this form.
8622      * @return {BasicForm} this
8623      */
8624     reset : function(){
8625         var items = this.getItems();
8626         items.each(function(f){
8627             f.reset();
8628         });
8629
8630         Roo.each(this.childForms || [], function (f) {
8631             f.reset();
8632         });
8633
8634
8635         return this;
8636     },
8637     
8638     getItems : function()
8639     {
8640         var r=new Roo.util.MixedCollection(false, function(o){
8641             return o.id || (o.id = Roo.id());
8642         });
8643         var iter = function(el) {
8644             if (el.inputEl) {
8645                 r.add(el);
8646             }
8647             if (!el.items) {
8648                 return;
8649             }
8650             Roo.each(el.items,function(e) {
8651                 iter(e);
8652             });
8653         };
8654
8655         iter(this);
8656         return r;
8657     },
8658     
8659     hideFields : function(items)
8660     {
8661         Roo.each(items, function(i){
8662             
8663             var f = this.findField(i);
8664             
8665             if(!f){
8666                 return;
8667             }
8668             
8669             f.hide();
8670             
8671         }, this);
8672     },
8673     
8674     showFields : function(items)
8675     {
8676         Roo.each(items, function(i){
8677             
8678             var f = this.findField(i);
8679             
8680             if(!f){
8681                 return;
8682             }
8683             
8684             f.show();
8685             
8686         }, this);
8687     }
8688
8689 });
8690
8691 Roo.apply(Roo.bootstrap.Form, {
8692     
8693     popover : {
8694         
8695         padding : 5,
8696         
8697         isApplied : false,
8698         
8699         isMasked : false,
8700         
8701         form : false,
8702         
8703         target : false,
8704         
8705         toolTip : false,
8706         
8707         intervalID : false,
8708         
8709         maskEl : false,
8710         
8711         apply : function()
8712         {
8713             if(this.isApplied){
8714                 return;
8715             }
8716             
8717             this.maskEl = {
8718                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8719                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8720                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8721                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8722             };
8723             
8724             this.maskEl.top.enableDisplayMode("block");
8725             this.maskEl.left.enableDisplayMode("block");
8726             this.maskEl.bottom.enableDisplayMode("block");
8727             this.maskEl.right.enableDisplayMode("block");
8728             
8729             this.toolTip = new Roo.bootstrap.Tooltip({
8730                 cls : 'roo-form-error-popover',
8731                 alignment : {
8732                     'left' : ['r-l', [-2,0], 'right'],
8733                     'right' : ['l-r', [2,0], 'left'],
8734                     'bottom' : ['tl-bl', [0,2], 'top'],
8735                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8736                 }
8737             });
8738             
8739             this.toolTip.render(Roo.get(document.body));
8740
8741             this.toolTip.el.enableDisplayMode("block");
8742             
8743             Roo.get(document.body).on('click', function(){
8744                 this.unmask();
8745             }, this);
8746             
8747             Roo.get(document.body).on('touchstart', function(){
8748                 this.unmask();
8749             }, this);
8750             
8751             this.isApplied = true
8752         },
8753         
8754         mask : function(form, target)
8755         {
8756             this.form = form;
8757             
8758             this.target = target;
8759             
8760             if(!this.form.errorMask || !target.el){
8761                 return;
8762             }
8763             
8764             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8765             
8766             Roo.log(scrollable);
8767             
8768             var ot = this.target.el.calcOffsetsTo(scrollable);
8769             
8770             var scrollTo = ot[1] - this.form.maskOffset;
8771             
8772             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8773             
8774             scrollable.scrollTo('top', scrollTo);
8775             
8776             var box = this.target.el.getBox();
8777             Roo.log(box);
8778             var zIndex = Roo.bootstrap.Modal.zIndex++;
8779
8780             
8781             this.maskEl.top.setStyle('position', 'absolute');
8782             this.maskEl.top.setStyle('z-index', zIndex);
8783             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8784             this.maskEl.top.setLeft(0);
8785             this.maskEl.top.setTop(0);
8786             this.maskEl.top.show();
8787             
8788             this.maskEl.left.setStyle('position', 'absolute');
8789             this.maskEl.left.setStyle('z-index', zIndex);
8790             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8791             this.maskEl.left.setLeft(0);
8792             this.maskEl.left.setTop(box.y - this.padding);
8793             this.maskEl.left.show();
8794
8795             this.maskEl.bottom.setStyle('position', 'absolute');
8796             this.maskEl.bottom.setStyle('z-index', zIndex);
8797             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8798             this.maskEl.bottom.setLeft(0);
8799             this.maskEl.bottom.setTop(box.bottom + this.padding);
8800             this.maskEl.bottom.show();
8801
8802             this.maskEl.right.setStyle('position', 'absolute');
8803             this.maskEl.right.setStyle('z-index', zIndex);
8804             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8805             this.maskEl.right.setLeft(box.right + this.padding);
8806             this.maskEl.right.setTop(box.y - this.padding);
8807             this.maskEl.right.show();
8808
8809             this.toolTip.bindEl = this.target.el;
8810
8811             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8812
8813             var tip = this.target.blankText;
8814
8815             if(this.target.getValue() !== '' ) {
8816                 
8817                 if (this.target.invalidText.length) {
8818                     tip = this.target.invalidText;
8819                 } else if (this.target.regexText.length){
8820                     tip = this.target.regexText;
8821                 }
8822             }
8823
8824             this.toolTip.show(tip);
8825
8826             this.intervalID = window.setInterval(function() {
8827                 Roo.bootstrap.Form.popover.unmask();
8828             }, 10000);
8829
8830             window.onwheel = function(){ return false;};
8831             
8832             (function(){ this.isMasked = true; }).defer(500, this);
8833             
8834         },
8835         
8836         unmask : function()
8837         {
8838             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8839                 return;
8840             }
8841             
8842             this.maskEl.top.setStyle('position', 'absolute');
8843             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8844             this.maskEl.top.hide();
8845
8846             this.maskEl.left.setStyle('position', 'absolute');
8847             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8848             this.maskEl.left.hide();
8849
8850             this.maskEl.bottom.setStyle('position', 'absolute');
8851             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8852             this.maskEl.bottom.hide();
8853
8854             this.maskEl.right.setStyle('position', 'absolute');
8855             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8856             this.maskEl.right.hide();
8857             
8858             this.toolTip.hide();
8859             
8860             this.toolTip.el.hide();
8861             
8862             window.onwheel = function(){ return true;};
8863             
8864             if(this.intervalID){
8865                 window.clearInterval(this.intervalID);
8866                 this.intervalID = false;
8867             }
8868             
8869             this.isMasked = false;
8870             
8871         }
8872         
8873     }
8874     
8875 });
8876
8877 /*
8878  * Based on:
8879  * Ext JS Library 1.1.1
8880  * Copyright(c) 2006-2007, Ext JS, LLC.
8881  *
8882  * Originally Released Under LGPL - original licence link has changed is not relivant.
8883  *
8884  * Fork - LGPL
8885  * <script type="text/javascript">
8886  */
8887 /**
8888  * @class Roo.form.VTypes
8889  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8890  * @singleton
8891  */
8892 Roo.form.VTypes = function(){
8893     // closure these in so they are only created once.
8894     var alpha = /^[a-zA-Z_]+$/;
8895     var alphanum = /^[a-zA-Z0-9_]+$/;
8896     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8897     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8898
8899     // All these messages and functions are configurable
8900     return {
8901         /**
8902          * The function used to validate email addresses
8903          * @param {String} value The email address
8904          */
8905         'email' : function(v){
8906             return email.test(v);
8907         },
8908         /**
8909          * The error text to display when the email validation function returns false
8910          * @type String
8911          */
8912         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8913         /**
8914          * The keystroke filter mask to be applied on email input
8915          * @type RegExp
8916          */
8917         'emailMask' : /[a-z0-9_\.\-@]/i,
8918
8919         /**
8920          * The function used to validate URLs
8921          * @param {String} value The URL
8922          */
8923         'url' : function(v){
8924             return url.test(v);
8925         },
8926         /**
8927          * The error text to display when the url validation function returns false
8928          * @type String
8929          */
8930         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8931         
8932         /**
8933          * The function used to validate alpha values
8934          * @param {String} value The value
8935          */
8936         'alpha' : function(v){
8937             return alpha.test(v);
8938         },
8939         /**
8940          * The error text to display when the alpha validation function returns false
8941          * @type String
8942          */
8943         'alphaText' : 'This field should only contain letters and _',
8944         /**
8945          * The keystroke filter mask to be applied on alpha input
8946          * @type RegExp
8947          */
8948         'alphaMask' : /[a-z_]/i,
8949
8950         /**
8951          * The function used to validate alphanumeric values
8952          * @param {String} value The value
8953          */
8954         'alphanum' : function(v){
8955             return alphanum.test(v);
8956         },
8957         /**
8958          * The error text to display when the alphanumeric validation function returns false
8959          * @type String
8960          */
8961         'alphanumText' : 'This field should only contain letters, numbers and _',
8962         /**
8963          * The keystroke filter mask to be applied on alphanumeric input
8964          * @type RegExp
8965          */
8966         'alphanumMask' : /[a-z0-9_]/i
8967     };
8968 }();/*
8969  * - LGPL
8970  *
8971  * Input
8972  * 
8973  */
8974
8975 /**
8976  * @class Roo.bootstrap.Input
8977  * @extends Roo.bootstrap.Component
8978  * Bootstrap Input class
8979  * @cfg {Boolean} disabled is it disabled
8980  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8981  * @cfg {String} name name of the input
8982  * @cfg {string} fieldLabel - the label associated
8983  * @cfg {string} placeholder - placeholder to put in text.
8984  * @cfg {string}  before - input group add on before
8985  * @cfg {string} after - input group add on after
8986  * @cfg {string} size - (lg|sm) or leave empty..
8987  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8988  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8989  * @cfg {Number} md colspan out of 12 for computer-sized screens
8990  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8991  * @cfg {string} value default value of the input
8992  * @cfg {Number} labelWidth set the width of label 
8993  * @cfg {Number} labellg set the width of label (1-12)
8994  * @cfg {Number} labelmd set the width of label (1-12)
8995  * @cfg {Number} labelsm set the width of label (1-12)
8996  * @cfg {Number} labelxs set the width of label (1-12)
8997  * @cfg {String} labelAlign (top|left)
8998  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8999  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9000  * @cfg {String} indicatorpos (left|right) default left
9001  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9002  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9003
9004  * @cfg {String} align (left|center|right) Default left
9005  * @cfg {Boolean} forceFeedback (true|false) Default false
9006  * 
9007  * @constructor
9008  * Create a new Input
9009  * @param {Object} config The config object
9010  */
9011
9012 Roo.bootstrap.Input = function(config){
9013     
9014     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9015     
9016     this.addEvents({
9017         /**
9018          * @event focus
9019          * Fires when this field receives input focus.
9020          * @param {Roo.form.Field} this
9021          */
9022         focus : true,
9023         /**
9024          * @event blur
9025          * Fires when this field loses input focus.
9026          * @param {Roo.form.Field} this
9027          */
9028         blur : true,
9029         /**
9030          * @event specialkey
9031          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9032          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9033          * @param {Roo.form.Field} this
9034          * @param {Roo.EventObject} e The event object
9035          */
9036         specialkey : true,
9037         /**
9038          * @event change
9039          * Fires just before the field blurs if the field value has changed.
9040          * @param {Roo.form.Field} this
9041          * @param {Mixed} newValue The new value
9042          * @param {Mixed} oldValue The original value
9043          */
9044         change : true,
9045         /**
9046          * @event invalid
9047          * Fires after the field has been marked as invalid.
9048          * @param {Roo.form.Field} this
9049          * @param {String} msg The validation message
9050          */
9051         invalid : true,
9052         /**
9053          * @event valid
9054          * Fires after the field has been validated with no errors.
9055          * @param {Roo.form.Field} this
9056          */
9057         valid : true,
9058          /**
9059          * @event keyup
9060          * Fires after the key up
9061          * @param {Roo.form.Field} this
9062          * @param {Roo.EventObject}  e The event Object
9063          */
9064         keyup : true
9065     });
9066 };
9067
9068 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9069      /**
9070      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9071       automatic validation (defaults to "keyup").
9072      */
9073     validationEvent : "keyup",
9074      /**
9075      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9076      */
9077     validateOnBlur : true,
9078     /**
9079      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9080      */
9081     validationDelay : 250,
9082      /**
9083      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9084      */
9085     focusClass : "x-form-focus",  // not needed???
9086     
9087        
9088     /**
9089      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9090      */
9091     invalidClass : "has-warning",
9092     
9093     /**
9094      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9095      */
9096     validClass : "has-success",
9097     
9098     /**
9099      * @cfg {Boolean} hasFeedback (true|false) default true
9100      */
9101     hasFeedback : true,
9102     
9103     /**
9104      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9105      */
9106     invalidFeedbackClass : "glyphicon-warning-sign",
9107     
9108     /**
9109      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9110      */
9111     validFeedbackClass : "glyphicon-ok",
9112     
9113     /**
9114      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9115      */
9116     selectOnFocus : false,
9117     
9118      /**
9119      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9120      */
9121     maskRe : null,
9122        /**
9123      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9124      */
9125     vtype : null,
9126     
9127       /**
9128      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9129      */
9130     disableKeyFilter : false,
9131     
9132        /**
9133      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9134      */
9135     disabled : false,
9136      /**
9137      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9138      */
9139     allowBlank : true,
9140     /**
9141      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9142      */
9143     blankText : "Please complete this mandatory field",
9144     
9145      /**
9146      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9147      */
9148     minLength : 0,
9149     /**
9150      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9151      */
9152     maxLength : Number.MAX_VALUE,
9153     /**
9154      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9155      */
9156     minLengthText : "The minimum length for this field is {0}",
9157     /**
9158      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9159      */
9160     maxLengthText : "The maximum length for this field is {0}",
9161   
9162     
9163     /**
9164      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9165      * If available, this function will be called only after the basic validators all return true, and will be passed the
9166      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9167      */
9168     validator : null,
9169     /**
9170      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9171      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9172      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9173      */
9174     regex : null,
9175     /**
9176      * @cfg {String} regexText -- Depricated - use Invalid Text
9177      */
9178     regexText : "",
9179     
9180     /**
9181      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9182      */
9183     invalidText : "",
9184     
9185     
9186     
9187     autocomplete: false,
9188     
9189     
9190     fieldLabel : '',
9191     inputType : 'text',
9192     
9193     name : false,
9194     placeholder: false,
9195     before : false,
9196     after : false,
9197     size : false,
9198     hasFocus : false,
9199     preventMark: false,
9200     isFormField : true,
9201     value : '',
9202     labelWidth : 2,
9203     labelAlign : false,
9204     readOnly : false,
9205     align : false,
9206     formatedValue : false,
9207     forceFeedback : false,
9208     
9209     indicatorpos : 'left',
9210     
9211     labellg : 0,
9212     labelmd : 0,
9213     labelsm : 0,
9214     labelxs : 0,
9215     
9216     capture : '',
9217     accept : '',
9218     
9219     parentLabelAlign : function()
9220     {
9221         var parent = this;
9222         while (parent.parent()) {
9223             parent = parent.parent();
9224             if (typeof(parent.labelAlign) !='undefined') {
9225                 return parent.labelAlign;
9226             }
9227         }
9228         return 'left';
9229         
9230     },
9231     
9232     getAutoCreate : function()
9233     {
9234         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9235         
9236         var id = Roo.id();
9237         
9238         var cfg = {};
9239         
9240         if(this.inputType != 'hidden'){
9241             cfg.cls = 'form-group' //input-group
9242         }
9243         
9244         var input =  {
9245             tag: 'input',
9246             id : id,
9247             type : this.inputType,
9248             value : this.value,
9249             cls : 'form-control',
9250             placeholder : this.placeholder || '',
9251             autocomplete : this.autocomplete || 'new-password'
9252         };
9253         
9254         if(this.capture.length){
9255             input.capture = this.capture;
9256         }
9257         
9258         if(this.accept.length){
9259             input.accept = this.accept + "/*";
9260         }
9261         
9262         if(this.align){
9263             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9264         }
9265         
9266         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9267             input.maxLength = this.maxLength;
9268         }
9269         
9270         if (this.disabled) {
9271             input.disabled=true;
9272         }
9273         
9274         if (this.readOnly) {
9275             input.readonly=true;
9276         }
9277         
9278         if (this.name) {
9279             input.name = this.name;
9280         }
9281         
9282         if (this.size) {
9283             input.cls += ' input-' + this.size;
9284         }
9285         
9286         var settings=this;
9287         ['xs','sm','md','lg'].map(function(size){
9288             if (settings[size]) {
9289                 cfg.cls += ' col-' + size + '-' + settings[size];
9290             }
9291         });
9292         
9293         var inputblock = input;
9294         
9295         var feedback = {
9296             tag: 'span',
9297             cls: 'glyphicon form-control-feedback'
9298         };
9299             
9300         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9301             
9302             inputblock = {
9303                 cls : 'has-feedback',
9304                 cn :  [
9305                     input,
9306                     feedback
9307                 ] 
9308             };  
9309         }
9310         
9311         if (this.before || this.after) {
9312             
9313             inputblock = {
9314                 cls : 'input-group',
9315                 cn :  [] 
9316             };
9317             
9318             if (this.before && typeof(this.before) == 'string') {
9319                 
9320                 inputblock.cn.push({
9321                     tag :'span',
9322                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9323                     html : this.before
9324                 });
9325             }
9326             if (this.before && typeof(this.before) == 'object') {
9327                 this.before = Roo.factory(this.before);
9328                 
9329                 inputblock.cn.push({
9330                     tag :'span',
9331                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9332                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9333                 });
9334             }
9335             
9336             inputblock.cn.push(input);
9337             
9338             if (this.after && typeof(this.after) == 'string') {
9339                 inputblock.cn.push({
9340                     tag :'span',
9341                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9342                     html : this.after
9343                 });
9344             }
9345             if (this.after && typeof(this.after) == 'object') {
9346                 this.after = Roo.factory(this.after);
9347                 
9348                 inputblock.cn.push({
9349                     tag :'span',
9350                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9351                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9352                 });
9353             }
9354             
9355             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9356                 inputblock.cls += ' has-feedback';
9357                 inputblock.cn.push(feedback);
9358             }
9359         };
9360         var indicator = {
9361             tag : 'i',
9362             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9363             tooltip : 'This field is required'
9364         };
9365         if (Roo.bootstrap.version == 4) {
9366             indicator = {
9367                 tag : 'i',
9368                 style : 'display-none'
9369             };
9370         }
9371         if (align ==='left' && this.fieldLabel.length) {
9372             
9373             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9374             
9375             cfg.cn = [
9376                 indicator,
9377                 {
9378                     tag: 'label',
9379                     'for' :  id,
9380                     cls : 'control-label col-form-label',
9381                     html : this.fieldLabel
9382
9383                 },
9384                 {
9385                     cls : "", 
9386                     cn: [
9387                         inputblock
9388                     ]
9389                 }
9390             ];
9391             
9392             var labelCfg = cfg.cn[1];
9393             var contentCfg = cfg.cn[2];
9394             
9395             if(this.indicatorpos == 'right'){
9396                 cfg.cn = [
9397                     {
9398                         tag: 'label',
9399                         'for' :  id,
9400                         cls : 'control-label col-form-label',
9401                         cn : [
9402                             {
9403                                 tag : 'span',
9404                                 html : this.fieldLabel
9405                             },
9406                             indicator
9407                         ]
9408                     },
9409                     {
9410                         cls : "",
9411                         cn: [
9412                             inputblock
9413                         ]
9414                     }
9415
9416                 ];
9417                 
9418                 labelCfg = cfg.cn[0];
9419                 contentCfg = cfg.cn[1];
9420             
9421             }
9422             
9423             if(this.labelWidth > 12){
9424                 labelCfg.style = "width: " + this.labelWidth + 'px';
9425             }
9426             
9427             if(this.labelWidth < 13 && this.labelmd == 0){
9428                 this.labelmd = this.labelWidth;
9429             }
9430             
9431             if(this.labellg > 0){
9432                 labelCfg.cls += ' col-lg-' + this.labellg;
9433                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9434             }
9435             
9436             if(this.labelmd > 0){
9437                 labelCfg.cls += ' col-md-' + this.labelmd;
9438                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9439             }
9440             
9441             if(this.labelsm > 0){
9442                 labelCfg.cls += ' col-sm-' + this.labelsm;
9443                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9444             }
9445             
9446             if(this.labelxs > 0){
9447                 labelCfg.cls += ' col-xs-' + this.labelxs;
9448                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9449             }
9450             
9451             
9452         } else if ( this.fieldLabel.length) {
9453                 
9454             cfg.cn = [
9455                 {
9456                     tag : 'i',
9457                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9458                     tooltip : 'This field is required'
9459                 },
9460                 {
9461                     tag: 'label',
9462                    //cls : 'input-group-addon',
9463                     html : this.fieldLabel
9464
9465                 },
9466
9467                inputblock
9468
9469            ];
9470            
9471            if(this.indicatorpos == 'right'){
9472                 
9473                 cfg.cn = [
9474                     {
9475                         tag: 'label',
9476                        //cls : 'input-group-addon',
9477                         html : this.fieldLabel
9478
9479                     },
9480                     {
9481                         tag : 'i',
9482                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9483                         tooltip : 'This field is required'
9484                     },
9485
9486                    inputblock
9487
9488                ];
9489
9490             }
9491
9492         } else {
9493             
9494             cfg.cn = [
9495
9496                     inputblock
9497
9498             ];
9499                 
9500                 
9501         };
9502         
9503         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9504            cfg.cls += ' navbar-form';
9505         }
9506         
9507         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9508             // on BS4 we do this only if not form 
9509             cfg.cls += ' navbar-form';
9510             cfg.tag = 'li';
9511         }
9512         
9513         return cfg;
9514         
9515     },
9516     /**
9517      * return the real input element.
9518      */
9519     inputEl: function ()
9520     {
9521         return this.el.select('input.form-control',true).first();
9522     },
9523     
9524     tooltipEl : function()
9525     {
9526         return this.inputEl();
9527     },
9528     
9529     indicatorEl : function()
9530     {
9531         if (Roo.bootstrap.version == 4) {
9532             return false; // not enabled in v4 yet.
9533         }
9534         
9535         var indicator = this.el.select('i.roo-required-indicator',true).first();
9536         
9537         if(!indicator){
9538             return false;
9539         }
9540         
9541         return indicator;
9542         
9543     },
9544     
9545     setDisabled : function(v)
9546     {
9547         var i  = this.inputEl().dom;
9548         if (!v) {
9549             i.removeAttribute('disabled');
9550             return;
9551             
9552         }
9553         i.setAttribute('disabled','true');
9554     },
9555     initEvents : function()
9556     {
9557           
9558         this.inputEl().on("keydown" , this.fireKey,  this);
9559         this.inputEl().on("focus", this.onFocus,  this);
9560         this.inputEl().on("blur", this.onBlur,  this);
9561         
9562         this.inputEl().relayEvent('keyup', this);
9563         
9564         this.indicator = this.indicatorEl();
9565         
9566         if(this.indicator){
9567             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9568         }
9569  
9570         // reference to original value for reset
9571         this.originalValue = this.getValue();
9572         //Roo.form.TextField.superclass.initEvents.call(this);
9573         if(this.validationEvent == 'keyup'){
9574             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9575             this.inputEl().on('keyup', this.filterValidation, this);
9576         }
9577         else if(this.validationEvent !== false){
9578             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9579         }
9580         
9581         if(this.selectOnFocus){
9582             this.on("focus", this.preFocus, this);
9583             
9584         }
9585         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9586             this.inputEl().on("keypress", this.filterKeys, this);
9587         } else {
9588             this.inputEl().relayEvent('keypress', this);
9589         }
9590        /* if(this.grow){
9591             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9592             this.el.on("click", this.autoSize,  this);
9593         }
9594         */
9595         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9596             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9597         }
9598         
9599         if (typeof(this.before) == 'object') {
9600             this.before.render(this.el.select('.roo-input-before',true).first());
9601         }
9602         if (typeof(this.after) == 'object') {
9603             this.after.render(this.el.select('.roo-input-after',true).first());
9604         }
9605         
9606         this.inputEl().on('change', this.onChange, this);
9607         
9608     },
9609     filterValidation : function(e){
9610         if(!e.isNavKeyPress()){
9611             this.validationTask.delay(this.validationDelay);
9612         }
9613     },
9614      /**
9615      * Validates the field value
9616      * @return {Boolean} True if the value is valid, else false
9617      */
9618     validate : function(){
9619         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9620         if(this.disabled || this.validateValue(this.getRawValue())){
9621             this.markValid();
9622             return true;
9623         }
9624         
9625         this.markInvalid();
9626         return false;
9627     },
9628     
9629     
9630     /**
9631      * Validates a value according to the field's validation rules and marks the field as invalid
9632      * if the validation fails
9633      * @param {Mixed} value The value to validate
9634      * @return {Boolean} True if the value is valid, else false
9635      */
9636     validateValue : function(value)
9637     {
9638         if(this.getVisibilityEl().hasClass('hidden')){
9639             return true;
9640         }
9641         
9642         if(value.length < 1)  { // if it's blank
9643             if(this.allowBlank){
9644                 return true;
9645             }
9646             return false;
9647         }
9648         
9649         if(value.length < this.minLength){
9650             return false;
9651         }
9652         if(value.length > this.maxLength){
9653             return false;
9654         }
9655         if(this.vtype){
9656             var vt = Roo.form.VTypes;
9657             if(!vt[this.vtype](value, this)){
9658                 return false;
9659             }
9660         }
9661         if(typeof this.validator == "function"){
9662             var msg = this.validator(value);
9663             if(msg !== true){
9664                 return false;
9665             }
9666             if (typeof(msg) == 'string') {
9667                 this.invalidText = msg;
9668             }
9669         }
9670         
9671         if(this.regex && !this.regex.test(value)){
9672             return false;
9673         }
9674         
9675         return true;
9676     },
9677     
9678      // private
9679     fireKey : function(e){
9680         //Roo.log('field ' + e.getKey());
9681         if(e.isNavKeyPress()){
9682             this.fireEvent("specialkey", this, e);
9683         }
9684     },
9685     focus : function (selectText){
9686         if(this.rendered){
9687             this.inputEl().focus();
9688             if(selectText === true){
9689                 this.inputEl().dom.select();
9690             }
9691         }
9692         return this;
9693     } ,
9694     
9695     onFocus : function(){
9696         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9697            // this.el.addClass(this.focusClass);
9698         }
9699         if(!this.hasFocus){
9700             this.hasFocus = true;
9701             this.startValue = this.getValue();
9702             this.fireEvent("focus", this);
9703         }
9704     },
9705     
9706     beforeBlur : Roo.emptyFn,
9707
9708     
9709     // private
9710     onBlur : function(){
9711         this.beforeBlur();
9712         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9713             //this.el.removeClass(this.focusClass);
9714         }
9715         this.hasFocus = false;
9716         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9717             this.validate();
9718         }
9719         var v = this.getValue();
9720         if(String(v) !== String(this.startValue)){
9721             this.fireEvent('change', this, v, this.startValue);
9722         }
9723         this.fireEvent("blur", this);
9724     },
9725     
9726     onChange : function(e)
9727     {
9728         var v = this.getValue();
9729         if(String(v) !== String(this.startValue)){
9730             this.fireEvent('change', this, v, this.startValue);
9731         }
9732         
9733     },
9734     
9735     /**
9736      * Resets the current field value to the originally loaded value and clears any validation messages
9737      */
9738     reset : function(){
9739         this.setValue(this.originalValue);
9740         this.validate();
9741     },
9742      /**
9743      * Returns the name of the field
9744      * @return {Mixed} name The name field
9745      */
9746     getName: function(){
9747         return this.name;
9748     },
9749      /**
9750      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9751      * @return {Mixed} value The field value
9752      */
9753     getValue : function(){
9754         
9755         var v = this.inputEl().getValue();
9756         
9757         return v;
9758     },
9759     /**
9760      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9761      * @return {Mixed} value The field value
9762      */
9763     getRawValue : function(){
9764         var v = this.inputEl().getValue();
9765         
9766         return v;
9767     },
9768     
9769     /**
9770      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9771      * @param {Mixed} value The value to set
9772      */
9773     setRawValue : function(v){
9774         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9775     },
9776     
9777     selectText : function(start, end){
9778         var v = this.getRawValue();
9779         if(v.length > 0){
9780             start = start === undefined ? 0 : start;
9781             end = end === undefined ? v.length : end;
9782             var d = this.inputEl().dom;
9783             if(d.setSelectionRange){
9784                 d.setSelectionRange(start, end);
9785             }else if(d.createTextRange){
9786                 var range = d.createTextRange();
9787                 range.moveStart("character", start);
9788                 range.moveEnd("character", v.length-end);
9789                 range.select();
9790             }
9791         }
9792     },
9793     
9794     /**
9795      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9796      * @param {Mixed} value The value to set
9797      */
9798     setValue : function(v){
9799         this.value = v;
9800         if(this.rendered){
9801             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9802             this.validate();
9803         }
9804     },
9805     
9806     /*
9807     processValue : function(value){
9808         if(this.stripCharsRe){
9809             var newValue = value.replace(this.stripCharsRe, '');
9810             if(newValue !== value){
9811                 this.setRawValue(newValue);
9812                 return newValue;
9813             }
9814         }
9815         return value;
9816     },
9817   */
9818     preFocus : function(){
9819         
9820         if(this.selectOnFocus){
9821             this.inputEl().dom.select();
9822         }
9823     },
9824     filterKeys : function(e){
9825         var k = e.getKey();
9826         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9827             return;
9828         }
9829         var c = e.getCharCode(), cc = String.fromCharCode(c);
9830         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9831             return;
9832         }
9833         if(!this.maskRe.test(cc)){
9834             e.stopEvent();
9835         }
9836     },
9837      /**
9838      * Clear any invalid styles/messages for this field
9839      */
9840     clearInvalid : function(){
9841         
9842         if(!this.el || this.preventMark){ // not rendered
9843             return;
9844         }
9845         
9846         
9847         this.el.removeClass([this.invalidClass, 'is-invalid']);
9848         
9849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9850             
9851             var feedback = this.el.select('.form-control-feedback', true).first();
9852             
9853             if(feedback){
9854                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9855             }
9856             
9857         }
9858         
9859         if(this.indicator){
9860             this.indicator.removeClass('visible');
9861             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9862         }
9863         
9864         this.fireEvent('valid', this);
9865     },
9866     
9867      /**
9868      * Mark this field as valid
9869      */
9870     markValid : function()
9871     {
9872         if(!this.el  || this.preventMark){ // not rendered...
9873             return;
9874         }
9875         
9876         this.el.removeClass([this.invalidClass, this.validClass]);
9877         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9878
9879         var feedback = this.el.select('.form-control-feedback', true).first();
9880             
9881         if(feedback){
9882             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9883         }
9884         
9885         if(this.indicator){
9886             this.indicator.removeClass('visible');
9887             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9888         }
9889         
9890         if(this.disabled){
9891             return;
9892         }
9893         
9894         if(this.allowBlank && !this.getRawValue().length){
9895             return;
9896         }
9897         if (Roo.bootstrap.version == 3) {
9898             this.el.addClass(this.validClass);
9899         } else {
9900             this.inputEl().addClass('is-valid');
9901         }
9902
9903         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9904             
9905             var feedback = this.el.select('.form-control-feedback', true).first();
9906             
9907             if(feedback){
9908                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9909                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9910             }
9911             
9912         }
9913         
9914         this.fireEvent('valid', this);
9915     },
9916     
9917      /**
9918      * Mark this field as invalid
9919      * @param {String} msg The validation message
9920      */
9921     markInvalid : function(msg)
9922     {
9923         if(!this.el  || this.preventMark){ // not rendered
9924             return;
9925         }
9926         
9927         this.el.removeClass([this.invalidClass, this.validClass]);
9928         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9929         
9930         var feedback = this.el.select('.form-control-feedback', true).first();
9931             
9932         if(feedback){
9933             this.el.select('.form-control-feedback', true).first().removeClass(
9934                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9935         }
9936
9937         if(this.disabled){
9938             return;
9939         }
9940         
9941         if(this.allowBlank && !this.getRawValue().length){
9942             return;
9943         }
9944         
9945         if(this.indicator){
9946             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9947             this.indicator.addClass('visible');
9948         }
9949         if (Roo.bootstrap.version == 3) {
9950             this.el.addClass(this.invalidClass);
9951         } else {
9952             this.inputEl().addClass('is-invalid');
9953         }
9954         
9955         
9956         
9957         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9958             
9959             var feedback = this.el.select('.form-control-feedback', true).first();
9960             
9961             if(feedback){
9962                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9963                 
9964                 if(this.getValue().length || this.forceFeedback){
9965                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9966                 }
9967                 
9968             }
9969             
9970         }
9971         
9972         this.fireEvent('invalid', this, msg);
9973     },
9974     // private
9975     SafariOnKeyDown : function(event)
9976     {
9977         // this is a workaround for a password hang bug on chrome/ webkit.
9978         if (this.inputEl().dom.type != 'password') {
9979             return;
9980         }
9981         
9982         var isSelectAll = false;
9983         
9984         if(this.inputEl().dom.selectionEnd > 0){
9985             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9986         }
9987         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9988             event.preventDefault();
9989             this.setValue('');
9990             return;
9991         }
9992         
9993         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9994             
9995             event.preventDefault();
9996             // this is very hacky as keydown always get's upper case.
9997             //
9998             var cc = String.fromCharCode(event.getCharCode());
9999             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10000             
10001         }
10002     },
10003     adjustWidth : function(tag, w){
10004         tag = tag.toLowerCase();
10005         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10006             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10007                 if(tag == 'input'){
10008                     return w + 2;
10009                 }
10010                 if(tag == 'textarea'){
10011                     return w-2;
10012                 }
10013             }else if(Roo.isOpera){
10014                 if(tag == 'input'){
10015                     return w + 2;
10016                 }
10017                 if(tag == 'textarea'){
10018                     return w-2;
10019                 }
10020             }
10021         }
10022         return w;
10023     },
10024     
10025     setFieldLabel : function(v)
10026     {
10027         if(!this.rendered){
10028             return;
10029         }
10030         
10031         if(this.indicatorEl()){
10032             var ar = this.el.select('label > span',true);
10033             
10034             if (ar.elements.length) {
10035                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10036                 this.fieldLabel = v;
10037                 return;
10038             }
10039             
10040             var br = this.el.select('label',true);
10041             
10042             if(br.elements.length) {
10043                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10044                 this.fieldLabel = v;
10045                 return;
10046             }
10047             
10048             Roo.log('Cannot Found any of label > span || label in input');
10049             return;
10050         }
10051         
10052         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10053         this.fieldLabel = v;
10054         
10055         
10056     }
10057 });
10058
10059  
10060 /*
10061  * - LGPL
10062  *
10063  * Input
10064  * 
10065  */
10066
10067 /**
10068  * @class Roo.bootstrap.TextArea
10069  * @extends Roo.bootstrap.Input
10070  * Bootstrap TextArea class
10071  * @cfg {Number} cols Specifies the visible width of a text area
10072  * @cfg {Number} rows Specifies the visible number of lines in a text area
10073  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10074  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10075  * @cfg {string} html text
10076  * 
10077  * @constructor
10078  * Create a new TextArea
10079  * @param {Object} config The config object
10080  */
10081
10082 Roo.bootstrap.TextArea = function(config){
10083     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10084    
10085 };
10086
10087 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10088      
10089     cols : false,
10090     rows : 5,
10091     readOnly : false,
10092     warp : 'soft',
10093     resize : false,
10094     value: false,
10095     html: false,
10096     
10097     getAutoCreate : function(){
10098         
10099         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10100         
10101         var id = Roo.id();
10102         
10103         var cfg = {};
10104         
10105         if(this.inputType != 'hidden'){
10106             cfg.cls = 'form-group' //input-group
10107         }
10108         
10109         var input =  {
10110             tag: 'textarea',
10111             id : id,
10112             warp : this.warp,
10113             rows : this.rows,
10114             value : this.value || '',
10115             html: this.html || '',
10116             cls : 'form-control',
10117             placeholder : this.placeholder || '' 
10118             
10119         };
10120         
10121         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10122             input.maxLength = this.maxLength;
10123         }
10124         
10125         if(this.resize){
10126             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10127         }
10128         
10129         if(this.cols){
10130             input.cols = this.cols;
10131         }
10132         
10133         if (this.readOnly) {
10134             input.readonly = true;
10135         }
10136         
10137         if (this.name) {
10138             input.name = this.name;
10139         }
10140         
10141         if (this.size) {
10142             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10143         }
10144         
10145         var settings=this;
10146         ['xs','sm','md','lg'].map(function(size){
10147             if (settings[size]) {
10148                 cfg.cls += ' col-' + size + '-' + settings[size];
10149             }
10150         });
10151         
10152         var inputblock = input;
10153         
10154         if(this.hasFeedback && !this.allowBlank){
10155             
10156             var feedback = {
10157                 tag: 'span',
10158                 cls: 'glyphicon form-control-feedback'
10159             };
10160
10161             inputblock = {
10162                 cls : 'has-feedback',
10163                 cn :  [
10164                     input,
10165                     feedback
10166                 ] 
10167             };  
10168         }
10169         
10170         
10171         if (this.before || this.after) {
10172             
10173             inputblock = {
10174                 cls : 'input-group',
10175                 cn :  [] 
10176             };
10177             if (this.before) {
10178                 inputblock.cn.push({
10179                     tag :'span',
10180                     cls : 'input-group-addon',
10181                     html : this.before
10182                 });
10183             }
10184             
10185             inputblock.cn.push(input);
10186             
10187             if(this.hasFeedback && !this.allowBlank){
10188                 inputblock.cls += ' has-feedback';
10189                 inputblock.cn.push(feedback);
10190             }
10191             
10192             if (this.after) {
10193                 inputblock.cn.push({
10194                     tag :'span',
10195                     cls : 'input-group-addon',
10196                     html : this.after
10197                 });
10198             }
10199             
10200         }
10201         
10202         if (align ==='left' && this.fieldLabel.length) {
10203             cfg.cn = [
10204                 {
10205                     tag: 'label',
10206                     'for' :  id,
10207                     cls : 'control-label',
10208                     html : this.fieldLabel
10209                 },
10210                 {
10211                     cls : "",
10212                     cn: [
10213                         inputblock
10214                     ]
10215                 }
10216
10217             ];
10218             
10219             if(this.labelWidth > 12){
10220                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10221             }
10222
10223             if(this.labelWidth < 13 && this.labelmd == 0){
10224                 this.labelmd = this.labelWidth;
10225             }
10226
10227             if(this.labellg > 0){
10228                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10229                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10230             }
10231
10232             if(this.labelmd > 0){
10233                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10234                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10235             }
10236
10237             if(this.labelsm > 0){
10238                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10239                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10240             }
10241
10242             if(this.labelxs > 0){
10243                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10244                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10245             }
10246             
10247         } else if ( this.fieldLabel.length) {
10248             cfg.cn = [
10249
10250                {
10251                    tag: 'label',
10252                    //cls : 'input-group-addon',
10253                    html : this.fieldLabel
10254
10255                },
10256
10257                inputblock
10258
10259            ];
10260
10261         } else {
10262
10263             cfg.cn = [
10264
10265                 inputblock
10266
10267             ];
10268                 
10269         }
10270         
10271         if (this.disabled) {
10272             input.disabled=true;
10273         }
10274         
10275         return cfg;
10276         
10277     },
10278     /**
10279      * return the real textarea element.
10280      */
10281     inputEl: function ()
10282     {
10283         return this.el.select('textarea.form-control',true).first();
10284     },
10285     
10286     /**
10287      * Clear any invalid styles/messages for this field
10288      */
10289     clearInvalid : function()
10290     {
10291         
10292         if(!this.el || this.preventMark){ // not rendered
10293             return;
10294         }
10295         
10296         var label = this.el.select('label', true).first();
10297         var icon = this.el.select('i.fa-star', true).first();
10298         
10299         if(label && icon){
10300             icon.remove();
10301         }
10302         this.el.removeClass( this.validClass);
10303         this.inputEl().removeClass('is-invalid');
10304          
10305         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10306             
10307             var feedback = this.el.select('.form-control-feedback', true).first();
10308             
10309             if(feedback){
10310                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10311             }
10312             
10313         }
10314         
10315         this.fireEvent('valid', this);
10316     },
10317     
10318      /**
10319      * Mark this field as valid
10320      */
10321     markValid : function()
10322     {
10323         if(!this.el  || this.preventMark){ // not rendered
10324             return;
10325         }
10326         
10327         this.el.removeClass([this.invalidClass, this.validClass]);
10328         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10329         
10330         var feedback = this.el.select('.form-control-feedback', true).first();
10331             
10332         if(feedback){
10333             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10334         }
10335
10336         if(this.disabled || this.allowBlank){
10337             return;
10338         }
10339         
10340         var label = this.el.select('label', true).first();
10341         var icon = this.el.select('i.fa-star', true).first();
10342         
10343         if(label && icon){
10344             icon.remove();
10345         }
10346         if (Roo.bootstrap.version == 3) {
10347             this.el.addClass(this.validClass);
10348         } else {
10349             this.inputEl().addClass('is-valid');
10350         }
10351         
10352         
10353         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10354             
10355             var feedback = this.el.select('.form-control-feedback', true).first();
10356             
10357             if(feedback){
10358                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10359                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10360             }
10361             
10362         }
10363         
10364         this.fireEvent('valid', this);
10365     },
10366     
10367      /**
10368      * Mark this field as invalid
10369      * @param {String} msg The validation message
10370      */
10371     markInvalid : function(msg)
10372     {
10373         if(!this.el  || this.preventMark){ // not rendered
10374             return;
10375         }
10376         
10377         this.el.removeClass([this.invalidClass, this.validClass]);
10378         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10379         
10380         var feedback = this.el.select('.form-control-feedback', true).first();
10381             
10382         if(feedback){
10383             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10384         }
10385
10386         if(this.disabled || this.allowBlank){
10387             return;
10388         }
10389         
10390         var label = this.el.select('label', true).first();
10391         var icon = this.el.select('i.fa-star', true).first();
10392         
10393         if(!this.getValue().length && label && !icon){
10394             this.el.createChild({
10395                 tag : 'i',
10396                 cls : 'text-danger fa fa-lg fa-star',
10397                 tooltip : 'This field is required',
10398                 style : 'margin-right:5px;'
10399             }, label, true);
10400         }
10401         
10402         if (Roo.bootstrap.version == 3) {
10403             this.el.addClass(this.invalidClass);
10404         } else {
10405             this.inputEl().addClass('is-invalid');
10406         }
10407         
10408         // fixme ... this may be depricated need to test..
10409         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10410             
10411             var feedback = this.el.select('.form-control-feedback', true).first();
10412             
10413             if(feedback){
10414                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10415                 
10416                 if(this.getValue().length || this.forceFeedback){
10417                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10418                 }
10419                 
10420             }
10421             
10422         }
10423         
10424         this.fireEvent('invalid', this, msg);
10425     }
10426 });
10427
10428  
10429 /*
10430  * - LGPL
10431  *
10432  * trigger field - base class for combo..
10433  * 
10434  */
10435  
10436 /**
10437  * @class Roo.bootstrap.TriggerField
10438  * @extends Roo.bootstrap.Input
10439  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10440  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10441  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10442  * for which you can provide a custom implementation.  For example:
10443  * <pre><code>
10444 var trigger = new Roo.bootstrap.TriggerField();
10445 trigger.onTriggerClick = myTriggerFn;
10446 trigger.applyTo('my-field');
10447 </code></pre>
10448  *
10449  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10450  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10451  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10452  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10453  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10454
10455  * @constructor
10456  * Create a new TriggerField.
10457  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10458  * to the base TextField)
10459  */
10460 Roo.bootstrap.TriggerField = function(config){
10461     this.mimicing = false;
10462     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10463 };
10464
10465 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10466     /**
10467      * @cfg {String} triggerClass A CSS class to apply to the trigger
10468      */
10469      /**
10470      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10471      */
10472     hideTrigger:false,
10473
10474     /**
10475      * @cfg {Boolean} removable (true|false) special filter default false
10476      */
10477     removable : false,
10478     
10479     /** @cfg {Boolean} grow @hide */
10480     /** @cfg {Number} growMin @hide */
10481     /** @cfg {Number} growMax @hide */
10482
10483     /**
10484      * @hide 
10485      * @method
10486      */
10487     autoSize: Roo.emptyFn,
10488     // private
10489     monitorTab : true,
10490     // private
10491     deferHeight : true,
10492
10493     
10494     actionMode : 'wrap',
10495     
10496     caret : false,
10497     
10498     
10499     getAutoCreate : function(){
10500        
10501         var align = this.labelAlign || this.parentLabelAlign();
10502         
10503         var id = Roo.id();
10504         
10505         var cfg = {
10506             cls: 'form-group' //input-group
10507         };
10508         
10509         
10510         var input =  {
10511             tag: 'input',
10512             id : id,
10513             type : this.inputType,
10514             cls : 'form-control',
10515             autocomplete: 'new-password',
10516             placeholder : this.placeholder || '' 
10517             
10518         };
10519         if (this.name) {
10520             input.name = this.name;
10521         }
10522         if (this.size) {
10523             input.cls += ' input-' + this.size;
10524         }
10525         
10526         if (this.disabled) {
10527             input.disabled=true;
10528         }
10529         
10530         var inputblock = input;
10531         
10532         if(this.hasFeedback && !this.allowBlank){
10533             
10534             var feedback = {
10535                 tag: 'span',
10536                 cls: 'glyphicon form-control-feedback'
10537             };
10538             
10539             if(this.removable && !this.editable && !this.tickable){
10540                 inputblock = {
10541                     cls : 'has-feedback',
10542                     cn :  [
10543                         inputblock,
10544                         {
10545                             tag: 'button',
10546                             html : 'x',
10547                             cls : 'roo-combo-removable-btn close'
10548                         },
10549                         feedback
10550                     ] 
10551                 };
10552             } else {
10553                 inputblock = {
10554                     cls : 'has-feedback',
10555                     cn :  [
10556                         inputblock,
10557                         feedback
10558                     ] 
10559                 };
10560             }
10561
10562         } else {
10563             if(this.removable && !this.editable && !this.tickable){
10564                 inputblock = {
10565                     cls : 'roo-removable',
10566                     cn :  [
10567                         inputblock,
10568                         {
10569                             tag: 'button',
10570                             html : 'x',
10571                             cls : 'roo-combo-removable-btn close'
10572                         }
10573                     ] 
10574                 };
10575             }
10576         }
10577         
10578         if (this.before || this.after) {
10579             
10580             inputblock = {
10581                 cls : 'input-group',
10582                 cn :  [] 
10583             };
10584             if (this.before) {
10585                 inputblock.cn.push({
10586                     tag :'span',
10587                     cls : 'input-group-addon input-group-prepend input-group-text',
10588                     html : this.before
10589                 });
10590             }
10591             
10592             inputblock.cn.push(input);
10593             
10594             if(this.hasFeedback && !this.allowBlank){
10595                 inputblock.cls += ' has-feedback';
10596                 inputblock.cn.push(feedback);
10597             }
10598             
10599             if (this.after) {
10600                 inputblock.cn.push({
10601                     tag :'span',
10602                     cls : 'input-group-addon input-group-append input-group-text',
10603                     html : this.after
10604                 });
10605             }
10606             
10607         };
10608         
10609       
10610         
10611         var ibwrap = inputblock;
10612         
10613         if(this.multiple){
10614             ibwrap = {
10615                 tag: 'ul',
10616                 cls: 'roo-select2-choices',
10617                 cn:[
10618                     {
10619                         tag: 'li',
10620                         cls: 'roo-select2-search-field',
10621                         cn: [
10622
10623                             inputblock
10624                         ]
10625                     }
10626                 ]
10627             };
10628                 
10629         }
10630         
10631         var combobox = {
10632             cls: 'roo-select2-container input-group',
10633             cn: [
10634                  {
10635                     tag: 'input',
10636                     type : 'hidden',
10637                     cls: 'form-hidden-field'
10638                 },
10639                 ibwrap
10640             ]
10641         };
10642         
10643         if(!this.multiple && this.showToggleBtn){
10644             
10645             var caret = {
10646                         tag: 'span',
10647                         cls: 'caret'
10648              };
10649             if (this.caret != false) {
10650                 caret = {
10651                      tag: 'i',
10652                      cls: 'fa fa-' + this.caret
10653                 };
10654                 
10655             }
10656             
10657             combobox.cn.push({
10658                 tag :'span',
10659                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10660                 cn : [
10661                     caret,
10662                     {
10663                         tag: 'span',
10664                         cls: 'combobox-clear',
10665                         cn  : [
10666                             {
10667                                 tag : 'i',
10668                                 cls: 'icon-remove'
10669                             }
10670                         ]
10671                     }
10672                 ]
10673
10674             })
10675         }
10676         
10677         if(this.multiple){
10678             combobox.cls += ' roo-select2-container-multi';
10679         }
10680          var indicator = {
10681             tag : 'i',
10682             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10683             tooltip : 'This field is required'
10684         };
10685         if (Roo.bootstrap.version == 4) {
10686             indicator = {
10687                 tag : 'i',
10688                 style : 'display:none'
10689             };
10690         }
10691         
10692         
10693         if (align ==='left' && this.fieldLabel.length) {
10694             
10695             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10696
10697             cfg.cn = [
10698                 indicator,
10699                 {
10700                     tag: 'label',
10701                     'for' :  id,
10702                     cls : 'control-label',
10703                     html : this.fieldLabel
10704
10705                 },
10706                 {
10707                     cls : "", 
10708                     cn: [
10709                         combobox
10710                     ]
10711                 }
10712
10713             ];
10714             
10715             var labelCfg = cfg.cn[1];
10716             var contentCfg = cfg.cn[2];
10717             
10718             if(this.indicatorpos == 'right'){
10719                 cfg.cn = [
10720                     {
10721                         tag: 'label',
10722                         'for' :  id,
10723                         cls : 'control-label',
10724                         cn : [
10725                             {
10726                                 tag : 'span',
10727                                 html : this.fieldLabel
10728                             },
10729                             indicator
10730                         ]
10731                     },
10732                     {
10733                         cls : "", 
10734                         cn: [
10735                             combobox
10736                         ]
10737                     }
10738
10739                 ];
10740                 
10741                 labelCfg = cfg.cn[0];
10742                 contentCfg = cfg.cn[1];
10743             }
10744             
10745             if(this.labelWidth > 12){
10746                 labelCfg.style = "width: " + this.labelWidth + 'px';
10747             }
10748             
10749             if(this.labelWidth < 13 && this.labelmd == 0){
10750                 this.labelmd = this.labelWidth;
10751             }
10752             
10753             if(this.labellg > 0){
10754                 labelCfg.cls += ' col-lg-' + this.labellg;
10755                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10756             }
10757             
10758             if(this.labelmd > 0){
10759                 labelCfg.cls += ' col-md-' + this.labelmd;
10760                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10761             }
10762             
10763             if(this.labelsm > 0){
10764                 labelCfg.cls += ' col-sm-' + this.labelsm;
10765                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10766             }
10767             
10768             if(this.labelxs > 0){
10769                 labelCfg.cls += ' col-xs-' + this.labelxs;
10770                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10771             }
10772             
10773         } else if ( this.fieldLabel.length) {
10774 //                Roo.log(" label");
10775             cfg.cn = [
10776                 indicator,
10777                {
10778                    tag: 'label',
10779                    //cls : 'input-group-addon',
10780                    html : this.fieldLabel
10781
10782                },
10783
10784                combobox
10785
10786             ];
10787             
10788             if(this.indicatorpos == 'right'){
10789                 
10790                 cfg.cn = [
10791                     {
10792                        tag: 'label',
10793                        cn : [
10794                            {
10795                                tag : 'span',
10796                                html : this.fieldLabel
10797                            },
10798                            indicator
10799                        ]
10800
10801                     },
10802                     combobox
10803
10804                 ];
10805
10806             }
10807
10808         } else {
10809             
10810 //                Roo.log(" no label && no align");
10811                 cfg = combobox
10812                      
10813                 
10814         }
10815         
10816         var settings=this;
10817         ['xs','sm','md','lg'].map(function(size){
10818             if (settings[size]) {
10819                 cfg.cls += ' col-' + size + '-' + settings[size];
10820             }
10821         });
10822         
10823         return cfg;
10824         
10825     },
10826     
10827     
10828     
10829     // private
10830     onResize : function(w, h){
10831 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10832 //        if(typeof w == 'number'){
10833 //            var x = w - this.trigger.getWidth();
10834 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10835 //            this.trigger.setStyle('left', x+'px');
10836 //        }
10837     },
10838
10839     // private
10840     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10841
10842     // private
10843     getResizeEl : function(){
10844         return this.inputEl();
10845     },
10846
10847     // private
10848     getPositionEl : function(){
10849         return this.inputEl();
10850     },
10851
10852     // private
10853     alignErrorIcon : function(){
10854         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10855     },
10856
10857     // private
10858     initEvents : function(){
10859         
10860         this.createList();
10861         
10862         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10863         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10864         if(!this.multiple && this.showToggleBtn){
10865             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10866             if(this.hideTrigger){
10867                 this.trigger.setDisplayed(false);
10868             }
10869             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10870         }
10871         
10872         if(this.multiple){
10873             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10874         }
10875         
10876         if(this.removable && !this.editable && !this.tickable){
10877             var close = this.closeTriggerEl();
10878             
10879             if(close){
10880                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10881                 close.on('click', this.removeBtnClick, this, close);
10882             }
10883         }
10884         
10885         //this.trigger.addClassOnOver('x-form-trigger-over');
10886         //this.trigger.addClassOnClick('x-form-trigger-click');
10887         
10888         //if(!this.width){
10889         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10890         //}
10891     },
10892     
10893     closeTriggerEl : function()
10894     {
10895         var close = this.el.select('.roo-combo-removable-btn', true).first();
10896         return close ? close : false;
10897     },
10898     
10899     removeBtnClick : function(e, h, el)
10900     {
10901         e.preventDefault();
10902         
10903         if(this.fireEvent("remove", this) !== false){
10904             this.reset();
10905             this.fireEvent("afterremove", this)
10906         }
10907     },
10908     
10909     createList : function()
10910     {
10911         this.list = Roo.get(document.body).createChild({
10912             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10913             cls: 'typeahead typeahead-long dropdown-menu',
10914             style: 'display:none'
10915         });
10916         
10917         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10918         
10919     },
10920
10921     // private
10922     initTrigger : function(){
10923        
10924     },
10925
10926     // private
10927     onDestroy : function(){
10928         if(this.trigger){
10929             this.trigger.removeAllListeners();
10930           //  this.trigger.remove();
10931         }
10932         //if(this.wrap){
10933         //    this.wrap.remove();
10934         //}
10935         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10936     },
10937
10938     // private
10939     onFocus : function(){
10940         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10941         /*
10942         if(!this.mimicing){
10943             this.wrap.addClass('x-trigger-wrap-focus');
10944             this.mimicing = true;
10945             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10946             if(this.monitorTab){
10947                 this.el.on("keydown", this.checkTab, this);
10948             }
10949         }
10950         */
10951     },
10952
10953     // private
10954     checkTab : function(e){
10955         if(e.getKey() == e.TAB){
10956             this.triggerBlur();
10957         }
10958     },
10959
10960     // private
10961     onBlur : function(){
10962         // do nothing
10963     },
10964
10965     // private
10966     mimicBlur : function(e, t){
10967         /*
10968         if(!this.wrap.contains(t) && this.validateBlur()){
10969             this.triggerBlur();
10970         }
10971         */
10972     },
10973
10974     // private
10975     triggerBlur : function(){
10976         this.mimicing = false;
10977         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10978         if(this.monitorTab){
10979             this.el.un("keydown", this.checkTab, this);
10980         }
10981         //this.wrap.removeClass('x-trigger-wrap-focus');
10982         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10983     },
10984
10985     // private
10986     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10987     validateBlur : function(e, t){
10988         return true;
10989     },
10990
10991     // private
10992     onDisable : function(){
10993         this.inputEl().dom.disabled = true;
10994         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10995         //if(this.wrap){
10996         //    this.wrap.addClass('x-item-disabled');
10997         //}
10998     },
10999
11000     // private
11001     onEnable : function(){
11002         this.inputEl().dom.disabled = false;
11003         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11004         //if(this.wrap){
11005         //    this.el.removeClass('x-item-disabled');
11006         //}
11007     },
11008
11009     // private
11010     onShow : function(){
11011         var ae = this.getActionEl();
11012         
11013         if(ae){
11014             ae.dom.style.display = '';
11015             ae.dom.style.visibility = 'visible';
11016         }
11017     },
11018
11019     // private
11020     
11021     onHide : function(){
11022         var ae = this.getActionEl();
11023         ae.dom.style.display = 'none';
11024     },
11025
11026     /**
11027      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11028      * by an implementing function.
11029      * @method
11030      * @param {EventObject} e
11031      */
11032     onTriggerClick : Roo.emptyFn
11033 });
11034  /*
11035  * Based on:
11036  * Ext JS Library 1.1.1
11037  * Copyright(c) 2006-2007, Ext JS, LLC.
11038  *
11039  * Originally Released Under LGPL - original licence link has changed is not relivant.
11040  *
11041  * Fork - LGPL
11042  * <script type="text/javascript">
11043  */
11044
11045
11046 /**
11047  * @class Roo.data.SortTypes
11048  * @singleton
11049  * Defines the default sorting (casting?) comparison functions used when sorting data.
11050  */
11051 Roo.data.SortTypes = {
11052     /**
11053      * Default sort that does nothing
11054      * @param {Mixed} s The value being converted
11055      * @return {Mixed} The comparison value
11056      */
11057     none : function(s){
11058         return s;
11059     },
11060     
11061     /**
11062      * The regular expression used to strip tags
11063      * @type {RegExp}
11064      * @property
11065      */
11066     stripTagsRE : /<\/?[^>]+>/gi,
11067     
11068     /**
11069      * Strips all HTML tags to sort on text only
11070      * @param {Mixed} s The value being converted
11071      * @return {String} The comparison value
11072      */
11073     asText : function(s){
11074         return String(s).replace(this.stripTagsRE, "");
11075     },
11076     
11077     /**
11078      * Strips all HTML tags to sort on text only - Case insensitive
11079      * @param {Mixed} s The value being converted
11080      * @return {String} The comparison value
11081      */
11082     asUCText : function(s){
11083         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11084     },
11085     
11086     /**
11087      * Case insensitive string
11088      * @param {Mixed} s The value being converted
11089      * @return {String} The comparison value
11090      */
11091     asUCString : function(s) {
11092         return String(s).toUpperCase();
11093     },
11094     
11095     /**
11096      * Date sorting
11097      * @param {Mixed} s The value being converted
11098      * @return {Number} The comparison value
11099      */
11100     asDate : function(s) {
11101         if(!s){
11102             return 0;
11103         }
11104         if(s instanceof Date){
11105             return s.getTime();
11106         }
11107         return Date.parse(String(s));
11108     },
11109     
11110     /**
11111      * Float sorting
11112      * @param {Mixed} s The value being converted
11113      * @return {Float} The comparison value
11114      */
11115     asFloat : function(s) {
11116         var val = parseFloat(String(s).replace(/,/g, ""));
11117         if(isNaN(val)) {
11118             val = 0;
11119         }
11120         return val;
11121     },
11122     
11123     /**
11124      * Integer sorting
11125      * @param {Mixed} s The value being converted
11126      * @return {Number} The comparison value
11127      */
11128     asInt : function(s) {
11129         var val = parseInt(String(s).replace(/,/g, ""));
11130         if(isNaN(val)) {
11131             val = 0;
11132         }
11133         return val;
11134     }
11135 };/*
11136  * Based on:
11137  * Ext JS Library 1.1.1
11138  * Copyright(c) 2006-2007, Ext JS, LLC.
11139  *
11140  * Originally Released Under LGPL - original licence link has changed is not relivant.
11141  *
11142  * Fork - LGPL
11143  * <script type="text/javascript">
11144  */
11145
11146 /**
11147 * @class Roo.data.Record
11148  * Instances of this class encapsulate both record <em>definition</em> information, and record
11149  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11150  * to access Records cached in an {@link Roo.data.Store} object.<br>
11151  * <p>
11152  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11153  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11154  * objects.<br>
11155  * <p>
11156  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11157  * @constructor
11158  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11159  * {@link #create}. The parameters are the same.
11160  * @param {Array} data An associative Array of data values keyed by the field name.
11161  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11162  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11163  * not specified an integer id is generated.
11164  */
11165 Roo.data.Record = function(data, id){
11166     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11167     this.data = data;
11168 };
11169
11170 /**
11171  * Generate a constructor for a specific record layout.
11172  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11173  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11174  * Each field definition object may contain the following properties: <ul>
11175  * <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,
11176  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11177  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11178  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11179  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11180  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11181  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11182  * this may be omitted.</p></li>
11183  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11184  * <ul><li>auto (Default, implies no conversion)</li>
11185  * <li>string</li>
11186  * <li>int</li>
11187  * <li>float</li>
11188  * <li>boolean</li>
11189  * <li>date</li></ul></p></li>
11190  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11191  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11192  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11193  * by the Reader into an object that will be stored in the Record. It is passed the
11194  * following parameters:<ul>
11195  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11196  * </ul></p></li>
11197  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11198  * </ul>
11199  * <br>usage:<br><pre><code>
11200 var TopicRecord = Roo.data.Record.create(
11201     {name: 'title', mapping: 'topic_title'},
11202     {name: 'author', mapping: 'username'},
11203     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11204     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11205     {name: 'lastPoster', mapping: 'user2'},
11206     {name: 'excerpt', mapping: 'post_text'}
11207 );
11208
11209 var myNewRecord = new TopicRecord({
11210     title: 'Do my job please',
11211     author: 'noobie',
11212     totalPosts: 1,
11213     lastPost: new Date(),
11214     lastPoster: 'Animal',
11215     excerpt: 'No way dude!'
11216 });
11217 myStore.add(myNewRecord);
11218 </code></pre>
11219  * @method create
11220  * @static
11221  */
11222 Roo.data.Record.create = function(o){
11223     var f = function(){
11224         f.superclass.constructor.apply(this, arguments);
11225     };
11226     Roo.extend(f, Roo.data.Record);
11227     var p = f.prototype;
11228     p.fields = new Roo.util.MixedCollection(false, function(field){
11229         return field.name;
11230     });
11231     for(var i = 0, len = o.length; i < len; i++){
11232         p.fields.add(new Roo.data.Field(o[i]));
11233     }
11234     f.getField = function(name){
11235         return p.fields.get(name);  
11236     };
11237     return f;
11238 };
11239
11240 Roo.data.Record.AUTO_ID = 1000;
11241 Roo.data.Record.EDIT = 'edit';
11242 Roo.data.Record.REJECT = 'reject';
11243 Roo.data.Record.COMMIT = 'commit';
11244
11245 Roo.data.Record.prototype = {
11246     /**
11247      * Readonly flag - true if this record has been modified.
11248      * @type Boolean
11249      */
11250     dirty : false,
11251     editing : false,
11252     error: null,
11253     modified: null,
11254
11255     // private
11256     join : function(store){
11257         this.store = store;
11258     },
11259
11260     /**
11261      * Set the named field to the specified value.
11262      * @param {String} name The name of the field to set.
11263      * @param {Object} value The value to set the field to.
11264      */
11265     set : function(name, value){
11266         if(this.data[name] == value){
11267             return;
11268         }
11269         this.dirty = true;
11270         if(!this.modified){
11271             this.modified = {};
11272         }
11273         if(typeof this.modified[name] == 'undefined'){
11274             this.modified[name] = this.data[name];
11275         }
11276         this.data[name] = value;
11277         if(!this.editing && this.store){
11278             this.store.afterEdit(this);
11279         }       
11280     },
11281
11282     /**
11283      * Get the value of the named field.
11284      * @param {String} name The name of the field to get the value of.
11285      * @return {Object} The value of the field.
11286      */
11287     get : function(name){
11288         return this.data[name]; 
11289     },
11290
11291     // private
11292     beginEdit : function(){
11293         this.editing = true;
11294         this.modified = {}; 
11295     },
11296
11297     // private
11298     cancelEdit : function(){
11299         this.editing = false;
11300         delete this.modified;
11301     },
11302
11303     // private
11304     endEdit : function(){
11305         this.editing = false;
11306         if(this.dirty && this.store){
11307             this.store.afterEdit(this);
11308         }
11309     },
11310
11311     /**
11312      * Usually called by the {@link Roo.data.Store} which owns the Record.
11313      * Rejects all changes made to the Record since either creation, or the last commit operation.
11314      * Modified fields are reverted to their original values.
11315      * <p>
11316      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11317      * of reject operations.
11318      */
11319     reject : function(){
11320         var m = this.modified;
11321         for(var n in m){
11322             if(typeof m[n] != "function"){
11323                 this.data[n] = m[n];
11324             }
11325         }
11326         this.dirty = false;
11327         delete this.modified;
11328         this.editing = false;
11329         if(this.store){
11330             this.store.afterReject(this);
11331         }
11332     },
11333
11334     /**
11335      * Usually called by the {@link Roo.data.Store} which owns the Record.
11336      * Commits all changes made to the Record since either creation, or the last commit operation.
11337      * <p>
11338      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11339      * of commit operations.
11340      */
11341     commit : function(){
11342         this.dirty = false;
11343         delete this.modified;
11344         this.editing = false;
11345         if(this.store){
11346             this.store.afterCommit(this);
11347         }
11348     },
11349
11350     // private
11351     hasError : function(){
11352         return this.error != null;
11353     },
11354
11355     // private
11356     clearError : function(){
11357         this.error = null;
11358     },
11359
11360     /**
11361      * Creates a copy of this record.
11362      * @param {String} id (optional) A new record id if you don't want to use this record's id
11363      * @return {Record}
11364      */
11365     copy : function(newId) {
11366         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11367     }
11368 };/*
11369  * Based on:
11370  * Ext JS Library 1.1.1
11371  * Copyright(c) 2006-2007, Ext JS, LLC.
11372  *
11373  * Originally Released Under LGPL - original licence link has changed is not relivant.
11374  *
11375  * Fork - LGPL
11376  * <script type="text/javascript">
11377  */
11378
11379
11380
11381 /**
11382  * @class Roo.data.Store
11383  * @extends Roo.util.Observable
11384  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11385  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11386  * <p>
11387  * 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
11388  * has no knowledge of the format of the data returned by the Proxy.<br>
11389  * <p>
11390  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11391  * instances from the data object. These records are cached and made available through accessor functions.
11392  * @constructor
11393  * Creates a new Store.
11394  * @param {Object} config A config object containing the objects needed for the Store to access data,
11395  * and read the data into Records.
11396  */
11397 Roo.data.Store = function(config){
11398     this.data = new Roo.util.MixedCollection(false);
11399     this.data.getKey = function(o){
11400         return o.id;
11401     };
11402     this.baseParams = {};
11403     // private
11404     this.paramNames = {
11405         "start" : "start",
11406         "limit" : "limit",
11407         "sort" : "sort",
11408         "dir" : "dir",
11409         "multisort" : "_multisort"
11410     };
11411
11412     if(config && config.data){
11413         this.inlineData = config.data;
11414         delete config.data;
11415     }
11416
11417     Roo.apply(this, config);
11418     
11419     if(this.reader){ // reader passed
11420         this.reader = Roo.factory(this.reader, Roo.data);
11421         this.reader.xmodule = this.xmodule || false;
11422         if(!this.recordType){
11423             this.recordType = this.reader.recordType;
11424         }
11425         if(this.reader.onMetaChange){
11426             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11427         }
11428     }
11429
11430     if(this.recordType){
11431         this.fields = this.recordType.prototype.fields;
11432     }
11433     this.modified = [];
11434
11435     this.addEvents({
11436         /**
11437          * @event datachanged
11438          * Fires when the data cache has changed, and a widget which is using this Store
11439          * as a Record cache should refresh its view.
11440          * @param {Store} this
11441          */
11442         datachanged : true,
11443         /**
11444          * @event metachange
11445          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11446          * @param {Store} this
11447          * @param {Object} meta The JSON metadata
11448          */
11449         metachange : true,
11450         /**
11451          * @event add
11452          * Fires when Records have been added to the Store
11453          * @param {Store} this
11454          * @param {Roo.data.Record[]} records The array of Records added
11455          * @param {Number} index The index at which the record(s) were added
11456          */
11457         add : true,
11458         /**
11459          * @event remove
11460          * Fires when a Record has been removed from the Store
11461          * @param {Store} this
11462          * @param {Roo.data.Record} record The Record that was removed
11463          * @param {Number} index The index at which the record was removed
11464          */
11465         remove : true,
11466         /**
11467          * @event update
11468          * Fires when a Record has been updated
11469          * @param {Store} this
11470          * @param {Roo.data.Record} record The Record that was updated
11471          * @param {String} operation The update operation being performed.  Value may be one of:
11472          * <pre><code>
11473  Roo.data.Record.EDIT
11474  Roo.data.Record.REJECT
11475  Roo.data.Record.COMMIT
11476          * </code></pre>
11477          */
11478         update : true,
11479         /**
11480          * @event clear
11481          * Fires when the data cache has been cleared.
11482          * @param {Store} this
11483          */
11484         clear : true,
11485         /**
11486          * @event beforeload
11487          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11488          * the load action will be canceled.
11489          * @param {Store} this
11490          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11491          */
11492         beforeload : true,
11493         /**
11494          * @event beforeloadadd
11495          * Fires after a new set of Records has been loaded.
11496          * @param {Store} this
11497          * @param {Roo.data.Record[]} records The Records that were loaded
11498          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11499          */
11500         beforeloadadd : true,
11501         /**
11502          * @event load
11503          * Fires after a new set of Records has been loaded, before they are added to the store.
11504          * @param {Store} this
11505          * @param {Roo.data.Record[]} records The Records that were loaded
11506          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11507          * @params {Object} return from reader
11508          */
11509         load : true,
11510         /**
11511          * @event loadexception
11512          * Fires if an exception occurs in the Proxy during loading.
11513          * Called with the signature of the Proxy's "loadexception" event.
11514          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11515          * 
11516          * @param {Proxy} 
11517          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11518          * @param {Object} load options 
11519          * @param {Object} jsonData from your request (normally this contains the Exception)
11520          */
11521         loadexception : true
11522     });
11523     
11524     if(this.proxy){
11525         this.proxy = Roo.factory(this.proxy, Roo.data);
11526         this.proxy.xmodule = this.xmodule || false;
11527         this.relayEvents(this.proxy,  ["loadexception"]);
11528     }
11529     this.sortToggle = {};
11530     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11531
11532     Roo.data.Store.superclass.constructor.call(this);
11533
11534     if(this.inlineData){
11535         this.loadData(this.inlineData);
11536         delete this.inlineData;
11537     }
11538 };
11539
11540 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11541      /**
11542     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11543     * without a remote query - used by combo/forms at present.
11544     */
11545     
11546     /**
11547     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11548     */
11549     /**
11550     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11551     */
11552     /**
11553     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11554     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11555     */
11556     /**
11557     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11558     * on any HTTP request
11559     */
11560     /**
11561     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11562     */
11563     /**
11564     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11565     */
11566     multiSort: false,
11567     /**
11568     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11569     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11570     */
11571     remoteSort : false,
11572
11573     /**
11574     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11575      * loaded or when a record is removed. (defaults to false).
11576     */
11577     pruneModifiedRecords : false,
11578
11579     // private
11580     lastOptions : null,
11581
11582     /**
11583      * Add Records to the Store and fires the add event.
11584      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11585      */
11586     add : function(records){
11587         records = [].concat(records);
11588         for(var i = 0, len = records.length; i < len; i++){
11589             records[i].join(this);
11590         }
11591         var index = this.data.length;
11592         this.data.addAll(records);
11593         this.fireEvent("add", this, records, index);
11594     },
11595
11596     /**
11597      * Remove a Record from the Store and fires the remove event.
11598      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11599      */
11600     remove : function(record){
11601         var index = this.data.indexOf(record);
11602         this.data.removeAt(index);
11603  
11604         if(this.pruneModifiedRecords){
11605             this.modified.remove(record);
11606         }
11607         this.fireEvent("remove", this, record, index);
11608     },
11609
11610     /**
11611      * Remove all Records from the Store and fires the clear event.
11612      */
11613     removeAll : function(){
11614         this.data.clear();
11615         if(this.pruneModifiedRecords){
11616             this.modified = [];
11617         }
11618         this.fireEvent("clear", this);
11619     },
11620
11621     /**
11622      * Inserts Records to the Store at the given index and fires the add event.
11623      * @param {Number} index The start index at which to insert the passed Records.
11624      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11625      */
11626     insert : function(index, records){
11627         records = [].concat(records);
11628         for(var i = 0, len = records.length; i < len; i++){
11629             this.data.insert(index, records[i]);
11630             records[i].join(this);
11631         }
11632         this.fireEvent("add", this, records, index);
11633     },
11634
11635     /**
11636      * Get the index within the cache of the passed Record.
11637      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11638      * @return {Number} The index of the passed Record. Returns -1 if not found.
11639      */
11640     indexOf : function(record){
11641         return this.data.indexOf(record);
11642     },
11643
11644     /**
11645      * Get the index within the cache of the Record with the passed id.
11646      * @param {String} id The id of the Record to find.
11647      * @return {Number} The index of the Record. Returns -1 if not found.
11648      */
11649     indexOfId : function(id){
11650         return this.data.indexOfKey(id);
11651     },
11652
11653     /**
11654      * Get the Record with the specified id.
11655      * @param {String} id The id of the Record to find.
11656      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11657      */
11658     getById : function(id){
11659         return this.data.key(id);
11660     },
11661
11662     /**
11663      * Get the Record at the specified index.
11664      * @param {Number} index The index of the Record to find.
11665      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11666      */
11667     getAt : function(index){
11668         return this.data.itemAt(index);
11669     },
11670
11671     /**
11672      * Returns a range of Records between specified indices.
11673      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11674      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11675      * @return {Roo.data.Record[]} An array of Records
11676      */
11677     getRange : function(start, end){
11678         return this.data.getRange(start, end);
11679     },
11680
11681     // private
11682     storeOptions : function(o){
11683         o = Roo.apply({}, o);
11684         delete o.callback;
11685         delete o.scope;
11686         this.lastOptions = o;
11687     },
11688
11689     /**
11690      * Loads the Record cache from the configured Proxy using the configured Reader.
11691      * <p>
11692      * If using remote paging, then the first load call must specify the <em>start</em>
11693      * and <em>limit</em> properties in the options.params property to establish the initial
11694      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11695      * <p>
11696      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11697      * and this call will return before the new data has been loaded. Perform any post-processing
11698      * in a callback function, or in a "load" event handler.</strong>
11699      * <p>
11700      * @param {Object} options An object containing properties which control loading options:<ul>
11701      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11702      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11703      * passed the following arguments:<ul>
11704      * <li>r : Roo.data.Record[]</li>
11705      * <li>options: Options object from the load call</li>
11706      * <li>success: Boolean success indicator</li></ul></li>
11707      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11708      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11709      * </ul>
11710      */
11711     load : function(options){
11712         options = options || {};
11713         if(this.fireEvent("beforeload", this, options) !== false){
11714             this.storeOptions(options);
11715             var p = Roo.apply(options.params || {}, this.baseParams);
11716             // if meta was not loaded from remote source.. try requesting it.
11717             if (!this.reader.metaFromRemote) {
11718                 p._requestMeta = 1;
11719             }
11720             if(this.sortInfo && this.remoteSort){
11721                 var pn = this.paramNames;
11722                 p[pn["sort"]] = this.sortInfo.field;
11723                 p[pn["dir"]] = this.sortInfo.direction;
11724             }
11725             if (this.multiSort) {
11726                 var pn = this.paramNames;
11727                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11728             }
11729             
11730             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11731         }
11732     },
11733
11734     /**
11735      * Reloads the Record cache from the configured Proxy using the configured Reader and
11736      * the options from the last load operation performed.
11737      * @param {Object} options (optional) An object containing properties which may override the options
11738      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11739      * the most recently used options are reused).
11740      */
11741     reload : function(options){
11742         this.load(Roo.applyIf(options||{}, this.lastOptions));
11743     },
11744
11745     // private
11746     // Called as a callback by the Reader during a load operation.
11747     loadRecords : function(o, options, success){
11748         if(!o || success === false){
11749             if(success !== false){
11750                 this.fireEvent("load", this, [], options, o);
11751             }
11752             if(options.callback){
11753                 options.callback.call(options.scope || this, [], options, false);
11754             }
11755             return;
11756         }
11757         // if data returned failure - throw an exception.
11758         if (o.success === false) {
11759             // show a message if no listener is registered.
11760             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11761                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11762             }
11763             // loadmask wil be hooked into this..
11764             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11765             return;
11766         }
11767         var r = o.records, t = o.totalRecords || r.length;
11768         
11769         this.fireEvent("beforeloadadd", this, r, options, o);
11770         
11771         if(!options || options.add !== true){
11772             if(this.pruneModifiedRecords){
11773                 this.modified = [];
11774             }
11775             for(var i = 0, len = r.length; i < len; i++){
11776                 r[i].join(this);
11777             }
11778             if(this.snapshot){
11779                 this.data = this.snapshot;
11780                 delete this.snapshot;
11781             }
11782             this.data.clear();
11783             this.data.addAll(r);
11784             this.totalLength = t;
11785             this.applySort();
11786             this.fireEvent("datachanged", this);
11787         }else{
11788             this.totalLength = Math.max(t, this.data.length+r.length);
11789             this.add(r);
11790         }
11791         
11792         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11793                 
11794             var e = new Roo.data.Record({});
11795
11796             e.set(this.parent.displayField, this.parent.emptyTitle);
11797             e.set(this.parent.valueField, '');
11798
11799             this.insert(0, e);
11800         }
11801             
11802         this.fireEvent("load", this, r, options, o);
11803         if(options.callback){
11804             options.callback.call(options.scope || this, r, options, true);
11805         }
11806     },
11807
11808
11809     /**
11810      * Loads data from a passed data block. A Reader which understands the format of the data
11811      * must have been configured in the constructor.
11812      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11813      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11814      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11815      */
11816     loadData : function(o, append){
11817         var r = this.reader.readRecords(o);
11818         this.loadRecords(r, {add: append}, true);
11819     },
11820
11821     /**
11822      * Gets the number of cached records.
11823      * <p>
11824      * <em>If using paging, this may not be the total size of the dataset. If the data object
11825      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11826      * the data set size</em>
11827      */
11828     getCount : function(){
11829         return this.data.length || 0;
11830     },
11831
11832     /**
11833      * Gets the total number of records in the dataset as returned by the server.
11834      * <p>
11835      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11836      * the dataset size</em>
11837      */
11838     getTotalCount : function(){
11839         return this.totalLength || 0;
11840     },
11841
11842     /**
11843      * Returns the sort state of the Store as an object with two properties:
11844      * <pre><code>
11845  field {String} The name of the field by which the Records are sorted
11846  direction {String} The sort order, "ASC" or "DESC"
11847      * </code></pre>
11848      */
11849     getSortState : function(){
11850         return this.sortInfo;
11851     },
11852
11853     // private
11854     applySort : function(){
11855         if(this.sortInfo && !this.remoteSort){
11856             var s = this.sortInfo, f = s.field;
11857             var st = this.fields.get(f).sortType;
11858             var fn = function(r1, r2){
11859                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11860                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11861             };
11862             this.data.sort(s.direction, fn);
11863             if(this.snapshot && this.snapshot != this.data){
11864                 this.snapshot.sort(s.direction, fn);
11865             }
11866         }
11867     },
11868
11869     /**
11870      * Sets the default sort column and order to be used by the next load operation.
11871      * @param {String} fieldName The name of the field to sort by.
11872      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11873      */
11874     setDefaultSort : function(field, dir){
11875         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11876     },
11877
11878     /**
11879      * Sort the Records.
11880      * If remote sorting is used, the sort is performed on the server, and the cache is
11881      * reloaded. If local sorting is used, the cache is sorted internally.
11882      * @param {String} fieldName The name of the field to sort by.
11883      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11884      */
11885     sort : function(fieldName, dir){
11886         var f = this.fields.get(fieldName);
11887         if(!dir){
11888             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11889             
11890             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11891                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11892             }else{
11893                 dir = f.sortDir;
11894             }
11895         }
11896         this.sortToggle[f.name] = dir;
11897         this.sortInfo = {field: f.name, direction: dir};
11898         if(!this.remoteSort){
11899             this.applySort();
11900             this.fireEvent("datachanged", this);
11901         }else{
11902             this.load(this.lastOptions);
11903         }
11904     },
11905
11906     /**
11907      * Calls the specified function for each of the Records in the cache.
11908      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11909      * Returning <em>false</em> aborts and exits the iteration.
11910      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11911      */
11912     each : function(fn, scope){
11913         this.data.each(fn, scope);
11914     },
11915
11916     /**
11917      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11918      * (e.g., during paging).
11919      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11920      */
11921     getModifiedRecords : function(){
11922         return this.modified;
11923     },
11924
11925     // private
11926     createFilterFn : function(property, value, anyMatch){
11927         if(!value.exec){ // not a regex
11928             value = String(value);
11929             if(value.length == 0){
11930                 return false;
11931             }
11932             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11933         }
11934         return function(r){
11935             return value.test(r.data[property]);
11936         };
11937     },
11938
11939     /**
11940      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11941      * @param {String} property A field on your records
11942      * @param {Number} start The record index to start at (defaults to 0)
11943      * @param {Number} end The last record index to include (defaults to length - 1)
11944      * @return {Number} The sum
11945      */
11946     sum : function(property, start, end){
11947         var rs = this.data.items, v = 0;
11948         start = start || 0;
11949         end = (end || end === 0) ? end : rs.length-1;
11950
11951         for(var i = start; i <= end; i++){
11952             v += (rs[i].data[property] || 0);
11953         }
11954         return v;
11955     },
11956
11957     /**
11958      * Filter the records by a specified property.
11959      * @param {String} field A field on your records
11960      * @param {String/RegExp} value Either a string that the field
11961      * should start with or a RegExp to test against the field
11962      * @param {Boolean} anyMatch True to match any part not just the beginning
11963      */
11964     filter : function(property, value, anyMatch){
11965         var fn = this.createFilterFn(property, value, anyMatch);
11966         return fn ? this.filterBy(fn) : this.clearFilter();
11967     },
11968
11969     /**
11970      * Filter by a function. The specified function will be called with each
11971      * record in this data source. If the function returns true the record is included,
11972      * otherwise it is filtered.
11973      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11974      * @param {Object} scope (optional) The scope of the function (defaults to this)
11975      */
11976     filterBy : function(fn, scope){
11977         this.snapshot = this.snapshot || this.data;
11978         this.data = this.queryBy(fn, scope||this);
11979         this.fireEvent("datachanged", this);
11980     },
11981
11982     /**
11983      * Query the records by a specified property.
11984      * @param {String} field A field on your records
11985      * @param {String/RegExp} value Either a string that the field
11986      * should start with or a RegExp to test against the field
11987      * @param {Boolean} anyMatch True to match any part not just the beginning
11988      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11989      */
11990     query : function(property, value, anyMatch){
11991         var fn = this.createFilterFn(property, value, anyMatch);
11992         return fn ? this.queryBy(fn) : this.data.clone();
11993     },
11994
11995     /**
11996      * Query by a function. The specified function will be called with each
11997      * record in this data source. If the function returns true the record is included
11998      * in the results.
11999      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12000      * @param {Object} scope (optional) The scope of the function (defaults to this)
12001       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12002      **/
12003     queryBy : function(fn, scope){
12004         var data = this.snapshot || this.data;
12005         return data.filterBy(fn, scope||this);
12006     },
12007
12008     /**
12009      * Collects unique values for a particular dataIndex from this store.
12010      * @param {String} dataIndex The property to collect
12011      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12012      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12013      * @return {Array} An array of the unique values
12014      **/
12015     collect : function(dataIndex, allowNull, bypassFilter){
12016         var d = (bypassFilter === true && this.snapshot) ?
12017                 this.snapshot.items : this.data.items;
12018         var v, sv, r = [], l = {};
12019         for(var i = 0, len = d.length; i < len; i++){
12020             v = d[i].data[dataIndex];
12021             sv = String(v);
12022             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12023                 l[sv] = true;
12024                 r[r.length] = v;
12025             }
12026         }
12027         return r;
12028     },
12029
12030     /**
12031      * Revert to a view of the Record cache with no filtering applied.
12032      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12033      */
12034     clearFilter : function(suppressEvent){
12035         if(this.snapshot && this.snapshot != this.data){
12036             this.data = this.snapshot;
12037             delete this.snapshot;
12038             if(suppressEvent !== true){
12039                 this.fireEvent("datachanged", this);
12040             }
12041         }
12042     },
12043
12044     // private
12045     afterEdit : function(record){
12046         if(this.modified.indexOf(record) == -1){
12047             this.modified.push(record);
12048         }
12049         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12050     },
12051     
12052     // private
12053     afterReject : function(record){
12054         this.modified.remove(record);
12055         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12056     },
12057
12058     // private
12059     afterCommit : function(record){
12060         this.modified.remove(record);
12061         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12062     },
12063
12064     /**
12065      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12066      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12067      */
12068     commitChanges : function(){
12069         var m = this.modified.slice(0);
12070         this.modified = [];
12071         for(var i = 0, len = m.length; i < len; i++){
12072             m[i].commit();
12073         }
12074     },
12075
12076     /**
12077      * Cancel outstanding changes on all changed records.
12078      */
12079     rejectChanges : function(){
12080         var m = this.modified.slice(0);
12081         this.modified = [];
12082         for(var i = 0, len = m.length; i < len; i++){
12083             m[i].reject();
12084         }
12085     },
12086
12087     onMetaChange : function(meta, rtype, o){
12088         this.recordType = rtype;
12089         this.fields = rtype.prototype.fields;
12090         delete this.snapshot;
12091         this.sortInfo = meta.sortInfo || this.sortInfo;
12092         this.modified = [];
12093         this.fireEvent('metachange', this, this.reader.meta);
12094     },
12095     
12096     moveIndex : function(data, type)
12097     {
12098         var index = this.indexOf(data);
12099         
12100         var newIndex = index + type;
12101         
12102         this.remove(data);
12103         
12104         this.insert(newIndex, data);
12105         
12106     }
12107 });/*
12108  * Based on:
12109  * Ext JS Library 1.1.1
12110  * Copyright(c) 2006-2007, Ext JS, LLC.
12111  *
12112  * Originally Released Under LGPL - original licence link has changed is not relivant.
12113  *
12114  * Fork - LGPL
12115  * <script type="text/javascript">
12116  */
12117
12118 /**
12119  * @class Roo.data.SimpleStore
12120  * @extends Roo.data.Store
12121  * Small helper class to make creating Stores from Array data easier.
12122  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12123  * @cfg {Array} fields An array of field definition objects, or field name strings.
12124  * @cfg {Array} data The multi-dimensional array of data
12125  * @constructor
12126  * @param {Object} config
12127  */
12128 Roo.data.SimpleStore = function(config){
12129     Roo.data.SimpleStore.superclass.constructor.call(this, {
12130         isLocal : true,
12131         reader: new Roo.data.ArrayReader({
12132                 id: config.id
12133             },
12134             Roo.data.Record.create(config.fields)
12135         ),
12136         proxy : new Roo.data.MemoryProxy(config.data)
12137     });
12138     this.load();
12139 };
12140 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12141  * Based on:
12142  * Ext JS Library 1.1.1
12143  * Copyright(c) 2006-2007, Ext JS, LLC.
12144  *
12145  * Originally Released Under LGPL - original licence link has changed is not relivant.
12146  *
12147  * Fork - LGPL
12148  * <script type="text/javascript">
12149  */
12150
12151 /**
12152 /**
12153  * @extends Roo.data.Store
12154  * @class Roo.data.JsonStore
12155  * Small helper class to make creating Stores for JSON data easier. <br/>
12156 <pre><code>
12157 var store = new Roo.data.JsonStore({
12158     url: 'get-images.php',
12159     root: 'images',
12160     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12161 });
12162 </code></pre>
12163  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12164  * JsonReader and HttpProxy (unless inline data is provided).</b>
12165  * @cfg {Array} fields An array of field definition objects, or field name strings.
12166  * @constructor
12167  * @param {Object} config
12168  */
12169 Roo.data.JsonStore = function(c){
12170     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12171         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12172         reader: new Roo.data.JsonReader(c, c.fields)
12173     }));
12174 };
12175 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12176  * Based on:
12177  * Ext JS Library 1.1.1
12178  * Copyright(c) 2006-2007, Ext JS, LLC.
12179  *
12180  * Originally Released Under LGPL - original licence link has changed is not relivant.
12181  *
12182  * Fork - LGPL
12183  * <script type="text/javascript">
12184  */
12185
12186  
12187 Roo.data.Field = function(config){
12188     if(typeof config == "string"){
12189         config = {name: config};
12190     }
12191     Roo.apply(this, config);
12192     
12193     if(!this.type){
12194         this.type = "auto";
12195     }
12196     
12197     var st = Roo.data.SortTypes;
12198     // named sortTypes are supported, here we look them up
12199     if(typeof this.sortType == "string"){
12200         this.sortType = st[this.sortType];
12201     }
12202     
12203     // set default sortType for strings and dates
12204     if(!this.sortType){
12205         switch(this.type){
12206             case "string":
12207                 this.sortType = st.asUCString;
12208                 break;
12209             case "date":
12210                 this.sortType = st.asDate;
12211                 break;
12212             default:
12213                 this.sortType = st.none;
12214         }
12215     }
12216
12217     // define once
12218     var stripRe = /[\$,%]/g;
12219
12220     // prebuilt conversion function for this field, instead of
12221     // switching every time we're reading a value
12222     if(!this.convert){
12223         var cv, dateFormat = this.dateFormat;
12224         switch(this.type){
12225             case "":
12226             case "auto":
12227             case undefined:
12228                 cv = function(v){ return v; };
12229                 break;
12230             case "string":
12231                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12232                 break;
12233             case "int":
12234                 cv = function(v){
12235                     return v !== undefined && v !== null && v !== '' ?
12236                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12237                     };
12238                 break;
12239             case "float":
12240                 cv = function(v){
12241                     return v !== undefined && v !== null && v !== '' ?
12242                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12243                     };
12244                 break;
12245             case "bool":
12246             case "boolean":
12247                 cv = function(v){ return v === true || v === "true" || v == 1; };
12248                 break;
12249             case "date":
12250                 cv = function(v){
12251                     if(!v){
12252                         return '';
12253                     }
12254                     if(v instanceof Date){
12255                         return v;
12256                     }
12257                     if(dateFormat){
12258                         if(dateFormat == "timestamp"){
12259                             return new Date(v*1000);
12260                         }
12261                         return Date.parseDate(v, dateFormat);
12262                     }
12263                     var parsed = Date.parse(v);
12264                     return parsed ? new Date(parsed) : null;
12265                 };
12266              break;
12267             
12268         }
12269         this.convert = cv;
12270     }
12271 };
12272
12273 Roo.data.Field.prototype = {
12274     dateFormat: null,
12275     defaultValue: "",
12276     mapping: null,
12277     sortType : null,
12278     sortDir : "ASC"
12279 };/*
12280  * Based on:
12281  * Ext JS Library 1.1.1
12282  * Copyright(c) 2006-2007, Ext JS, LLC.
12283  *
12284  * Originally Released Under LGPL - original licence link has changed is not relivant.
12285  *
12286  * Fork - LGPL
12287  * <script type="text/javascript">
12288  */
12289  
12290 // Base class for reading structured data from a data source.  This class is intended to be
12291 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12292
12293 /**
12294  * @class Roo.data.DataReader
12295  * Base class for reading structured data from a data source.  This class is intended to be
12296  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12297  */
12298
12299 Roo.data.DataReader = function(meta, recordType){
12300     
12301     this.meta = meta;
12302     
12303     this.recordType = recordType instanceof Array ? 
12304         Roo.data.Record.create(recordType) : recordType;
12305 };
12306
12307 Roo.data.DataReader.prototype = {
12308      /**
12309      * Create an empty record
12310      * @param {Object} data (optional) - overlay some values
12311      * @return {Roo.data.Record} record created.
12312      */
12313     newRow :  function(d) {
12314         var da =  {};
12315         this.recordType.prototype.fields.each(function(c) {
12316             switch( c.type) {
12317                 case 'int' : da[c.name] = 0; break;
12318                 case 'date' : da[c.name] = new Date(); break;
12319                 case 'float' : da[c.name] = 0.0; break;
12320                 case 'boolean' : da[c.name] = false; break;
12321                 default : da[c.name] = ""; break;
12322             }
12323             
12324         });
12325         return new this.recordType(Roo.apply(da, d));
12326     }
12327     
12328 };/*
12329  * Based on:
12330  * Ext JS Library 1.1.1
12331  * Copyright(c) 2006-2007, Ext JS, LLC.
12332  *
12333  * Originally Released Under LGPL - original licence link has changed is not relivant.
12334  *
12335  * Fork - LGPL
12336  * <script type="text/javascript">
12337  */
12338
12339 /**
12340  * @class Roo.data.DataProxy
12341  * @extends Roo.data.Observable
12342  * This class is an abstract base class for implementations which provide retrieval of
12343  * unformatted data objects.<br>
12344  * <p>
12345  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12346  * (of the appropriate type which knows how to parse the data object) to provide a block of
12347  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12348  * <p>
12349  * Custom implementations must implement the load method as described in
12350  * {@link Roo.data.HttpProxy#load}.
12351  */
12352 Roo.data.DataProxy = function(){
12353     this.addEvents({
12354         /**
12355          * @event beforeload
12356          * Fires before a network request is made to retrieve a data object.
12357          * @param {Object} This DataProxy object.
12358          * @param {Object} params The params parameter to the load function.
12359          */
12360         beforeload : true,
12361         /**
12362          * @event load
12363          * Fires before the load method's callback is called.
12364          * @param {Object} This DataProxy object.
12365          * @param {Object} o The data object.
12366          * @param {Object} arg The callback argument object passed to the load function.
12367          */
12368         load : true,
12369         /**
12370          * @event loadexception
12371          * Fires if an Exception occurs during data retrieval.
12372          * @param {Object} This DataProxy object.
12373          * @param {Object} o The data object.
12374          * @param {Object} arg The callback argument object passed to the load function.
12375          * @param {Object} e The Exception.
12376          */
12377         loadexception : true
12378     });
12379     Roo.data.DataProxy.superclass.constructor.call(this);
12380 };
12381
12382 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12383
12384     /**
12385      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12386      */
12387 /*
12388  * Based on:
12389  * Ext JS Library 1.1.1
12390  * Copyright(c) 2006-2007, Ext JS, LLC.
12391  *
12392  * Originally Released Under LGPL - original licence link has changed is not relivant.
12393  *
12394  * Fork - LGPL
12395  * <script type="text/javascript">
12396  */
12397 /**
12398  * @class Roo.data.MemoryProxy
12399  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12400  * to the Reader when its load method is called.
12401  * @constructor
12402  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12403  */
12404 Roo.data.MemoryProxy = function(data){
12405     if (data.data) {
12406         data = data.data;
12407     }
12408     Roo.data.MemoryProxy.superclass.constructor.call(this);
12409     this.data = data;
12410 };
12411
12412 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12413     
12414     /**
12415      * Load data from the requested source (in this case an in-memory
12416      * data object passed to the constructor), read the data object into
12417      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12418      * process that block using the passed callback.
12419      * @param {Object} params This parameter is not used by the MemoryProxy class.
12420      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12421      * object into a block of Roo.data.Records.
12422      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12423      * The function must be passed <ul>
12424      * <li>The Record block object</li>
12425      * <li>The "arg" argument from the load function</li>
12426      * <li>A boolean success indicator</li>
12427      * </ul>
12428      * @param {Object} scope The scope in which to call the callback
12429      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12430      */
12431     load : function(params, reader, callback, scope, arg){
12432         params = params || {};
12433         var result;
12434         try {
12435             result = reader.readRecords(params.data ? params.data :this.data);
12436         }catch(e){
12437             this.fireEvent("loadexception", this, arg, null, e);
12438             callback.call(scope, null, arg, false);
12439             return;
12440         }
12441         callback.call(scope, result, arg, true);
12442     },
12443     
12444     // private
12445     update : function(params, records){
12446         
12447     }
12448 });/*
12449  * Based on:
12450  * Ext JS Library 1.1.1
12451  * Copyright(c) 2006-2007, Ext JS, LLC.
12452  *
12453  * Originally Released Under LGPL - original licence link has changed is not relivant.
12454  *
12455  * Fork - LGPL
12456  * <script type="text/javascript">
12457  */
12458 /**
12459  * @class Roo.data.HttpProxy
12460  * @extends Roo.data.DataProxy
12461  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12462  * configured to reference a certain URL.<br><br>
12463  * <p>
12464  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12465  * from which the running page was served.<br><br>
12466  * <p>
12467  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12468  * <p>
12469  * Be aware that to enable the browser to parse an XML document, the server must set
12470  * the Content-Type header in the HTTP response to "text/xml".
12471  * @constructor
12472  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12473  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12474  * will be used to make the request.
12475  */
12476 Roo.data.HttpProxy = function(conn){
12477     Roo.data.HttpProxy.superclass.constructor.call(this);
12478     // is conn a conn config or a real conn?
12479     this.conn = conn;
12480     this.useAjax = !conn || !conn.events;
12481   
12482 };
12483
12484 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12485     // thse are take from connection...
12486     
12487     /**
12488      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12489      */
12490     /**
12491      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12492      * extra parameters to each request made by this object. (defaults to undefined)
12493      */
12494     /**
12495      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12496      *  to each request made by this object. (defaults to undefined)
12497      */
12498     /**
12499      * @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)
12500      */
12501     /**
12502      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12503      */
12504      /**
12505      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12506      * @type Boolean
12507      */
12508   
12509
12510     /**
12511      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12512      * @type Boolean
12513      */
12514     /**
12515      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12516      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12517      * a finer-grained basis than the DataProxy events.
12518      */
12519     getConnection : function(){
12520         return this.useAjax ? Roo.Ajax : this.conn;
12521     },
12522
12523     /**
12524      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12525      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12526      * process that block using the passed callback.
12527      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12528      * for the request to the remote server.
12529      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12530      * object into a block of Roo.data.Records.
12531      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12532      * The function must be passed <ul>
12533      * <li>The Record block object</li>
12534      * <li>The "arg" argument from the load function</li>
12535      * <li>A boolean success indicator</li>
12536      * </ul>
12537      * @param {Object} scope The scope in which to call the callback
12538      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12539      */
12540     load : function(params, reader, callback, scope, arg){
12541         if(this.fireEvent("beforeload", this, params) !== false){
12542             var  o = {
12543                 params : params || {},
12544                 request: {
12545                     callback : callback,
12546                     scope : scope,
12547                     arg : arg
12548                 },
12549                 reader: reader,
12550                 callback : this.loadResponse,
12551                 scope: this
12552             };
12553             if(this.useAjax){
12554                 Roo.applyIf(o, this.conn);
12555                 if(this.activeRequest){
12556                     Roo.Ajax.abort(this.activeRequest);
12557                 }
12558                 this.activeRequest = Roo.Ajax.request(o);
12559             }else{
12560                 this.conn.request(o);
12561             }
12562         }else{
12563             callback.call(scope||this, null, arg, false);
12564         }
12565     },
12566
12567     // private
12568     loadResponse : function(o, success, response){
12569         delete this.activeRequest;
12570         if(!success){
12571             this.fireEvent("loadexception", this, o, response);
12572             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12573             return;
12574         }
12575         var result;
12576         try {
12577             result = o.reader.read(response);
12578         }catch(e){
12579             this.fireEvent("loadexception", this, o, response, e);
12580             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12581             return;
12582         }
12583         
12584         this.fireEvent("load", this, o, o.request.arg);
12585         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12586     },
12587
12588     // private
12589     update : function(dataSet){
12590
12591     },
12592
12593     // private
12594     updateResponse : function(dataSet){
12595
12596     }
12597 });/*
12598  * Based on:
12599  * Ext JS Library 1.1.1
12600  * Copyright(c) 2006-2007, Ext JS, LLC.
12601  *
12602  * Originally Released Under LGPL - original licence link has changed is not relivant.
12603  *
12604  * Fork - LGPL
12605  * <script type="text/javascript">
12606  */
12607
12608 /**
12609  * @class Roo.data.ScriptTagProxy
12610  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12611  * other than the originating domain of the running page.<br><br>
12612  * <p>
12613  * <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
12614  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12615  * <p>
12616  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12617  * source code that is used as the source inside a &lt;script> tag.<br><br>
12618  * <p>
12619  * In order for the browser to process the returned data, the server must wrap the data object
12620  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12621  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12622  * depending on whether the callback name was passed:
12623  * <p>
12624  * <pre><code>
12625 boolean scriptTag = false;
12626 String cb = request.getParameter("callback");
12627 if (cb != null) {
12628     scriptTag = true;
12629     response.setContentType("text/javascript");
12630 } else {
12631     response.setContentType("application/x-json");
12632 }
12633 Writer out = response.getWriter();
12634 if (scriptTag) {
12635     out.write(cb + "(");
12636 }
12637 out.print(dataBlock.toJsonString());
12638 if (scriptTag) {
12639     out.write(");");
12640 }
12641 </pre></code>
12642  *
12643  * @constructor
12644  * @param {Object} config A configuration object.
12645  */
12646 Roo.data.ScriptTagProxy = function(config){
12647     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12648     Roo.apply(this, config);
12649     this.head = document.getElementsByTagName("head")[0];
12650 };
12651
12652 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12653
12654 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12655     /**
12656      * @cfg {String} url The URL from which to request the data object.
12657      */
12658     /**
12659      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12660      */
12661     timeout : 30000,
12662     /**
12663      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12664      * the server the name of the callback function set up by the load call to process the returned data object.
12665      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12666      * javascript output which calls this named function passing the data object as its only parameter.
12667      */
12668     callbackParam : "callback",
12669     /**
12670      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12671      * name to the request.
12672      */
12673     nocache : true,
12674
12675     /**
12676      * Load data from the configured URL, read the data object into
12677      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12678      * process that block using the passed callback.
12679      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12680      * for the request to the remote server.
12681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12682      * object into a block of Roo.data.Records.
12683      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12684      * The function must be passed <ul>
12685      * <li>The Record block object</li>
12686      * <li>The "arg" argument from the load function</li>
12687      * <li>A boolean success indicator</li>
12688      * </ul>
12689      * @param {Object} scope The scope in which to call the callback
12690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12691      */
12692     load : function(params, reader, callback, scope, arg){
12693         if(this.fireEvent("beforeload", this, params) !== false){
12694
12695             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12696
12697             var url = this.url;
12698             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12699             if(this.nocache){
12700                 url += "&_dc=" + (new Date().getTime());
12701             }
12702             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12703             var trans = {
12704                 id : transId,
12705                 cb : "stcCallback"+transId,
12706                 scriptId : "stcScript"+transId,
12707                 params : params,
12708                 arg : arg,
12709                 url : url,
12710                 callback : callback,
12711                 scope : scope,
12712                 reader : reader
12713             };
12714             var conn = this;
12715
12716             window[trans.cb] = function(o){
12717                 conn.handleResponse(o, trans);
12718             };
12719
12720             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12721
12722             if(this.autoAbort !== false){
12723                 this.abort();
12724             }
12725
12726             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12727
12728             var script = document.createElement("script");
12729             script.setAttribute("src", url);
12730             script.setAttribute("type", "text/javascript");
12731             script.setAttribute("id", trans.scriptId);
12732             this.head.appendChild(script);
12733
12734             this.trans = trans;
12735         }else{
12736             callback.call(scope||this, null, arg, false);
12737         }
12738     },
12739
12740     // private
12741     isLoading : function(){
12742         return this.trans ? true : false;
12743     },
12744
12745     /**
12746      * Abort the current server request.
12747      */
12748     abort : function(){
12749         if(this.isLoading()){
12750             this.destroyTrans(this.trans);
12751         }
12752     },
12753
12754     // private
12755     destroyTrans : function(trans, isLoaded){
12756         this.head.removeChild(document.getElementById(trans.scriptId));
12757         clearTimeout(trans.timeoutId);
12758         if(isLoaded){
12759             window[trans.cb] = undefined;
12760             try{
12761                 delete window[trans.cb];
12762             }catch(e){}
12763         }else{
12764             // if hasn't been loaded, wait for load to remove it to prevent script error
12765             window[trans.cb] = function(){
12766                 window[trans.cb] = undefined;
12767                 try{
12768                     delete window[trans.cb];
12769                 }catch(e){}
12770             };
12771         }
12772     },
12773
12774     // private
12775     handleResponse : function(o, trans){
12776         this.trans = false;
12777         this.destroyTrans(trans, true);
12778         var result;
12779         try {
12780             result = trans.reader.readRecords(o);
12781         }catch(e){
12782             this.fireEvent("loadexception", this, o, trans.arg, e);
12783             trans.callback.call(trans.scope||window, null, trans.arg, false);
12784             return;
12785         }
12786         this.fireEvent("load", this, o, trans.arg);
12787         trans.callback.call(trans.scope||window, result, trans.arg, true);
12788     },
12789
12790     // private
12791     handleFailure : function(trans){
12792         this.trans = false;
12793         this.destroyTrans(trans, false);
12794         this.fireEvent("loadexception", this, null, trans.arg);
12795         trans.callback.call(trans.scope||window, null, trans.arg, false);
12796     }
12797 });/*
12798  * Based on:
12799  * Ext JS Library 1.1.1
12800  * Copyright(c) 2006-2007, Ext JS, LLC.
12801  *
12802  * Originally Released Under LGPL - original licence link has changed is not relivant.
12803  *
12804  * Fork - LGPL
12805  * <script type="text/javascript">
12806  */
12807
12808 /**
12809  * @class Roo.data.JsonReader
12810  * @extends Roo.data.DataReader
12811  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12812  * based on mappings in a provided Roo.data.Record constructor.
12813  * 
12814  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12815  * in the reply previously. 
12816  * 
12817  * <p>
12818  * Example code:
12819  * <pre><code>
12820 var RecordDef = Roo.data.Record.create([
12821     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12822     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12823 ]);
12824 var myReader = new Roo.data.JsonReader({
12825     totalProperty: "results",    // The property which contains the total dataset size (optional)
12826     root: "rows",                // The property which contains an Array of row objects
12827     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12828 }, RecordDef);
12829 </code></pre>
12830  * <p>
12831  * This would consume a JSON file like this:
12832  * <pre><code>
12833 { 'results': 2, 'rows': [
12834     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12835     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12836 }
12837 </code></pre>
12838  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12839  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12840  * paged from the remote server.
12841  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12842  * @cfg {String} root name of the property which contains the Array of row objects.
12843  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12844  * @cfg {Array} fields Array of field definition objects
12845  * @constructor
12846  * Create a new JsonReader
12847  * @param {Object} meta Metadata configuration options
12848  * @param {Object} recordType Either an Array of field definition objects,
12849  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12850  */
12851 Roo.data.JsonReader = function(meta, recordType){
12852     
12853     meta = meta || {};
12854     // set some defaults:
12855     Roo.applyIf(meta, {
12856         totalProperty: 'total',
12857         successProperty : 'success',
12858         root : 'data',
12859         id : 'id'
12860     });
12861     
12862     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12863 };
12864 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12865     
12866     /**
12867      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12868      * Used by Store query builder to append _requestMeta to params.
12869      * 
12870      */
12871     metaFromRemote : false,
12872     /**
12873      * This method is only used by a DataProxy which has retrieved data from a remote server.
12874      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12875      * @return {Object} data A data block which is used by an Roo.data.Store object as
12876      * a cache of Roo.data.Records.
12877      */
12878     read : function(response){
12879         var json = response.responseText;
12880        
12881         var o = /* eval:var:o */ eval("("+json+")");
12882         if(!o) {
12883             throw {message: "JsonReader.read: Json object not found"};
12884         }
12885         
12886         if(o.metaData){
12887             
12888             delete this.ef;
12889             this.metaFromRemote = true;
12890             this.meta = o.metaData;
12891             this.recordType = Roo.data.Record.create(o.metaData.fields);
12892             this.onMetaChange(this.meta, this.recordType, o);
12893         }
12894         return this.readRecords(o);
12895     },
12896
12897     // private function a store will implement
12898     onMetaChange : function(meta, recordType, o){
12899
12900     },
12901
12902     /**
12903          * @ignore
12904          */
12905     simpleAccess: function(obj, subsc) {
12906         return obj[subsc];
12907     },
12908
12909         /**
12910          * @ignore
12911          */
12912     getJsonAccessor: function(){
12913         var re = /[\[\.]/;
12914         return function(expr) {
12915             try {
12916                 return(re.test(expr))
12917                     ? new Function("obj", "return obj." + expr)
12918                     : function(obj){
12919                         return obj[expr];
12920                     };
12921             } catch(e){}
12922             return Roo.emptyFn;
12923         };
12924     }(),
12925
12926     /**
12927      * Create a data block containing Roo.data.Records from an XML document.
12928      * @param {Object} o An object which contains an Array of row objects in the property specified
12929      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12930      * which contains the total size of the dataset.
12931      * @return {Object} data A data block which is used by an Roo.data.Store object as
12932      * a cache of Roo.data.Records.
12933      */
12934     readRecords : function(o){
12935         /**
12936          * After any data loads, the raw JSON data is available for further custom processing.
12937          * @type Object
12938          */
12939         this.o = o;
12940         var s = this.meta, Record = this.recordType,
12941             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12942
12943 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12944         if (!this.ef) {
12945             if(s.totalProperty) {
12946                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12947                 }
12948                 if(s.successProperty) {
12949                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12950                 }
12951                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12952                 if (s.id) {
12953                         var g = this.getJsonAccessor(s.id);
12954                         this.getId = function(rec) {
12955                                 var r = g(rec);  
12956                                 return (r === undefined || r === "") ? null : r;
12957                         };
12958                 } else {
12959                         this.getId = function(){return null;};
12960                 }
12961             this.ef = [];
12962             for(var jj = 0; jj < fl; jj++){
12963                 f = fi[jj];
12964                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12965                 this.ef[jj] = this.getJsonAccessor(map);
12966             }
12967         }
12968
12969         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12970         if(s.totalProperty){
12971             var vt = parseInt(this.getTotal(o), 10);
12972             if(!isNaN(vt)){
12973                 totalRecords = vt;
12974             }
12975         }
12976         if(s.successProperty){
12977             var vs = this.getSuccess(o);
12978             if(vs === false || vs === 'false'){
12979                 success = false;
12980             }
12981         }
12982         var records = [];
12983         for(var i = 0; i < c; i++){
12984                 var n = root[i];
12985             var values = {};
12986             var id = this.getId(n);
12987             for(var j = 0; j < fl; j++){
12988                 f = fi[j];
12989             var v = this.ef[j](n);
12990             if (!f.convert) {
12991                 Roo.log('missing convert for ' + f.name);
12992                 Roo.log(f);
12993                 continue;
12994             }
12995             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12996             }
12997             var record = new Record(values, id);
12998             record.json = n;
12999             records[i] = record;
13000         }
13001         return {
13002             raw : o,
13003             success : success,
13004             records : records,
13005             totalRecords : totalRecords
13006         };
13007     }
13008 });/*
13009  * Based on:
13010  * Ext JS Library 1.1.1
13011  * Copyright(c) 2006-2007, Ext JS, LLC.
13012  *
13013  * Originally Released Under LGPL - original licence link has changed is not relivant.
13014  *
13015  * Fork - LGPL
13016  * <script type="text/javascript">
13017  */
13018
13019 /**
13020  * @class Roo.data.ArrayReader
13021  * @extends Roo.data.DataReader
13022  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13023  * Each element of that Array represents a row of data fields. The
13024  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13025  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13026  * <p>
13027  * Example code:.
13028  * <pre><code>
13029 var RecordDef = Roo.data.Record.create([
13030     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13031     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13032 ]);
13033 var myReader = new Roo.data.ArrayReader({
13034     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13035 }, RecordDef);
13036 </code></pre>
13037  * <p>
13038  * This would consume an Array like this:
13039  * <pre><code>
13040 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13041   </code></pre>
13042  
13043  * @constructor
13044  * Create a new JsonReader
13045  * @param {Object} meta Metadata configuration options.
13046  * @param {Object|Array} recordType Either an Array of field definition objects
13047  * 
13048  * @cfg {Array} fields Array of field definition objects
13049  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13050  * as specified to {@link Roo.data.Record#create},
13051  * or an {@link Roo.data.Record} object
13052  *
13053  * 
13054  * created using {@link Roo.data.Record#create}.
13055  */
13056 Roo.data.ArrayReader = function(meta, recordType){
13057     
13058      
13059     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13060 };
13061
13062 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13063     /**
13064      * Create a data block containing Roo.data.Records from an XML document.
13065      * @param {Object} o An Array of row objects which represents the dataset.
13066      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13067      * a cache of Roo.data.Records.
13068      */
13069     readRecords : function(o){
13070         var sid = this.meta ? this.meta.id : null;
13071         var recordType = this.recordType, fields = recordType.prototype.fields;
13072         var records = [];
13073         var root = o;
13074             for(var i = 0; i < root.length; i++){
13075                     var n = root[i];
13076                 var values = {};
13077                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13078                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13079                 var f = fields.items[j];
13080                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13081                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13082                 v = f.convert(v);
13083                 values[f.name] = v;
13084             }
13085                 var record = new recordType(values, id);
13086                 record.json = n;
13087                 records[records.length] = record;
13088             }
13089             return {
13090                 records : records,
13091                 totalRecords : records.length
13092             };
13093     }
13094 });/*
13095  * - LGPL
13096  * * 
13097  */
13098
13099 /**
13100  * @class Roo.bootstrap.ComboBox
13101  * @extends Roo.bootstrap.TriggerField
13102  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13103  * @cfg {Boolean} append (true|false) default false
13104  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13105  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13106  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13107  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13108  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13109  * @cfg {Boolean} animate default true
13110  * @cfg {Boolean} emptyResultText only for touch device
13111  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13112  * @cfg {String} emptyTitle default ''
13113  * @constructor
13114  * Create a new ComboBox.
13115  * @param {Object} config Configuration options
13116  */
13117 Roo.bootstrap.ComboBox = function(config){
13118     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13119     this.addEvents({
13120         /**
13121          * @event expand
13122          * Fires when the dropdown list is expanded
13123         * @param {Roo.bootstrap.ComboBox} combo This combo box
13124         */
13125         'expand' : true,
13126         /**
13127          * @event collapse
13128          * Fires when the dropdown list is collapsed
13129         * @param {Roo.bootstrap.ComboBox} combo This combo box
13130         */
13131         'collapse' : true,
13132         /**
13133          * @event beforeselect
13134          * Fires before a list item is selected. Return false to cancel the selection.
13135         * @param {Roo.bootstrap.ComboBox} combo This combo box
13136         * @param {Roo.data.Record} record The data record returned from the underlying store
13137         * @param {Number} index The index of the selected item in the dropdown list
13138         */
13139         'beforeselect' : true,
13140         /**
13141          * @event select
13142          * Fires when a list item is selected
13143         * @param {Roo.bootstrap.ComboBox} combo This combo box
13144         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13145         * @param {Number} index The index of the selected item in the dropdown list
13146         */
13147         'select' : true,
13148         /**
13149          * @event beforequery
13150          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13151          * The event object passed has these properties:
13152         * @param {Roo.bootstrap.ComboBox} combo This combo box
13153         * @param {String} query The query
13154         * @param {Boolean} forceAll true to force "all" query
13155         * @param {Boolean} cancel true to cancel the query
13156         * @param {Object} e The query event object
13157         */
13158         'beforequery': true,
13159          /**
13160          * @event add
13161          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13162         * @param {Roo.bootstrap.ComboBox} combo This combo box
13163         */
13164         'add' : true,
13165         /**
13166          * @event edit
13167          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13168         * @param {Roo.bootstrap.ComboBox} combo This combo box
13169         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13170         */
13171         'edit' : true,
13172         /**
13173          * @event remove
13174          * Fires when the remove value from the combobox array
13175         * @param {Roo.bootstrap.ComboBox} combo This combo box
13176         */
13177         'remove' : true,
13178         /**
13179          * @event afterremove
13180          * Fires when the remove value from the combobox array
13181         * @param {Roo.bootstrap.ComboBox} combo This combo box
13182         */
13183         'afterremove' : true,
13184         /**
13185          * @event specialfilter
13186          * Fires when specialfilter
13187             * @param {Roo.bootstrap.ComboBox} combo This combo box
13188             */
13189         'specialfilter' : true,
13190         /**
13191          * @event tick
13192          * Fires when tick the element
13193             * @param {Roo.bootstrap.ComboBox} combo This combo box
13194             */
13195         'tick' : true,
13196         /**
13197          * @event touchviewdisplay
13198          * Fires when touch view require special display (default is using displayField)
13199             * @param {Roo.bootstrap.ComboBox} combo This combo box
13200             * @param {Object} cfg set html .
13201             */
13202         'touchviewdisplay' : true
13203         
13204     });
13205     
13206     this.item = [];
13207     this.tickItems = [];
13208     
13209     this.selectedIndex = -1;
13210     if(this.mode == 'local'){
13211         if(config.queryDelay === undefined){
13212             this.queryDelay = 10;
13213         }
13214         if(config.minChars === undefined){
13215             this.minChars = 0;
13216         }
13217     }
13218 };
13219
13220 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13221      
13222     /**
13223      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13224      * rendering into an Roo.Editor, defaults to false)
13225      */
13226     /**
13227      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13228      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13229      */
13230     /**
13231      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13232      */
13233     /**
13234      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13235      * the dropdown list (defaults to undefined, with no header element)
13236      */
13237
13238      /**
13239      * @cfg {String/Roo.Template} tpl The template to use to render the output
13240      */
13241      
13242      /**
13243      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13244      */
13245     listWidth: undefined,
13246     /**
13247      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13248      * mode = 'remote' or 'text' if mode = 'local')
13249      */
13250     displayField: undefined,
13251     
13252     /**
13253      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13254      * mode = 'remote' or 'value' if mode = 'local'). 
13255      * Note: use of a valueField requires the user make a selection
13256      * in order for a value to be mapped.
13257      */
13258     valueField: undefined,
13259     /**
13260      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13261      */
13262     modalTitle : '',
13263     
13264     /**
13265      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13266      * field's data value (defaults to the underlying DOM element's name)
13267      */
13268     hiddenName: undefined,
13269     /**
13270      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13271      */
13272     listClass: '',
13273     /**
13274      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13275      */
13276     selectedClass: 'active',
13277     
13278     /**
13279      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13280      */
13281     shadow:'sides',
13282     /**
13283      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13284      * anchor positions (defaults to 'tl-bl')
13285      */
13286     listAlign: 'tl-bl?',
13287     /**
13288      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13289      */
13290     maxHeight: 300,
13291     /**
13292      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13293      * query specified by the allQuery config option (defaults to 'query')
13294      */
13295     triggerAction: 'query',
13296     /**
13297      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13298      * (defaults to 4, does not apply if editable = false)
13299      */
13300     minChars : 4,
13301     /**
13302      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13303      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13304      */
13305     typeAhead: false,
13306     /**
13307      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13308      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13309      */
13310     queryDelay: 500,
13311     /**
13312      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13313      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13314      */
13315     pageSize: 0,
13316     /**
13317      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13318      * when editable = true (defaults to false)
13319      */
13320     selectOnFocus:false,
13321     /**
13322      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13323      */
13324     queryParam: 'query',
13325     /**
13326      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13327      * when mode = 'remote' (defaults to 'Loading...')
13328      */
13329     loadingText: 'Loading...',
13330     /**
13331      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13332      */
13333     resizable: false,
13334     /**
13335      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13336      */
13337     handleHeight : 8,
13338     /**
13339      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13340      * traditional select (defaults to true)
13341      */
13342     editable: true,
13343     /**
13344      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13345      */
13346     allQuery: '',
13347     /**
13348      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13349      */
13350     mode: 'remote',
13351     /**
13352      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13353      * listWidth has a higher value)
13354      */
13355     minListWidth : 70,
13356     /**
13357      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13358      * allow the user to set arbitrary text into the field (defaults to false)
13359      */
13360     forceSelection:false,
13361     /**
13362      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13363      * if typeAhead = true (defaults to 250)
13364      */
13365     typeAheadDelay : 250,
13366     /**
13367      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13368      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13369      */
13370     valueNotFoundText : undefined,
13371     /**
13372      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13373      */
13374     blockFocus : false,
13375     
13376     /**
13377      * @cfg {Boolean} disableClear Disable showing of clear button.
13378      */
13379     disableClear : false,
13380     /**
13381      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13382      */
13383     alwaysQuery : false,
13384     
13385     /**
13386      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13387      */
13388     multiple : false,
13389     
13390     /**
13391      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13392      */
13393     invalidClass : "has-warning",
13394     
13395     /**
13396      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13397      */
13398     validClass : "has-success",
13399     
13400     /**
13401      * @cfg {Boolean} specialFilter (true|false) special filter default false
13402      */
13403     specialFilter : false,
13404     
13405     /**
13406      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13407      */
13408     mobileTouchView : true,
13409     
13410     /**
13411      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13412      */
13413     useNativeIOS : false,
13414     
13415     /**
13416      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13417      */
13418     mobile_restrict_height : false,
13419     
13420     ios_options : false,
13421     
13422     //private
13423     addicon : false,
13424     editicon: false,
13425     
13426     page: 0,
13427     hasQuery: false,
13428     append: false,
13429     loadNext: false,
13430     autoFocus : true,
13431     tickable : false,
13432     btnPosition : 'right',
13433     triggerList : true,
13434     showToggleBtn : true,
13435     animate : true,
13436     emptyResultText: 'Empty',
13437     triggerText : 'Select',
13438     emptyTitle : '',
13439     
13440     // element that contains real text value.. (when hidden is used..)
13441     
13442     getAutoCreate : function()
13443     {   
13444         var cfg = false;
13445         //render
13446         /*
13447          * Render classic select for iso
13448          */
13449         
13450         if(Roo.isIOS && this.useNativeIOS){
13451             cfg = this.getAutoCreateNativeIOS();
13452             return cfg;
13453         }
13454         
13455         /*
13456          * Touch Devices
13457          */
13458         
13459         if(Roo.isTouch && this.mobileTouchView){
13460             cfg = this.getAutoCreateTouchView();
13461             return cfg;;
13462         }
13463         
13464         /*
13465          *  Normal ComboBox
13466          */
13467         if(!this.tickable){
13468             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13469             return cfg;
13470         }
13471         
13472         /*
13473          *  ComboBox with tickable selections
13474          */
13475              
13476         var align = this.labelAlign || this.parentLabelAlign();
13477         
13478         cfg = {
13479             cls : 'form-group roo-combobox-tickable' //input-group
13480         };
13481         
13482         var btn_text_select = '';
13483         var btn_text_done = '';
13484         var btn_text_cancel = '';
13485         
13486         if (this.btn_text_show) {
13487             btn_text_select = 'Select';
13488             btn_text_done = 'Done';
13489             btn_text_cancel = 'Cancel'; 
13490         }
13491         
13492         var buttons = {
13493             tag : 'div',
13494             cls : 'tickable-buttons',
13495             cn : [
13496                 {
13497                     tag : 'button',
13498                     type : 'button',
13499                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13500                     //html : this.triggerText
13501                     html: btn_text_select
13502                 },
13503                 {
13504                     tag : 'button',
13505                     type : 'button',
13506                     name : 'ok',
13507                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13508                     //html : 'Done'
13509                     html: btn_text_done
13510                 },
13511                 {
13512                     tag : 'button',
13513                     type : 'button',
13514                     name : 'cancel',
13515                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13516                     //html : 'Cancel'
13517                     html: btn_text_cancel
13518                 }
13519             ]
13520         };
13521         
13522         if(this.editable){
13523             buttons.cn.unshift({
13524                 tag: 'input',
13525                 cls: 'roo-select2-search-field-input'
13526             });
13527         }
13528         
13529         var _this = this;
13530         
13531         Roo.each(buttons.cn, function(c){
13532             if (_this.size) {
13533                 c.cls += ' btn-' + _this.size;
13534             }
13535
13536             if (_this.disabled) {
13537                 c.disabled = true;
13538             }
13539         });
13540         
13541         var box = {
13542             tag: 'div',
13543             style : 'display: contents',
13544             cn: [
13545                 {
13546                     tag: 'input',
13547                     type : 'hidden',
13548                     cls: 'form-hidden-field'
13549                 },
13550                 {
13551                     tag: 'ul',
13552                     cls: 'roo-select2-choices',
13553                     cn:[
13554                         {
13555                             tag: 'li',
13556                             cls: 'roo-select2-search-field',
13557                             cn: [
13558                                 buttons
13559                             ]
13560                         }
13561                     ]
13562                 }
13563             ]
13564         };
13565         
13566         var combobox = {
13567             cls: 'roo-select2-container input-group roo-select2-container-multi',
13568             cn: [
13569                 
13570                 box
13571 //                {
13572 //                    tag: 'ul',
13573 //                    cls: 'typeahead typeahead-long dropdown-menu',
13574 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13575 //                }
13576             ]
13577         };
13578         
13579         if(this.hasFeedback && !this.allowBlank){
13580             
13581             var feedback = {
13582                 tag: 'span',
13583                 cls: 'glyphicon form-control-feedback'
13584             };
13585
13586             combobox.cn.push(feedback);
13587         }
13588         
13589         var indicator = {
13590             tag : 'i',
13591             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13592             tooltip : 'This field is required'
13593         };
13594         if (Roo.bootstrap.version == 4) {
13595             indicator = {
13596                 tag : 'i',
13597                 style : 'display:none'
13598             };
13599         }
13600         if (align ==='left' && this.fieldLabel.length) {
13601             
13602             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13603             
13604             cfg.cn = [
13605                 indicator,
13606                 {
13607                     tag: 'label',
13608                     'for' :  id,
13609                     cls : 'control-label col-form-label',
13610                     html : this.fieldLabel
13611
13612                 },
13613                 {
13614                     cls : "", 
13615                     cn: [
13616                         combobox
13617                     ]
13618                 }
13619
13620             ];
13621             
13622             var labelCfg = cfg.cn[1];
13623             var contentCfg = cfg.cn[2];
13624             
13625
13626             if(this.indicatorpos == 'right'){
13627                 
13628                 cfg.cn = [
13629                     {
13630                         tag: 'label',
13631                         'for' :  id,
13632                         cls : 'control-label col-form-label',
13633                         cn : [
13634                             {
13635                                 tag : 'span',
13636                                 html : this.fieldLabel
13637                             },
13638                             indicator
13639                         ]
13640                     },
13641                     {
13642                         cls : "",
13643                         cn: [
13644                             combobox
13645                         ]
13646                     }
13647
13648                 ];
13649                 
13650                 
13651                 
13652                 labelCfg = cfg.cn[0];
13653                 contentCfg = cfg.cn[1];
13654             
13655             }
13656             
13657             if(this.labelWidth > 12){
13658                 labelCfg.style = "width: " + this.labelWidth + 'px';
13659             }
13660             
13661             if(this.labelWidth < 13 && this.labelmd == 0){
13662                 this.labelmd = this.labelWidth;
13663             }
13664             
13665             if(this.labellg > 0){
13666                 labelCfg.cls += ' col-lg-' + this.labellg;
13667                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13668             }
13669             
13670             if(this.labelmd > 0){
13671                 labelCfg.cls += ' col-md-' + this.labelmd;
13672                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13673             }
13674             
13675             if(this.labelsm > 0){
13676                 labelCfg.cls += ' col-sm-' + this.labelsm;
13677                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13678             }
13679             
13680             if(this.labelxs > 0){
13681                 labelCfg.cls += ' col-xs-' + this.labelxs;
13682                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13683             }
13684                 
13685                 
13686         } else if ( this.fieldLabel.length) {
13687 //                Roo.log(" label");
13688                  cfg.cn = [
13689                    indicator,
13690                     {
13691                         tag: 'label',
13692                         //cls : 'input-group-addon',
13693                         html : this.fieldLabel
13694                     },
13695                     combobox
13696                 ];
13697                 
13698                 if(this.indicatorpos == 'right'){
13699                     cfg.cn = [
13700                         {
13701                             tag: 'label',
13702                             //cls : 'input-group-addon',
13703                             html : this.fieldLabel
13704                         },
13705                         indicator,
13706                         combobox
13707                     ];
13708                     
13709                 }
13710
13711         } else {
13712             
13713 //                Roo.log(" no label && no align");
13714                 cfg = combobox
13715                      
13716                 
13717         }
13718          
13719         var settings=this;
13720         ['xs','sm','md','lg'].map(function(size){
13721             if (settings[size]) {
13722                 cfg.cls += ' col-' + size + '-' + settings[size];
13723             }
13724         });
13725         
13726         return cfg;
13727         
13728     },
13729     
13730     _initEventsCalled : false,
13731     
13732     // private
13733     initEvents: function()
13734     {   
13735         if (this._initEventsCalled) { // as we call render... prevent looping...
13736             return;
13737         }
13738         this._initEventsCalled = true;
13739         
13740         if (!this.store) {
13741             throw "can not find store for combo";
13742         }
13743         
13744         this.indicator = this.indicatorEl();
13745         
13746         this.store = Roo.factory(this.store, Roo.data);
13747         this.store.parent = this;
13748         
13749         // if we are building from html. then this element is so complex, that we can not really
13750         // use the rendered HTML.
13751         // so we have to trash and replace the previous code.
13752         if (Roo.XComponent.build_from_html) {
13753             // remove this element....
13754             var e = this.el.dom, k=0;
13755             while (e ) { e = e.previousSibling;  ++k;}
13756
13757             this.el.remove();
13758             
13759             this.el=false;
13760             this.rendered = false;
13761             
13762             this.render(this.parent().getChildContainer(true), k);
13763         }
13764         
13765         if(Roo.isIOS && this.useNativeIOS){
13766             this.initIOSView();
13767             return;
13768         }
13769         
13770         /*
13771          * Touch Devices
13772          */
13773         
13774         if(Roo.isTouch && this.mobileTouchView){
13775             this.initTouchView();
13776             return;
13777         }
13778         
13779         if(this.tickable){
13780             this.initTickableEvents();
13781             return;
13782         }
13783         
13784         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13785         
13786         if(this.hiddenName){
13787             
13788             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13789             
13790             this.hiddenField.dom.value =
13791                 this.hiddenValue !== undefined ? this.hiddenValue :
13792                 this.value !== undefined ? this.value : '';
13793
13794             // prevent input submission
13795             this.el.dom.removeAttribute('name');
13796             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13797              
13798              
13799         }
13800         //if(Roo.isGecko){
13801         //    this.el.dom.setAttribute('autocomplete', 'off');
13802         //}
13803         
13804         var cls = 'x-combo-list';
13805         
13806         //this.list = new Roo.Layer({
13807         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13808         //});
13809         
13810         var _this = this;
13811         
13812         (function(){
13813             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13814             _this.list.setWidth(lw);
13815         }).defer(100);
13816         
13817         this.list.on('mouseover', this.onViewOver, this);
13818         this.list.on('mousemove', this.onViewMove, this);
13819         this.list.on('scroll', this.onViewScroll, this);
13820         
13821         /*
13822         this.list.swallowEvent('mousewheel');
13823         this.assetHeight = 0;
13824
13825         if(this.title){
13826             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13827             this.assetHeight += this.header.getHeight();
13828         }
13829
13830         this.innerList = this.list.createChild({cls:cls+'-inner'});
13831         this.innerList.on('mouseover', this.onViewOver, this);
13832         this.innerList.on('mousemove', this.onViewMove, this);
13833         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13834         
13835         if(this.allowBlank && !this.pageSize && !this.disableClear){
13836             this.footer = this.list.createChild({cls:cls+'-ft'});
13837             this.pageTb = new Roo.Toolbar(this.footer);
13838            
13839         }
13840         if(this.pageSize){
13841             this.footer = this.list.createChild({cls:cls+'-ft'});
13842             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13843                     {pageSize: this.pageSize});
13844             
13845         }
13846         
13847         if (this.pageTb && this.allowBlank && !this.disableClear) {
13848             var _this = this;
13849             this.pageTb.add(new Roo.Toolbar.Fill(), {
13850                 cls: 'x-btn-icon x-btn-clear',
13851                 text: '&#160;',
13852                 handler: function()
13853                 {
13854                     _this.collapse();
13855                     _this.clearValue();
13856                     _this.onSelect(false, -1);
13857                 }
13858             });
13859         }
13860         if (this.footer) {
13861             this.assetHeight += this.footer.getHeight();
13862         }
13863         */
13864             
13865         if(!this.tpl){
13866             this.tpl = Roo.bootstrap.version == 4 ?
13867                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13868                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13869         }
13870
13871         this.view = new Roo.View(this.list, this.tpl, {
13872             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13873         });
13874         //this.view.wrapEl.setDisplayed(false);
13875         this.view.on('click', this.onViewClick, this);
13876         
13877         
13878         this.store.on('beforeload', this.onBeforeLoad, this);
13879         this.store.on('load', this.onLoad, this);
13880         this.store.on('loadexception', this.onLoadException, this);
13881         /*
13882         if(this.resizable){
13883             this.resizer = new Roo.Resizable(this.list,  {
13884                pinned:true, handles:'se'
13885             });
13886             this.resizer.on('resize', function(r, w, h){
13887                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13888                 this.listWidth = w;
13889                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13890                 this.restrictHeight();
13891             }, this);
13892             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13893         }
13894         */
13895         if(!this.editable){
13896             this.editable = true;
13897             this.setEditable(false);
13898         }
13899         
13900         /*
13901         
13902         if (typeof(this.events.add.listeners) != 'undefined') {
13903             
13904             this.addicon = this.wrap.createChild(
13905                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13906        
13907             this.addicon.on('click', function(e) {
13908                 this.fireEvent('add', this);
13909             }, this);
13910         }
13911         if (typeof(this.events.edit.listeners) != 'undefined') {
13912             
13913             this.editicon = this.wrap.createChild(
13914                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13915             if (this.addicon) {
13916                 this.editicon.setStyle('margin-left', '40px');
13917             }
13918             this.editicon.on('click', function(e) {
13919                 
13920                 // we fire even  if inothing is selected..
13921                 this.fireEvent('edit', this, this.lastData );
13922                 
13923             }, this);
13924         }
13925         */
13926         
13927         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13928             "up" : function(e){
13929                 this.inKeyMode = true;
13930                 this.selectPrev();
13931             },
13932
13933             "down" : function(e){
13934                 if(!this.isExpanded()){
13935                     this.onTriggerClick();
13936                 }else{
13937                     this.inKeyMode = true;
13938                     this.selectNext();
13939                 }
13940             },
13941
13942             "enter" : function(e){
13943 //                this.onViewClick();
13944                 //return true;
13945                 this.collapse();
13946                 
13947                 if(this.fireEvent("specialkey", this, e)){
13948                     this.onViewClick(false);
13949                 }
13950                 
13951                 return true;
13952             },
13953
13954             "esc" : function(e){
13955                 this.collapse();
13956             },
13957
13958             "tab" : function(e){
13959                 this.collapse();
13960                 
13961                 if(this.fireEvent("specialkey", this, e)){
13962                     this.onViewClick(false);
13963                 }
13964                 
13965                 return true;
13966             },
13967
13968             scope : this,
13969
13970             doRelay : function(foo, bar, hname){
13971                 if(hname == 'down' || this.scope.isExpanded()){
13972                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13973                 }
13974                 return true;
13975             },
13976
13977             forceKeyDown: true
13978         });
13979         
13980         
13981         this.queryDelay = Math.max(this.queryDelay || 10,
13982                 this.mode == 'local' ? 10 : 250);
13983         
13984         
13985         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13986         
13987         if(this.typeAhead){
13988             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13989         }
13990         if(this.editable !== false){
13991             this.inputEl().on("keyup", this.onKeyUp, this);
13992         }
13993         if(this.forceSelection){
13994             this.inputEl().on('blur', this.doForce, this);
13995         }
13996         
13997         if(this.multiple){
13998             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13999             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14000         }
14001     },
14002     
14003     initTickableEvents: function()
14004     {   
14005         this.createList();
14006         
14007         if(this.hiddenName){
14008             
14009             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14010             
14011             this.hiddenField.dom.value =
14012                 this.hiddenValue !== undefined ? this.hiddenValue :
14013                 this.value !== undefined ? this.value : '';
14014
14015             // prevent input submission
14016             this.el.dom.removeAttribute('name');
14017             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14018              
14019              
14020         }
14021         
14022 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14023         
14024         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14025         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14026         if(this.triggerList){
14027             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14028         }
14029          
14030         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14031         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14032         
14033         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14034         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14035         
14036         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14037         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14038         
14039         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14040         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14041         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14042         
14043         this.okBtn.hide();
14044         this.cancelBtn.hide();
14045         
14046         var _this = this;
14047         
14048         (function(){
14049             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14050             _this.list.setWidth(lw);
14051         }).defer(100);
14052         
14053         this.list.on('mouseover', this.onViewOver, this);
14054         this.list.on('mousemove', this.onViewMove, this);
14055         
14056         this.list.on('scroll', this.onViewScroll, this);
14057         
14058         if(!this.tpl){
14059             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14060                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14061         }
14062
14063         this.view = new Roo.View(this.list, this.tpl, {
14064             singleSelect:true,
14065             tickable:true,
14066             parent:this,
14067             store: this.store,
14068             selectedClass: this.selectedClass
14069         });
14070         
14071         //this.view.wrapEl.setDisplayed(false);
14072         this.view.on('click', this.onViewClick, this);
14073         
14074         
14075         
14076         this.store.on('beforeload', this.onBeforeLoad, this);
14077         this.store.on('load', this.onLoad, this);
14078         this.store.on('loadexception', this.onLoadException, this);
14079         
14080         if(this.editable){
14081             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14082                 "up" : function(e){
14083                     this.inKeyMode = true;
14084                     this.selectPrev();
14085                 },
14086
14087                 "down" : function(e){
14088                     this.inKeyMode = true;
14089                     this.selectNext();
14090                 },
14091
14092                 "enter" : function(e){
14093                     if(this.fireEvent("specialkey", this, e)){
14094                         this.onViewClick(false);
14095                     }
14096                     
14097                     return true;
14098                 },
14099
14100                 "esc" : function(e){
14101                     this.onTickableFooterButtonClick(e, false, false);
14102                 },
14103
14104                 "tab" : function(e){
14105                     this.fireEvent("specialkey", this, e);
14106                     
14107                     this.onTickableFooterButtonClick(e, false, false);
14108                     
14109                     return true;
14110                 },
14111
14112                 scope : this,
14113
14114                 doRelay : function(e, fn, key){
14115                     if(this.scope.isExpanded()){
14116                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14117                     }
14118                     return true;
14119                 },
14120
14121                 forceKeyDown: true
14122             });
14123         }
14124         
14125         this.queryDelay = Math.max(this.queryDelay || 10,
14126                 this.mode == 'local' ? 10 : 250);
14127         
14128         
14129         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14130         
14131         if(this.typeAhead){
14132             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14133         }
14134         
14135         if(this.editable !== false){
14136             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14137         }
14138         
14139         this.indicator = this.indicatorEl();
14140         
14141         if(this.indicator){
14142             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14143             this.indicator.hide();
14144         }
14145         
14146     },
14147
14148     onDestroy : function(){
14149         if(this.view){
14150             this.view.setStore(null);
14151             this.view.el.removeAllListeners();
14152             this.view.el.remove();
14153             this.view.purgeListeners();
14154         }
14155         if(this.list){
14156             this.list.dom.innerHTML  = '';
14157         }
14158         
14159         if(this.store){
14160             this.store.un('beforeload', this.onBeforeLoad, this);
14161             this.store.un('load', this.onLoad, this);
14162             this.store.un('loadexception', this.onLoadException, this);
14163         }
14164         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14165     },
14166
14167     // private
14168     fireKey : function(e){
14169         if(e.isNavKeyPress() && !this.list.isVisible()){
14170             this.fireEvent("specialkey", this, e);
14171         }
14172     },
14173
14174     // private
14175     onResize: function(w, h){
14176 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14177 //        
14178 //        if(typeof w != 'number'){
14179 //            // we do not handle it!?!?
14180 //            return;
14181 //        }
14182 //        var tw = this.trigger.getWidth();
14183 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14184 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14185 //        var x = w - tw;
14186 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14187 //            
14188 //        //this.trigger.setStyle('left', x+'px');
14189 //        
14190 //        if(this.list && this.listWidth === undefined){
14191 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14192 //            this.list.setWidth(lw);
14193 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14194 //        }
14195         
14196     
14197         
14198     },
14199
14200     /**
14201      * Allow or prevent the user from directly editing the field text.  If false is passed,
14202      * the user will only be able to select from the items defined in the dropdown list.  This method
14203      * is the runtime equivalent of setting the 'editable' config option at config time.
14204      * @param {Boolean} value True to allow the user to directly edit the field text
14205      */
14206     setEditable : function(value){
14207         if(value == this.editable){
14208             return;
14209         }
14210         this.editable = value;
14211         if(!value){
14212             this.inputEl().dom.setAttribute('readOnly', true);
14213             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14214             this.inputEl().addClass('x-combo-noedit');
14215         }else{
14216             this.inputEl().dom.setAttribute('readOnly', false);
14217             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14218             this.inputEl().removeClass('x-combo-noedit');
14219         }
14220     },
14221
14222     // private
14223     
14224     onBeforeLoad : function(combo,opts){
14225         if(!this.hasFocus){
14226             return;
14227         }
14228          if (!opts.add) {
14229             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14230          }
14231         this.restrictHeight();
14232         this.selectedIndex = -1;
14233     },
14234
14235     // private
14236     onLoad : function(){
14237         
14238         this.hasQuery = false;
14239         
14240         if(!this.hasFocus){
14241             return;
14242         }
14243         
14244         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14245             this.loading.hide();
14246         }
14247         
14248         if(this.store.getCount() > 0){
14249             
14250             this.expand();
14251             this.restrictHeight();
14252             if(this.lastQuery == this.allQuery){
14253                 if(this.editable && !this.tickable){
14254                     this.inputEl().dom.select();
14255                 }
14256                 
14257                 if(
14258                     !this.selectByValue(this.value, true) &&
14259                     this.autoFocus && 
14260                     (
14261                         !this.store.lastOptions ||
14262                         typeof(this.store.lastOptions.add) == 'undefined' || 
14263                         this.store.lastOptions.add != true
14264                     )
14265                 ){
14266                     this.select(0, true);
14267                 }
14268             }else{
14269                 if(this.autoFocus){
14270                     this.selectNext();
14271                 }
14272                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14273                     this.taTask.delay(this.typeAheadDelay);
14274                 }
14275             }
14276         }else{
14277             this.onEmptyResults();
14278         }
14279         
14280         //this.el.focus();
14281     },
14282     // private
14283     onLoadException : function()
14284     {
14285         this.hasQuery = false;
14286         
14287         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14288             this.loading.hide();
14289         }
14290         
14291         if(this.tickable && this.editable){
14292             return;
14293         }
14294         
14295         this.collapse();
14296         // only causes errors at present
14297         //Roo.log(this.store.reader.jsonData);
14298         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14299             // fixme
14300             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14301         //}
14302         
14303         
14304     },
14305     // private
14306     onTypeAhead : function(){
14307         if(this.store.getCount() > 0){
14308             var r = this.store.getAt(0);
14309             var newValue = r.data[this.displayField];
14310             var len = newValue.length;
14311             var selStart = this.getRawValue().length;
14312             
14313             if(selStart != len){
14314                 this.setRawValue(newValue);
14315                 this.selectText(selStart, newValue.length);
14316             }
14317         }
14318     },
14319
14320     // private
14321     onSelect : function(record, index){
14322         
14323         if(this.fireEvent('beforeselect', this, record, index) !== false){
14324         
14325             this.setFromData(index > -1 ? record.data : false);
14326             
14327             this.collapse();
14328             this.fireEvent('select', this, record, index);
14329         }
14330     },
14331
14332     /**
14333      * Returns the currently selected field value or empty string if no value is set.
14334      * @return {String} value The selected value
14335      */
14336     getValue : function()
14337     {
14338         if(Roo.isIOS && this.useNativeIOS){
14339             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14340         }
14341         
14342         if(this.multiple){
14343             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14344         }
14345         
14346         if(this.valueField){
14347             return typeof this.value != 'undefined' ? this.value : '';
14348         }else{
14349             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14350         }
14351     },
14352     
14353     getRawValue : function()
14354     {
14355         if(Roo.isIOS && this.useNativeIOS){
14356             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14357         }
14358         
14359         var v = this.inputEl().getValue();
14360         
14361         return v;
14362     },
14363
14364     /**
14365      * Clears any text/value currently set in the field
14366      */
14367     clearValue : function(){
14368         
14369         if(this.hiddenField){
14370             this.hiddenField.dom.value = '';
14371         }
14372         this.value = '';
14373         this.setRawValue('');
14374         this.lastSelectionText = '';
14375         this.lastData = false;
14376         
14377         var close = this.closeTriggerEl();
14378         
14379         if(close){
14380             close.hide();
14381         }
14382         
14383         this.validate();
14384         
14385     },
14386
14387     /**
14388      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14389      * will be displayed in the field.  If the value does not match the data value of an existing item,
14390      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14391      * Otherwise the field will be blank (although the value will still be set).
14392      * @param {String} value The value to match
14393      */
14394     setValue : function(v)
14395     {
14396         if(Roo.isIOS && this.useNativeIOS){
14397             this.setIOSValue(v);
14398             return;
14399         }
14400         
14401         if(this.multiple){
14402             this.syncValue();
14403             return;
14404         }
14405         
14406         var text = v;
14407         if(this.valueField){
14408             var r = this.findRecord(this.valueField, v);
14409             if(r){
14410                 text = r.data[this.displayField];
14411             }else if(this.valueNotFoundText !== undefined){
14412                 text = this.valueNotFoundText;
14413             }
14414         }
14415         this.lastSelectionText = text;
14416         if(this.hiddenField){
14417             this.hiddenField.dom.value = v;
14418         }
14419         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14420         this.value = v;
14421         
14422         var close = this.closeTriggerEl();
14423         
14424         if(close){
14425             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14426         }
14427         
14428         this.validate();
14429     },
14430     /**
14431      * @property {Object} the last set data for the element
14432      */
14433     
14434     lastData : false,
14435     /**
14436      * Sets the value of the field based on a object which is related to the record format for the store.
14437      * @param {Object} value the value to set as. or false on reset?
14438      */
14439     setFromData : function(o){
14440         
14441         if(this.multiple){
14442             this.addItem(o);
14443             return;
14444         }
14445             
14446         var dv = ''; // display value
14447         var vv = ''; // value value..
14448         this.lastData = o;
14449         if (this.displayField) {
14450             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14451         } else {
14452             // this is an error condition!!!
14453             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14454         }
14455         
14456         if(this.valueField){
14457             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14458         }
14459         
14460         var close = this.closeTriggerEl();
14461         
14462         if(close){
14463             if(dv.length || vv * 1 > 0){
14464                 close.show() ;
14465                 this.blockFocus=true;
14466             } else {
14467                 close.hide();
14468             }             
14469         }
14470         
14471         if(this.hiddenField){
14472             this.hiddenField.dom.value = vv;
14473             
14474             this.lastSelectionText = dv;
14475             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14476             this.value = vv;
14477             return;
14478         }
14479         // no hidden field.. - we store the value in 'value', but still display
14480         // display field!!!!
14481         this.lastSelectionText = dv;
14482         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14483         this.value = vv;
14484         
14485         
14486         
14487     },
14488     // private
14489     reset : function(){
14490         // overridden so that last data is reset..
14491         
14492         if(this.multiple){
14493             this.clearItem();
14494             return;
14495         }
14496         
14497         this.setValue(this.originalValue);
14498         //this.clearInvalid();
14499         this.lastData = false;
14500         if (this.view) {
14501             this.view.clearSelections();
14502         }
14503         
14504         this.validate();
14505     },
14506     // private
14507     findRecord : function(prop, value){
14508         var record;
14509         if(this.store.getCount() > 0){
14510             this.store.each(function(r){
14511                 if(r.data[prop] == value){
14512                     record = r;
14513                     return false;
14514                 }
14515                 return true;
14516             });
14517         }
14518         return record;
14519     },
14520     
14521     getName: function()
14522     {
14523         // returns hidden if it's set..
14524         if (!this.rendered) {return ''};
14525         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14526         
14527     },
14528     // private
14529     onViewMove : function(e, t){
14530         this.inKeyMode = false;
14531     },
14532
14533     // private
14534     onViewOver : function(e, t){
14535         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14536             return;
14537         }
14538         var item = this.view.findItemFromChild(t);
14539         
14540         if(item){
14541             var index = this.view.indexOf(item);
14542             this.select(index, false);
14543         }
14544     },
14545
14546     // private
14547     onViewClick : function(view, doFocus, el, e)
14548     {
14549         var index = this.view.getSelectedIndexes()[0];
14550         
14551         var r = this.store.getAt(index);
14552         
14553         if(this.tickable){
14554             
14555             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14556                 return;
14557             }
14558             
14559             var rm = false;
14560             var _this = this;
14561             
14562             Roo.each(this.tickItems, function(v,k){
14563                 
14564                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14565                     Roo.log(v);
14566                     _this.tickItems.splice(k, 1);
14567                     
14568                     if(typeof(e) == 'undefined' && view == false){
14569                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14570                     }
14571                     
14572                     rm = true;
14573                     return;
14574                 }
14575             });
14576             
14577             if(rm){
14578                 return;
14579             }
14580             
14581             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14582                 this.tickItems.push(r.data);
14583             }
14584             
14585             if(typeof(e) == 'undefined' && view == false){
14586                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14587             }
14588                     
14589             return;
14590         }
14591         
14592         if(r){
14593             this.onSelect(r, index);
14594         }
14595         if(doFocus !== false && !this.blockFocus){
14596             this.inputEl().focus();
14597         }
14598     },
14599
14600     // private
14601     restrictHeight : function(){
14602         //this.innerList.dom.style.height = '';
14603         //var inner = this.innerList.dom;
14604         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14605         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14606         //this.list.beginUpdate();
14607         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14608         this.list.alignTo(this.inputEl(), this.listAlign);
14609         this.list.alignTo(this.inputEl(), this.listAlign);
14610         //this.list.endUpdate();
14611     },
14612
14613     // private
14614     onEmptyResults : function(){
14615         
14616         if(this.tickable && this.editable){
14617             this.hasFocus = false;
14618             this.restrictHeight();
14619             return;
14620         }
14621         
14622         this.collapse();
14623     },
14624
14625     /**
14626      * Returns true if the dropdown list is expanded, else false.
14627      */
14628     isExpanded : function(){
14629         return this.list.isVisible();
14630     },
14631
14632     /**
14633      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14634      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14635      * @param {String} value The data value of the item to select
14636      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14637      * selected item if it is not currently in view (defaults to true)
14638      * @return {Boolean} True if the value matched an item in the list, else false
14639      */
14640     selectByValue : function(v, scrollIntoView){
14641         if(v !== undefined && v !== null){
14642             var r = this.findRecord(this.valueField || this.displayField, v);
14643             if(r){
14644                 this.select(this.store.indexOf(r), scrollIntoView);
14645                 return true;
14646             }
14647         }
14648         return false;
14649     },
14650
14651     /**
14652      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14653      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14654      * @param {Number} index The zero-based index of the list item to select
14655      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14656      * selected item if it is not currently in view (defaults to true)
14657      */
14658     select : function(index, scrollIntoView){
14659         this.selectedIndex = index;
14660         this.view.select(index);
14661         if(scrollIntoView !== false){
14662             var el = this.view.getNode(index);
14663             /*
14664              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14665              */
14666             if(el){
14667                 this.list.scrollChildIntoView(el, false);
14668             }
14669         }
14670     },
14671
14672     // private
14673     selectNext : function(){
14674         var ct = this.store.getCount();
14675         if(ct > 0){
14676             if(this.selectedIndex == -1){
14677                 this.select(0);
14678             }else if(this.selectedIndex < ct-1){
14679                 this.select(this.selectedIndex+1);
14680             }
14681         }
14682     },
14683
14684     // private
14685     selectPrev : function(){
14686         var ct = this.store.getCount();
14687         if(ct > 0){
14688             if(this.selectedIndex == -1){
14689                 this.select(0);
14690             }else if(this.selectedIndex != 0){
14691                 this.select(this.selectedIndex-1);
14692             }
14693         }
14694     },
14695
14696     // private
14697     onKeyUp : function(e){
14698         if(this.editable !== false && !e.isSpecialKey()){
14699             this.lastKey = e.getKey();
14700             this.dqTask.delay(this.queryDelay);
14701         }
14702     },
14703
14704     // private
14705     validateBlur : function(){
14706         return !this.list || !this.list.isVisible();   
14707     },
14708
14709     // private
14710     initQuery : function(){
14711         
14712         var v = this.getRawValue();
14713         
14714         if(this.tickable && this.editable){
14715             v = this.tickableInputEl().getValue();
14716         }
14717         
14718         this.doQuery(v);
14719     },
14720
14721     // private
14722     doForce : function(){
14723         if(this.inputEl().dom.value.length > 0){
14724             this.inputEl().dom.value =
14725                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14726              
14727         }
14728     },
14729
14730     /**
14731      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14732      * query allowing the query action to be canceled if needed.
14733      * @param {String} query The SQL query to execute
14734      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14735      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14736      * saved in the current store (defaults to false)
14737      */
14738     doQuery : function(q, forceAll){
14739         
14740         if(q === undefined || q === null){
14741             q = '';
14742         }
14743         var qe = {
14744             query: q,
14745             forceAll: forceAll,
14746             combo: this,
14747             cancel:false
14748         };
14749         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14750             return false;
14751         }
14752         q = qe.query;
14753         
14754         forceAll = qe.forceAll;
14755         if(forceAll === true || (q.length >= this.minChars)){
14756             
14757             this.hasQuery = true;
14758             
14759             if(this.lastQuery != q || this.alwaysQuery){
14760                 this.lastQuery = q;
14761                 if(this.mode == 'local'){
14762                     this.selectedIndex = -1;
14763                     if(forceAll){
14764                         this.store.clearFilter();
14765                     }else{
14766                         
14767                         if(this.specialFilter){
14768                             this.fireEvent('specialfilter', this);
14769                             this.onLoad();
14770                             return;
14771                         }
14772                         
14773                         this.store.filter(this.displayField, q);
14774                     }
14775                     
14776                     this.store.fireEvent("datachanged", this.store);
14777                     
14778                     this.onLoad();
14779                     
14780                     
14781                 }else{
14782                     
14783                     this.store.baseParams[this.queryParam] = q;
14784                     
14785                     var options = {params : this.getParams(q)};
14786                     
14787                     if(this.loadNext){
14788                         options.add = true;
14789                         options.params.start = this.page * this.pageSize;
14790                     }
14791                     
14792                     this.store.load(options);
14793                     
14794                     /*
14795                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14796                      *  we should expand the list on onLoad
14797                      *  so command out it
14798                      */
14799 //                    this.expand();
14800                 }
14801             }else{
14802                 this.selectedIndex = -1;
14803                 this.onLoad();   
14804             }
14805         }
14806         
14807         this.loadNext = false;
14808     },
14809     
14810     // private
14811     getParams : function(q){
14812         var p = {};
14813         //p[this.queryParam] = q;
14814         
14815         if(this.pageSize){
14816             p.start = 0;
14817             p.limit = this.pageSize;
14818         }
14819         return p;
14820     },
14821
14822     /**
14823      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14824      */
14825     collapse : function(){
14826         if(!this.isExpanded()){
14827             return;
14828         }
14829         
14830         this.list.hide();
14831         
14832         this.hasFocus = false;
14833         
14834         if(this.tickable){
14835             this.okBtn.hide();
14836             this.cancelBtn.hide();
14837             this.trigger.show();
14838             
14839             if(this.editable){
14840                 this.tickableInputEl().dom.value = '';
14841                 this.tickableInputEl().blur();
14842             }
14843             
14844         }
14845         
14846         Roo.get(document).un('mousedown', this.collapseIf, this);
14847         Roo.get(document).un('mousewheel', this.collapseIf, this);
14848         if (!this.editable) {
14849             Roo.get(document).un('keydown', this.listKeyPress, this);
14850         }
14851         this.fireEvent('collapse', this);
14852         
14853         this.validate();
14854     },
14855
14856     // private
14857     collapseIf : function(e){
14858         var in_combo  = e.within(this.el);
14859         var in_list =  e.within(this.list);
14860         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14861         
14862         if (in_combo || in_list || is_list) {
14863             //e.stopPropagation();
14864             return;
14865         }
14866         
14867         if(this.tickable){
14868             this.onTickableFooterButtonClick(e, false, false);
14869         }
14870
14871         this.collapse();
14872         
14873     },
14874
14875     /**
14876      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14877      */
14878     expand : function(){
14879        
14880         if(this.isExpanded() || !this.hasFocus){
14881             return;
14882         }
14883         
14884         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14885         this.list.setWidth(lw);
14886         
14887         Roo.log('expand');
14888         
14889         this.list.show();
14890         
14891         this.restrictHeight();
14892         
14893         if(this.tickable){
14894             
14895             this.tickItems = Roo.apply([], this.item);
14896             
14897             this.okBtn.show();
14898             this.cancelBtn.show();
14899             this.trigger.hide();
14900             
14901             if(this.editable){
14902                 this.tickableInputEl().focus();
14903             }
14904             
14905         }
14906         
14907         Roo.get(document).on('mousedown', this.collapseIf, this);
14908         Roo.get(document).on('mousewheel', this.collapseIf, this);
14909         if (!this.editable) {
14910             Roo.get(document).on('keydown', this.listKeyPress, this);
14911         }
14912         
14913         this.fireEvent('expand', this);
14914     },
14915
14916     // private
14917     // Implements the default empty TriggerField.onTriggerClick function
14918     onTriggerClick : function(e)
14919     {
14920         Roo.log('trigger click');
14921         
14922         if(this.disabled || !this.triggerList){
14923             return;
14924         }
14925         
14926         this.page = 0;
14927         this.loadNext = false;
14928         
14929         if(this.isExpanded()){
14930             this.collapse();
14931             if (!this.blockFocus) {
14932                 this.inputEl().focus();
14933             }
14934             
14935         }else {
14936             this.hasFocus = true;
14937             if(this.triggerAction == 'all') {
14938                 this.doQuery(this.allQuery, true);
14939             } else {
14940                 this.doQuery(this.getRawValue());
14941             }
14942             if (!this.blockFocus) {
14943                 this.inputEl().focus();
14944             }
14945         }
14946     },
14947     
14948     onTickableTriggerClick : function(e)
14949     {
14950         if(this.disabled){
14951             return;
14952         }
14953         
14954         this.page = 0;
14955         this.loadNext = false;
14956         this.hasFocus = true;
14957         
14958         if(this.triggerAction == 'all') {
14959             this.doQuery(this.allQuery, true);
14960         } else {
14961             this.doQuery(this.getRawValue());
14962         }
14963     },
14964     
14965     onSearchFieldClick : function(e)
14966     {
14967         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14968             this.onTickableFooterButtonClick(e, false, false);
14969             return;
14970         }
14971         
14972         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14973             return;
14974         }
14975         
14976         this.page = 0;
14977         this.loadNext = false;
14978         this.hasFocus = true;
14979         
14980         if(this.triggerAction == 'all') {
14981             this.doQuery(this.allQuery, true);
14982         } else {
14983             this.doQuery(this.getRawValue());
14984         }
14985     },
14986     
14987     listKeyPress : function(e)
14988     {
14989         //Roo.log('listkeypress');
14990         // scroll to first matching element based on key pres..
14991         if (e.isSpecialKey()) {
14992             return false;
14993         }
14994         var k = String.fromCharCode(e.getKey()).toUpperCase();
14995         //Roo.log(k);
14996         var match  = false;
14997         var csel = this.view.getSelectedNodes();
14998         var cselitem = false;
14999         if (csel.length) {
15000             var ix = this.view.indexOf(csel[0]);
15001             cselitem  = this.store.getAt(ix);
15002             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15003                 cselitem = false;
15004             }
15005             
15006         }
15007         
15008         this.store.each(function(v) { 
15009             if (cselitem) {
15010                 // start at existing selection.
15011                 if (cselitem.id == v.id) {
15012                     cselitem = false;
15013                 }
15014                 return true;
15015             }
15016                 
15017             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15018                 match = this.store.indexOf(v);
15019                 return false;
15020             }
15021             return true;
15022         }, this);
15023         
15024         if (match === false) {
15025             return true; // no more action?
15026         }
15027         // scroll to?
15028         this.view.select(match);
15029         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15030         sn.scrollIntoView(sn.dom.parentNode, false);
15031     },
15032     
15033     onViewScroll : function(e, t){
15034         
15035         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){
15036             return;
15037         }
15038         
15039         this.hasQuery = true;
15040         
15041         this.loading = this.list.select('.loading', true).first();
15042         
15043         if(this.loading === null){
15044             this.list.createChild({
15045                 tag: 'div',
15046                 cls: 'loading roo-select2-more-results roo-select2-active',
15047                 html: 'Loading more results...'
15048             });
15049             
15050             this.loading = this.list.select('.loading', true).first();
15051             
15052             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15053             
15054             this.loading.hide();
15055         }
15056         
15057         this.loading.show();
15058         
15059         var _combo = this;
15060         
15061         this.page++;
15062         this.loadNext = true;
15063         
15064         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15065         
15066         return;
15067     },
15068     
15069     addItem : function(o)
15070     {   
15071         var dv = ''; // display value
15072         
15073         if (this.displayField) {
15074             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15075         } else {
15076             // this is an error condition!!!
15077             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15078         }
15079         
15080         if(!dv.length){
15081             return;
15082         }
15083         
15084         var choice = this.choices.createChild({
15085             tag: 'li',
15086             cls: 'roo-select2-search-choice',
15087             cn: [
15088                 {
15089                     tag: 'div',
15090                     html: dv
15091                 },
15092                 {
15093                     tag: 'a',
15094                     href: '#',
15095                     cls: 'roo-select2-search-choice-close fa fa-times',
15096                     tabindex: '-1'
15097                 }
15098             ]
15099             
15100         }, this.searchField);
15101         
15102         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15103         
15104         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15105         
15106         this.item.push(o);
15107         
15108         this.lastData = o;
15109         
15110         this.syncValue();
15111         
15112         this.inputEl().dom.value = '';
15113         
15114         this.validate();
15115     },
15116     
15117     onRemoveItem : function(e, _self, o)
15118     {
15119         e.preventDefault();
15120         
15121         this.lastItem = Roo.apply([], this.item);
15122         
15123         var index = this.item.indexOf(o.data) * 1;
15124         
15125         if( index < 0){
15126             Roo.log('not this item?!');
15127             return;
15128         }
15129         
15130         this.item.splice(index, 1);
15131         o.item.remove();
15132         
15133         this.syncValue();
15134         
15135         this.fireEvent('remove', this, e);
15136         
15137         this.validate();
15138         
15139     },
15140     
15141     syncValue : function()
15142     {
15143         if(!this.item.length){
15144             this.clearValue();
15145             return;
15146         }
15147             
15148         var value = [];
15149         var _this = this;
15150         Roo.each(this.item, function(i){
15151             if(_this.valueField){
15152                 value.push(i[_this.valueField]);
15153                 return;
15154             }
15155
15156             value.push(i);
15157         });
15158
15159         this.value = value.join(',');
15160
15161         if(this.hiddenField){
15162             this.hiddenField.dom.value = this.value;
15163         }
15164         
15165         this.store.fireEvent("datachanged", this.store);
15166         
15167         this.validate();
15168     },
15169     
15170     clearItem : function()
15171     {
15172         if(!this.multiple){
15173             return;
15174         }
15175         
15176         this.item = [];
15177         
15178         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15179            c.remove();
15180         });
15181         
15182         this.syncValue();
15183         
15184         this.validate();
15185         
15186         if(this.tickable && !Roo.isTouch){
15187             this.view.refresh();
15188         }
15189     },
15190     
15191     inputEl: function ()
15192     {
15193         if(Roo.isIOS && this.useNativeIOS){
15194             return this.el.select('select.roo-ios-select', true).first();
15195         }
15196         
15197         if(Roo.isTouch && this.mobileTouchView){
15198             return this.el.select('input.form-control',true).first();
15199         }
15200         
15201         if(this.tickable){
15202             return this.searchField;
15203         }
15204         
15205         return this.el.select('input.form-control',true).first();
15206     },
15207     
15208     onTickableFooterButtonClick : function(e, btn, el)
15209     {
15210         e.preventDefault();
15211         
15212         this.lastItem = Roo.apply([], this.item);
15213         
15214         if(btn && btn.name == 'cancel'){
15215             this.tickItems = Roo.apply([], this.item);
15216             this.collapse();
15217             return;
15218         }
15219         
15220         this.clearItem();
15221         
15222         var _this = this;
15223         
15224         Roo.each(this.tickItems, function(o){
15225             _this.addItem(o);
15226         });
15227         
15228         this.collapse();
15229         
15230     },
15231     
15232     validate : function()
15233     {
15234         if(this.getVisibilityEl().hasClass('hidden')){
15235             return true;
15236         }
15237         
15238         var v = this.getRawValue();
15239         
15240         if(this.multiple){
15241             v = this.getValue();
15242         }
15243         
15244         if(this.disabled || this.allowBlank || v.length){
15245             this.markValid();
15246             return true;
15247         }
15248         
15249         this.markInvalid();
15250         return false;
15251     },
15252     
15253     tickableInputEl : function()
15254     {
15255         if(!this.tickable || !this.editable){
15256             return this.inputEl();
15257         }
15258         
15259         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15260     },
15261     
15262     
15263     getAutoCreateTouchView : function()
15264     {
15265         var id = Roo.id();
15266         
15267         var cfg = {
15268             cls: 'form-group' //input-group
15269         };
15270         
15271         var input =  {
15272             tag: 'input',
15273             id : id,
15274             type : this.inputType,
15275             cls : 'form-control x-combo-noedit',
15276             autocomplete: 'new-password',
15277             placeholder : this.placeholder || '',
15278             readonly : true
15279         };
15280         
15281         if (this.name) {
15282             input.name = this.name;
15283         }
15284         
15285         if (this.size) {
15286             input.cls += ' input-' + this.size;
15287         }
15288         
15289         if (this.disabled) {
15290             input.disabled = true;
15291         }
15292         
15293         var inputblock = {
15294             cls : '',
15295             cn : [
15296                 input
15297             ]
15298         };
15299         
15300         if(this.before){
15301             inputblock.cls += ' input-group';
15302             
15303             inputblock.cn.unshift({
15304                 tag :'span',
15305                 cls : 'input-group-addon input-group-prepend input-group-text',
15306                 html : this.before
15307             });
15308         }
15309         
15310         if(this.removable && !this.multiple){
15311             inputblock.cls += ' roo-removable';
15312             
15313             inputblock.cn.push({
15314                 tag: 'button',
15315                 html : 'x',
15316                 cls : 'roo-combo-removable-btn close'
15317             });
15318         }
15319
15320         if(this.hasFeedback && !this.allowBlank){
15321             
15322             inputblock.cls += ' has-feedback';
15323             
15324             inputblock.cn.push({
15325                 tag: 'span',
15326                 cls: 'glyphicon form-control-feedback'
15327             });
15328             
15329         }
15330         
15331         if (this.after) {
15332             
15333             inputblock.cls += (this.before) ? '' : ' input-group';
15334             
15335             inputblock.cn.push({
15336                 tag :'span',
15337                 cls : 'input-group-addon input-group-append input-group-text',
15338                 html : this.after
15339             });
15340         }
15341
15342         
15343         var ibwrap = inputblock;
15344         
15345         if(this.multiple){
15346             ibwrap = {
15347                 tag: 'ul',
15348                 cls: 'roo-select2-choices',
15349                 cn:[
15350                     {
15351                         tag: 'li',
15352                         cls: 'roo-select2-search-field',
15353                         cn: [
15354
15355                             inputblock
15356                         ]
15357                     }
15358                 ]
15359             };
15360         
15361             
15362         }
15363         
15364         var combobox = {
15365             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15366             cn: [
15367                 {
15368                     tag: 'input',
15369                     type : 'hidden',
15370                     cls: 'form-hidden-field'
15371                 },
15372                 ibwrap
15373             ]
15374         };
15375         
15376         if(!this.multiple && this.showToggleBtn){
15377             
15378             var caret = {
15379                         tag: 'span',
15380                         cls: 'caret'
15381             };
15382             
15383             if (this.caret != false) {
15384                 caret = {
15385                      tag: 'i',
15386                      cls: 'fa fa-' + this.caret
15387                 };
15388                 
15389             }
15390             
15391             combobox.cn.push({
15392                 tag :'span',
15393                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15394                 cn : [
15395                     caret,
15396                     {
15397                         tag: 'span',
15398                         cls: 'combobox-clear',
15399                         cn  : [
15400                             {
15401                                 tag : 'i',
15402                                 cls: 'icon-remove'
15403                             }
15404                         ]
15405                     }
15406                 ]
15407
15408             })
15409         }
15410         
15411         if(this.multiple){
15412             combobox.cls += ' roo-select2-container-multi';
15413         }
15414         
15415         var align = this.labelAlign || this.parentLabelAlign();
15416         
15417         if (align ==='left' && this.fieldLabel.length) {
15418
15419             cfg.cn = [
15420                 {
15421                    tag : 'i',
15422                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15423                    tooltip : 'This field is required'
15424                 },
15425                 {
15426                     tag: 'label',
15427                     cls : 'control-label col-form-label',
15428                     html : this.fieldLabel
15429
15430                 },
15431                 {
15432                     cls : '', 
15433                     cn: [
15434                         combobox
15435                     ]
15436                 }
15437             ];
15438             
15439             var labelCfg = cfg.cn[1];
15440             var contentCfg = cfg.cn[2];
15441             
15442
15443             if(this.indicatorpos == 'right'){
15444                 cfg.cn = [
15445                     {
15446                         tag: 'label',
15447                         'for' :  id,
15448                         cls : 'control-label col-form-label',
15449                         cn : [
15450                             {
15451                                 tag : 'span',
15452                                 html : this.fieldLabel
15453                             },
15454                             {
15455                                 tag : 'i',
15456                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15457                                 tooltip : 'This field is required'
15458                             }
15459                         ]
15460                     },
15461                     {
15462                         cls : "",
15463                         cn: [
15464                             combobox
15465                         ]
15466                     }
15467
15468                 ];
15469                 
15470                 labelCfg = cfg.cn[0];
15471                 contentCfg = cfg.cn[1];
15472             }
15473             
15474            
15475             
15476             if(this.labelWidth > 12){
15477                 labelCfg.style = "width: " + this.labelWidth + 'px';
15478             }
15479             
15480             if(this.labelWidth < 13 && this.labelmd == 0){
15481                 this.labelmd = this.labelWidth;
15482             }
15483             
15484             if(this.labellg > 0){
15485                 labelCfg.cls += ' col-lg-' + this.labellg;
15486                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15487             }
15488             
15489             if(this.labelmd > 0){
15490                 labelCfg.cls += ' col-md-' + this.labelmd;
15491                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15492             }
15493             
15494             if(this.labelsm > 0){
15495                 labelCfg.cls += ' col-sm-' + this.labelsm;
15496                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15497             }
15498             
15499             if(this.labelxs > 0){
15500                 labelCfg.cls += ' col-xs-' + this.labelxs;
15501                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15502             }
15503                 
15504                 
15505         } else if ( this.fieldLabel.length) {
15506             cfg.cn = [
15507                 {
15508                    tag : 'i',
15509                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15510                    tooltip : 'This field is required'
15511                 },
15512                 {
15513                     tag: 'label',
15514                     cls : 'control-label',
15515                     html : this.fieldLabel
15516
15517                 },
15518                 {
15519                     cls : '', 
15520                     cn: [
15521                         combobox
15522                     ]
15523                 }
15524             ];
15525             
15526             if(this.indicatorpos == 'right'){
15527                 cfg.cn = [
15528                     {
15529                         tag: 'label',
15530                         cls : 'control-label',
15531                         html : this.fieldLabel,
15532                         cn : [
15533                             {
15534                                tag : 'i',
15535                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15536                                tooltip : 'This field is required'
15537                             }
15538                         ]
15539                     },
15540                     {
15541                         cls : '', 
15542                         cn: [
15543                             combobox
15544                         ]
15545                     }
15546                 ];
15547             }
15548         } else {
15549             cfg.cn = combobox;    
15550         }
15551         
15552         
15553         var settings = this;
15554         
15555         ['xs','sm','md','lg'].map(function(size){
15556             if (settings[size]) {
15557                 cfg.cls += ' col-' + size + '-' + settings[size];
15558             }
15559         });
15560         
15561         return cfg;
15562     },
15563     
15564     initTouchView : function()
15565     {
15566         this.renderTouchView();
15567         
15568         this.touchViewEl.on('scroll', function(){
15569             this.el.dom.scrollTop = 0;
15570         }, this);
15571         
15572         this.originalValue = this.getValue();
15573         
15574         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15575         
15576         this.inputEl().on("click", this.showTouchView, this);
15577         if (this.triggerEl) {
15578             this.triggerEl.on("click", this.showTouchView, this);
15579         }
15580         
15581         
15582         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15583         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15584         
15585         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15586         
15587         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15588         this.store.on('load', this.onTouchViewLoad, this);
15589         this.store.on('loadexception', this.onTouchViewLoadException, this);
15590         
15591         if(this.hiddenName){
15592             
15593             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15594             
15595             this.hiddenField.dom.value =
15596                 this.hiddenValue !== undefined ? this.hiddenValue :
15597                 this.value !== undefined ? this.value : '';
15598         
15599             this.el.dom.removeAttribute('name');
15600             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15601         }
15602         
15603         if(this.multiple){
15604             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15605             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15606         }
15607         
15608         if(this.removable && !this.multiple){
15609             var close = this.closeTriggerEl();
15610             if(close){
15611                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15612                 close.on('click', this.removeBtnClick, this, close);
15613             }
15614         }
15615         /*
15616          * fix the bug in Safari iOS8
15617          */
15618         this.inputEl().on("focus", function(e){
15619             document.activeElement.blur();
15620         }, this);
15621         
15622         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15623         
15624         return;
15625         
15626         
15627     },
15628     
15629     renderTouchView : function()
15630     {
15631         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15632         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15633         
15634         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15635         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15636         
15637         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15638         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15639         this.touchViewBodyEl.setStyle('overflow', 'auto');
15640         
15641         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15642         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15643         
15644         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15645         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15646         
15647     },
15648     
15649     showTouchView : function()
15650     {
15651         if(this.disabled){
15652             return;
15653         }
15654         
15655         this.touchViewHeaderEl.hide();
15656
15657         if(this.modalTitle.length){
15658             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15659             this.touchViewHeaderEl.show();
15660         }
15661
15662         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15663         this.touchViewEl.show();
15664
15665         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15666         
15667         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15668         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15669
15670         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15671
15672         if(this.modalTitle.length){
15673             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15674         }
15675         
15676         this.touchViewBodyEl.setHeight(bodyHeight);
15677
15678         if(this.animate){
15679             var _this = this;
15680             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15681         }else{
15682             this.touchViewEl.addClass('in');
15683         }
15684         
15685         if(this._touchViewMask){
15686             Roo.get(document.body).addClass("x-body-masked");
15687             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15688             this._touchViewMask.setStyle('z-index', 10000);
15689             this._touchViewMask.addClass('show');
15690         }
15691         
15692         this.doTouchViewQuery();
15693         
15694     },
15695     
15696     hideTouchView : function()
15697     {
15698         this.touchViewEl.removeClass('in');
15699
15700         if(this.animate){
15701             var _this = this;
15702             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15703         }else{
15704             this.touchViewEl.setStyle('display', 'none');
15705         }
15706         
15707         if(this._touchViewMask){
15708             this._touchViewMask.removeClass('show');
15709             Roo.get(document.body).removeClass("x-body-masked");
15710         }
15711     },
15712     
15713     setTouchViewValue : function()
15714     {
15715         if(this.multiple){
15716             this.clearItem();
15717         
15718             var _this = this;
15719
15720             Roo.each(this.tickItems, function(o){
15721                 this.addItem(o);
15722             }, this);
15723         }
15724         
15725         this.hideTouchView();
15726     },
15727     
15728     doTouchViewQuery : function()
15729     {
15730         var qe = {
15731             query: '',
15732             forceAll: true,
15733             combo: this,
15734             cancel:false
15735         };
15736         
15737         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15738             return false;
15739         }
15740         
15741         if(!this.alwaysQuery || this.mode == 'local'){
15742             this.onTouchViewLoad();
15743             return;
15744         }
15745         
15746         this.store.load();
15747     },
15748     
15749     onTouchViewBeforeLoad : function(combo,opts)
15750     {
15751         return;
15752     },
15753
15754     // private
15755     onTouchViewLoad : function()
15756     {
15757         if(this.store.getCount() < 1){
15758             this.onTouchViewEmptyResults();
15759             return;
15760         }
15761         
15762         this.clearTouchView();
15763         
15764         var rawValue = this.getRawValue();
15765         
15766         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15767         
15768         this.tickItems = [];
15769         
15770         this.store.data.each(function(d, rowIndex){
15771             var row = this.touchViewListGroup.createChild(template);
15772             
15773             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15774                 row.addClass(d.data.cls);
15775             }
15776             
15777             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15778                 var cfg = {
15779                     data : d.data,
15780                     html : d.data[this.displayField]
15781                 };
15782                 
15783                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15784                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15785                 }
15786             }
15787             row.removeClass('selected');
15788             if(!this.multiple && this.valueField &&
15789                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15790             {
15791                 // radio buttons..
15792                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15793                 row.addClass('selected');
15794             }
15795             
15796             if(this.multiple && this.valueField &&
15797                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15798             {
15799                 
15800                 // checkboxes...
15801                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15802                 this.tickItems.push(d.data);
15803             }
15804             
15805             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15806             
15807         }, this);
15808         
15809         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15810         
15811         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15812
15813         if(this.modalTitle.length){
15814             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15815         }
15816
15817         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15818         
15819         if(this.mobile_restrict_height && listHeight < bodyHeight){
15820             this.touchViewBodyEl.setHeight(listHeight);
15821         }
15822         
15823         var _this = this;
15824         
15825         if(firstChecked && listHeight > bodyHeight){
15826             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15827         }
15828         
15829     },
15830     
15831     onTouchViewLoadException : function()
15832     {
15833         this.hideTouchView();
15834     },
15835     
15836     onTouchViewEmptyResults : function()
15837     {
15838         this.clearTouchView();
15839         
15840         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15841         
15842         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15843         
15844     },
15845     
15846     clearTouchView : function()
15847     {
15848         this.touchViewListGroup.dom.innerHTML = '';
15849     },
15850     
15851     onTouchViewClick : function(e, el, o)
15852     {
15853         e.preventDefault();
15854         
15855         var row = o.row;
15856         var rowIndex = o.rowIndex;
15857         
15858         var r = this.store.getAt(rowIndex);
15859         
15860         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15861             
15862             if(!this.multiple){
15863                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15864                     c.dom.removeAttribute('checked');
15865                 }, this);
15866
15867                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15868
15869                 this.setFromData(r.data);
15870
15871                 var close = this.closeTriggerEl();
15872
15873                 if(close){
15874                     close.show();
15875                 }
15876
15877                 this.hideTouchView();
15878
15879                 this.fireEvent('select', this, r, rowIndex);
15880
15881                 return;
15882             }
15883
15884             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15885                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15886                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15887                 return;
15888             }
15889
15890             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15891             this.addItem(r.data);
15892             this.tickItems.push(r.data);
15893         }
15894     },
15895     
15896     getAutoCreateNativeIOS : function()
15897     {
15898         var cfg = {
15899             cls: 'form-group' //input-group,
15900         };
15901         
15902         var combobox =  {
15903             tag: 'select',
15904             cls : 'roo-ios-select'
15905         };
15906         
15907         if (this.name) {
15908             combobox.name = this.name;
15909         }
15910         
15911         if (this.disabled) {
15912             combobox.disabled = true;
15913         }
15914         
15915         var settings = this;
15916         
15917         ['xs','sm','md','lg'].map(function(size){
15918             if (settings[size]) {
15919                 cfg.cls += ' col-' + size + '-' + settings[size];
15920             }
15921         });
15922         
15923         cfg.cn = combobox;
15924         
15925         return cfg;
15926         
15927     },
15928     
15929     initIOSView : function()
15930     {
15931         this.store.on('load', this.onIOSViewLoad, this);
15932         
15933         return;
15934     },
15935     
15936     onIOSViewLoad : function()
15937     {
15938         if(this.store.getCount() < 1){
15939             return;
15940         }
15941         
15942         this.clearIOSView();
15943         
15944         if(this.allowBlank) {
15945             
15946             var default_text = '-- SELECT --';
15947             
15948             if(this.placeholder.length){
15949                 default_text = this.placeholder;
15950             }
15951             
15952             if(this.emptyTitle.length){
15953                 default_text += ' - ' + this.emptyTitle + ' -';
15954             }
15955             
15956             var opt = this.inputEl().createChild({
15957                 tag: 'option',
15958                 value : 0,
15959                 html : default_text
15960             });
15961             
15962             var o = {};
15963             o[this.valueField] = 0;
15964             o[this.displayField] = default_text;
15965             
15966             this.ios_options.push({
15967                 data : o,
15968                 el : opt
15969             });
15970             
15971         }
15972         
15973         this.store.data.each(function(d, rowIndex){
15974             
15975             var html = '';
15976             
15977             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15978                 html = d.data[this.displayField];
15979             }
15980             
15981             var value = '';
15982             
15983             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15984                 value = d.data[this.valueField];
15985             }
15986             
15987             var option = {
15988                 tag: 'option',
15989                 value : value,
15990                 html : html
15991             };
15992             
15993             if(this.value == d.data[this.valueField]){
15994                 option['selected'] = true;
15995             }
15996             
15997             var opt = this.inputEl().createChild(option);
15998             
15999             this.ios_options.push({
16000                 data : d.data,
16001                 el : opt
16002             });
16003             
16004         }, this);
16005         
16006         this.inputEl().on('change', function(){
16007            this.fireEvent('select', this);
16008         }, this);
16009         
16010     },
16011     
16012     clearIOSView: function()
16013     {
16014         this.inputEl().dom.innerHTML = '';
16015         
16016         this.ios_options = [];
16017     },
16018     
16019     setIOSValue: function(v)
16020     {
16021         this.value = v;
16022         
16023         if(!this.ios_options){
16024             return;
16025         }
16026         
16027         Roo.each(this.ios_options, function(opts){
16028            
16029            opts.el.dom.removeAttribute('selected');
16030            
16031            if(opts.data[this.valueField] != v){
16032                return;
16033            }
16034            
16035            opts.el.dom.setAttribute('selected', true);
16036            
16037         }, this);
16038     }
16039
16040     /** 
16041     * @cfg {Boolean} grow 
16042     * @hide 
16043     */
16044     /** 
16045     * @cfg {Number} growMin 
16046     * @hide 
16047     */
16048     /** 
16049     * @cfg {Number} growMax 
16050     * @hide 
16051     */
16052     /**
16053      * @hide
16054      * @method autoSize
16055      */
16056 });
16057
16058 Roo.apply(Roo.bootstrap.ComboBox,  {
16059     
16060     header : {
16061         tag: 'div',
16062         cls: 'modal-header',
16063         cn: [
16064             {
16065                 tag: 'h4',
16066                 cls: 'modal-title'
16067             }
16068         ]
16069     },
16070     
16071     body : {
16072         tag: 'div',
16073         cls: 'modal-body',
16074         cn: [
16075             {
16076                 tag: 'ul',
16077                 cls: 'list-group'
16078             }
16079         ]
16080     },
16081     
16082     listItemRadio : {
16083         tag: 'li',
16084         cls: 'list-group-item',
16085         cn: [
16086             {
16087                 tag: 'span',
16088                 cls: 'roo-combobox-list-group-item-value'
16089             },
16090             {
16091                 tag: 'div',
16092                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16093                 cn: [
16094                     {
16095                         tag: 'input',
16096                         type: 'radio'
16097                     },
16098                     {
16099                         tag: 'label'
16100                     }
16101                 ]
16102             }
16103         ]
16104     },
16105     
16106     listItemCheckbox : {
16107         tag: 'li',
16108         cls: 'list-group-item',
16109         cn: [
16110             {
16111                 tag: 'span',
16112                 cls: 'roo-combobox-list-group-item-value'
16113             },
16114             {
16115                 tag: 'div',
16116                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16117                 cn: [
16118                     {
16119                         tag: 'input',
16120                         type: 'checkbox'
16121                     },
16122                     {
16123                         tag: 'label'
16124                     }
16125                 ]
16126             }
16127         ]
16128     },
16129     
16130     emptyResult : {
16131         tag: 'div',
16132         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16133     },
16134     
16135     footer : {
16136         tag: 'div',
16137         cls: 'modal-footer',
16138         cn: [
16139             {
16140                 tag: 'div',
16141                 cls: 'row',
16142                 cn: [
16143                     {
16144                         tag: 'div',
16145                         cls: 'col-xs-6 text-left',
16146                         cn: {
16147                             tag: 'button',
16148                             cls: 'btn btn-danger roo-touch-view-cancel',
16149                             html: 'Cancel'
16150                         }
16151                     },
16152                     {
16153                         tag: 'div',
16154                         cls: 'col-xs-6 text-right',
16155                         cn: {
16156                             tag: 'button',
16157                             cls: 'btn btn-success roo-touch-view-ok',
16158                             html: 'OK'
16159                         }
16160                     }
16161                 ]
16162             }
16163         ]
16164         
16165     }
16166 });
16167
16168 Roo.apply(Roo.bootstrap.ComboBox,  {
16169     
16170     touchViewTemplate : {
16171         tag: 'div',
16172         cls: 'modal fade roo-combobox-touch-view',
16173         cn: [
16174             {
16175                 tag: 'div',
16176                 cls: 'modal-dialog',
16177                 style : 'position:fixed', // we have to fix position....
16178                 cn: [
16179                     {
16180                         tag: 'div',
16181                         cls: 'modal-content',
16182                         cn: [
16183                             Roo.bootstrap.ComboBox.header,
16184                             Roo.bootstrap.ComboBox.body,
16185                             Roo.bootstrap.ComboBox.footer
16186                         ]
16187                     }
16188                 ]
16189             }
16190         ]
16191     }
16192 });/*
16193  * Based on:
16194  * Ext JS Library 1.1.1
16195  * Copyright(c) 2006-2007, Ext JS, LLC.
16196  *
16197  * Originally Released Under LGPL - original licence link has changed is not relivant.
16198  *
16199  * Fork - LGPL
16200  * <script type="text/javascript">
16201  */
16202
16203 /**
16204  * @class Roo.View
16205  * @extends Roo.util.Observable
16206  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16207  * This class also supports single and multi selection modes. <br>
16208  * Create a data model bound view:
16209  <pre><code>
16210  var store = new Roo.data.Store(...);
16211
16212  var view = new Roo.View({
16213     el : "my-element",
16214     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16215  
16216     singleSelect: true,
16217     selectedClass: "ydataview-selected",
16218     store: store
16219  });
16220
16221  // listen for node click?
16222  view.on("click", function(vw, index, node, e){
16223  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16224  });
16225
16226  // load XML data
16227  dataModel.load("foobar.xml");
16228  </code></pre>
16229  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16230  * <br><br>
16231  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16232  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16233  * 
16234  * Note: old style constructor is still suported (container, template, config)
16235  * 
16236  * @constructor
16237  * Create a new View
16238  * @param {Object} config The config object
16239  * 
16240  */
16241 Roo.View = function(config, depreciated_tpl, depreciated_config){
16242     
16243     this.parent = false;
16244     
16245     if (typeof(depreciated_tpl) == 'undefined') {
16246         // new way.. - universal constructor.
16247         Roo.apply(this, config);
16248         this.el  = Roo.get(this.el);
16249     } else {
16250         // old format..
16251         this.el  = Roo.get(config);
16252         this.tpl = depreciated_tpl;
16253         Roo.apply(this, depreciated_config);
16254     }
16255     this.wrapEl  = this.el.wrap().wrap();
16256     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16257     
16258     
16259     if(typeof(this.tpl) == "string"){
16260         this.tpl = new Roo.Template(this.tpl);
16261     } else {
16262         // support xtype ctors..
16263         this.tpl = new Roo.factory(this.tpl, Roo);
16264     }
16265     
16266     
16267     this.tpl.compile();
16268     
16269     /** @private */
16270     this.addEvents({
16271         /**
16272          * @event beforeclick
16273          * Fires before a click is processed. Returns false to cancel the default action.
16274          * @param {Roo.View} this
16275          * @param {Number} index The index of the target node
16276          * @param {HTMLElement} node The target node
16277          * @param {Roo.EventObject} e The raw event object
16278          */
16279             "beforeclick" : true,
16280         /**
16281          * @event click
16282          * Fires when a template node is clicked.
16283          * @param {Roo.View} this
16284          * @param {Number} index The index of the target node
16285          * @param {HTMLElement} node The target node
16286          * @param {Roo.EventObject} e The raw event object
16287          */
16288             "click" : true,
16289         /**
16290          * @event dblclick
16291          * Fires when a template node is double clicked.
16292          * @param {Roo.View} this
16293          * @param {Number} index The index of the target node
16294          * @param {HTMLElement} node The target node
16295          * @param {Roo.EventObject} e The raw event object
16296          */
16297             "dblclick" : true,
16298         /**
16299          * @event contextmenu
16300          * Fires when a template node is right clicked.
16301          * @param {Roo.View} this
16302          * @param {Number} index The index of the target node
16303          * @param {HTMLElement} node The target node
16304          * @param {Roo.EventObject} e The raw event object
16305          */
16306             "contextmenu" : true,
16307         /**
16308          * @event selectionchange
16309          * Fires when the selected nodes change.
16310          * @param {Roo.View} this
16311          * @param {Array} selections Array of the selected nodes
16312          */
16313             "selectionchange" : true,
16314     
16315         /**
16316          * @event beforeselect
16317          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16318          * @param {Roo.View} this
16319          * @param {HTMLElement} node The node to be selected
16320          * @param {Array} selections Array of currently selected nodes
16321          */
16322             "beforeselect" : true,
16323         /**
16324          * @event preparedata
16325          * Fires on every row to render, to allow you to change the data.
16326          * @param {Roo.View} this
16327          * @param {Object} data to be rendered (change this)
16328          */
16329           "preparedata" : true
16330           
16331           
16332         });
16333
16334
16335
16336     this.el.on({
16337         "click": this.onClick,
16338         "dblclick": this.onDblClick,
16339         "contextmenu": this.onContextMenu,
16340         scope:this
16341     });
16342
16343     this.selections = [];
16344     this.nodes = [];
16345     this.cmp = new Roo.CompositeElementLite([]);
16346     if(this.store){
16347         this.store = Roo.factory(this.store, Roo.data);
16348         this.setStore(this.store, true);
16349     }
16350     
16351     if ( this.footer && this.footer.xtype) {
16352            
16353          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16354         
16355         this.footer.dataSource = this.store;
16356         this.footer.container = fctr;
16357         this.footer = Roo.factory(this.footer, Roo);
16358         fctr.insertFirst(this.el);
16359         
16360         // this is a bit insane - as the paging toolbar seems to detach the el..
16361 //        dom.parentNode.parentNode.parentNode
16362          // they get detached?
16363     }
16364     
16365     
16366     Roo.View.superclass.constructor.call(this);
16367     
16368     
16369 };
16370
16371 Roo.extend(Roo.View, Roo.util.Observable, {
16372     
16373      /**
16374      * @cfg {Roo.data.Store} store Data store to load data from.
16375      */
16376     store : false,
16377     
16378     /**
16379      * @cfg {String|Roo.Element} el The container element.
16380      */
16381     el : '',
16382     
16383     /**
16384      * @cfg {String|Roo.Template} tpl The template used by this View 
16385      */
16386     tpl : false,
16387     /**
16388      * @cfg {String} dataName the named area of the template to use as the data area
16389      *                          Works with domtemplates roo-name="name"
16390      */
16391     dataName: false,
16392     /**
16393      * @cfg {String} selectedClass The css class to add to selected nodes
16394      */
16395     selectedClass : "x-view-selected",
16396      /**
16397      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16398      */
16399     emptyText : "",
16400     
16401     /**
16402      * @cfg {String} text to display on mask (default Loading)
16403      */
16404     mask : false,
16405     /**
16406      * @cfg {Boolean} multiSelect Allow multiple selection
16407      */
16408     multiSelect : false,
16409     /**
16410      * @cfg {Boolean} singleSelect Allow single selection
16411      */
16412     singleSelect:  false,
16413     
16414     /**
16415      * @cfg {Boolean} toggleSelect - selecting 
16416      */
16417     toggleSelect : false,
16418     
16419     /**
16420      * @cfg {Boolean} tickable - selecting 
16421      */
16422     tickable : false,
16423     
16424     /**
16425      * Returns the element this view is bound to.
16426      * @return {Roo.Element}
16427      */
16428     getEl : function(){
16429         return this.wrapEl;
16430     },
16431     
16432     
16433
16434     /**
16435      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16436      */
16437     refresh : function(){
16438         //Roo.log('refresh');
16439         var t = this.tpl;
16440         
16441         // if we are using something like 'domtemplate', then
16442         // the what gets used is:
16443         // t.applySubtemplate(NAME, data, wrapping data..)
16444         // the outer template then get' applied with
16445         //     the store 'extra data'
16446         // and the body get's added to the
16447         //      roo-name="data" node?
16448         //      <span class='roo-tpl-{name}'></span> ?????
16449         
16450         
16451         
16452         this.clearSelections();
16453         this.el.update("");
16454         var html = [];
16455         var records = this.store.getRange();
16456         if(records.length < 1) {
16457             
16458             // is this valid??  = should it render a template??
16459             
16460             this.el.update(this.emptyText);
16461             return;
16462         }
16463         var el = this.el;
16464         if (this.dataName) {
16465             this.el.update(t.apply(this.store.meta)); //????
16466             el = this.el.child('.roo-tpl-' + this.dataName);
16467         }
16468         
16469         for(var i = 0, len = records.length; i < len; i++){
16470             var data = this.prepareData(records[i].data, i, records[i]);
16471             this.fireEvent("preparedata", this, data, i, records[i]);
16472             
16473             var d = Roo.apply({}, data);
16474             
16475             if(this.tickable){
16476                 Roo.apply(d, {'roo-id' : Roo.id()});
16477                 
16478                 var _this = this;
16479             
16480                 Roo.each(this.parent.item, function(item){
16481                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16482                         return;
16483                     }
16484                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16485                 });
16486             }
16487             
16488             html[html.length] = Roo.util.Format.trim(
16489                 this.dataName ?
16490                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16491                     t.apply(d)
16492             );
16493         }
16494         
16495         
16496         
16497         el.update(html.join(""));
16498         this.nodes = el.dom.childNodes;
16499         this.updateIndexes(0);
16500     },
16501     
16502
16503     /**
16504      * Function to override to reformat the data that is sent to
16505      * the template for each node.
16506      * DEPRICATED - use the preparedata event handler.
16507      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16508      * a JSON object for an UpdateManager bound view).
16509      */
16510     prepareData : function(data, index, record)
16511     {
16512         this.fireEvent("preparedata", this, data, index, record);
16513         return data;
16514     },
16515
16516     onUpdate : function(ds, record){
16517         // Roo.log('on update');   
16518         this.clearSelections();
16519         var index = this.store.indexOf(record);
16520         var n = this.nodes[index];
16521         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16522         n.parentNode.removeChild(n);
16523         this.updateIndexes(index, index);
16524     },
16525
16526     
16527     
16528 // --------- FIXME     
16529     onAdd : function(ds, records, index)
16530     {
16531         //Roo.log(['on Add', ds, records, index] );        
16532         this.clearSelections();
16533         if(this.nodes.length == 0){
16534             this.refresh();
16535             return;
16536         }
16537         var n = this.nodes[index];
16538         for(var i = 0, len = records.length; i < len; i++){
16539             var d = this.prepareData(records[i].data, i, records[i]);
16540             if(n){
16541                 this.tpl.insertBefore(n, d);
16542             }else{
16543                 
16544                 this.tpl.append(this.el, d);
16545             }
16546         }
16547         this.updateIndexes(index);
16548     },
16549
16550     onRemove : function(ds, record, index){
16551        // Roo.log('onRemove');
16552         this.clearSelections();
16553         var el = this.dataName  ?
16554             this.el.child('.roo-tpl-' + this.dataName) :
16555             this.el; 
16556         
16557         el.dom.removeChild(this.nodes[index]);
16558         this.updateIndexes(index);
16559     },
16560
16561     /**
16562      * Refresh an individual node.
16563      * @param {Number} index
16564      */
16565     refreshNode : function(index){
16566         this.onUpdate(this.store, this.store.getAt(index));
16567     },
16568
16569     updateIndexes : function(startIndex, endIndex){
16570         var ns = this.nodes;
16571         startIndex = startIndex || 0;
16572         endIndex = endIndex || ns.length - 1;
16573         for(var i = startIndex; i <= endIndex; i++){
16574             ns[i].nodeIndex = i;
16575         }
16576     },
16577
16578     /**
16579      * Changes the data store this view uses and refresh the view.
16580      * @param {Store} store
16581      */
16582     setStore : function(store, initial){
16583         if(!initial && this.store){
16584             this.store.un("datachanged", this.refresh);
16585             this.store.un("add", this.onAdd);
16586             this.store.un("remove", this.onRemove);
16587             this.store.un("update", this.onUpdate);
16588             this.store.un("clear", this.refresh);
16589             this.store.un("beforeload", this.onBeforeLoad);
16590             this.store.un("load", this.onLoad);
16591             this.store.un("loadexception", this.onLoad);
16592         }
16593         if(store){
16594           
16595             store.on("datachanged", this.refresh, this);
16596             store.on("add", this.onAdd, this);
16597             store.on("remove", this.onRemove, this);
16598             store.on("update", this.onUpdate, this);
16599             store.on("clear", this.refresh, this);
16600             store.on("beforeload", this.onBeforeLoad, this);
16601             store.on("load", this.onLoad, this);
16602             store.on("loadexception", this.onLoad, this);
16603         }
16604         
16605         if(store){
16606             this.refresh();
16607         }
16608     },
16609     /**
16610      * onbeforeLoad - masks the loading area.
16611      *
16612      */
16613     onBeforeLoad : function(store,opts)
16614     {
16615          //Roo.log('onBeforeLoad');   
16616         if (!opts.add) {
16617             this.el.update("");
16618         }
16619         this.el.mask(this.mask ? this.mask : "Loading" ); 
16620     },
16621     onLoad : function ()
16622     {
16623         this.el.unmask();
16624     },
16625     
16626
16627     /**
16628      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16629      * @param {HTMLElement} node
16630      * @return {HTMLElement} The template node
16631      */
16632     findItemFromChild : function(node){
16633         var el = this.dataName  ?
16634             this.el.child('.roo-tpl-' + this.dataName,true) :
16635             this.el.dom; 
16636         
16637         if(!node || node.parentNode == el){
16638                     return node;
16639             }
16640             var p = node.parentNode;
16641             while(p && p != el){
16642             if(p.parentNode == el){
16643                 return p;
16644             }
16645             p = p.parentNode;
16646         }
16647             return null;
16648     },
16649
16650     /** @ignore */
16651     onClick : function(e){
16652         var item = this.findItemFromChild(e.getTarget());
16653         if(item){
16654             var index = this.indexOf(item);
16655             if(this.onItemClick(item, index, e) !== false){
16656                 this.fireEvent("click", this, index, item, e);
16657             }
16658         }else{
16659             this.clearSelections();
16660         }
16661     },
16662
16663     /** @ignore */
16664     onContextMenu : function(e){
16665         var item = this.findItemFromChild(e.getTarget());
16666         if(item){
16667             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16668         }
16669     },
16670
16671     /** @ignore */
16672     onDblClick : function(e){
16673         var item = this.findItemFromChild(e.getTarget());
16674         if(item){
16675             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16676         }
16677     },
16678
16679     onItemClick : function(item, index, e)
16680     {
16681         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16682             return false;
16683         }
16684         if (this.toggleSelect) {
16685             var m = this.isSelected(item) ? 'unselect' : 'select';
16686             //Roo.log(m);
16687             var _t = this;
16688             _t[m](item, true, false);
16689             return true;
16690         }
16691         if(this.multiSelect || this.singleSelect){
16692             if(this.multiSelect && e.shiftKey && this.lastSelection){
16693                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16694             }else{
16695                 this.select(item, this.multiSelect && e.ctrlKey);
16696                 this.lastSelection = item;
16697             }
16698             
16699             if(!this.tickable){
16700                 e.preventDefault();
16701             }
16702             
16703         }
16704         return true;
16705     },
16706
16707     /**
16708      * Get the number of selected nodes.
16709      * @return {Number}
16710      */
16711     getSelectionCount : function(){
16712         return this.selections.length;
16713     },
16714
16715     /**
16716      * Get the currently selected nodes.
16717      * @return {Array} An array of HTMLElements
16718      */
16719     getSelectedNodes : function(){
16720         return this.selections;
16721     },
16722
16723     /**
16724      * Get the indexes of the selected nodes.
16725      * @return {Array}
16726      */
16727     getSelectedIndexes : function(){
16728         var indexes = [], s = this.selections;
16729         for(var i = 0, len = s.length; i < len; i++){
16730             indexes.push(s[i].nodeIndex);
16731         }
16732         return indexes;
16733     },
16734
16735     /**
16736      * Clear all selections
16737      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16738      */
16739     clearSelections : function(suppressEvent){
16740         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16741             this.cmp.elements = this.selections;
16742             this.cmp.removeClass(this.selectedClass);
16743             this.selections = [];
16744             if(!suppressEvent){
16745                 this.fireEvent("selectionchange", this, this.selections);
16746             }
16747         }
16748     },
16749
16750     /**
16751      * Returns true if the passed node is selected
16752      * @param {HTMLElement/Number} node The node or node index
16753      * @return {Boolean}
16754      */
16755     isSelected : function(node){
16756         var s = this.selections;
16757         if(s.length < 1){
16758             return false;
16759         }
16760         node = this.getNode(node);
16761         return s.indexOf(node) !== -1;
16762     },
16763
16764     /**
16765      * Selects nodes.
16766      * @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
16767      * @param {Boolean} keepExisting (optional) true to keep existing selections
16768      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16769      */
16770     select : function(nodeInfo, keepExisting, suppressEvent){
16771         if(nodeInfo instanceof Array){
16772             if(!keepExisting){
16773                 this.clearSelections(true);
16774             }
16775             for(var i = 0, len = nodeInfo.length; i < len; i++){
16776                 this.select(nodeInfo[i], true, true);
16777             }
16778             return;
16779         } 
16780         var node = this.getNode(nodeInfo);
16781         if(!node || this.isSelected(node)){
16782             return; // already selected.
16783         }
16784         if(!keepExisting){
16785             this.clearSelections(true);
16786         }
16787         
16788         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16789             Roo.fly(node).addClass(this.selectedClass);
16790             this.selections.push(node);
16791             if(!suppressEvent){
16792                 this.fireEvent("selectionchange", this, this.selections);
16793             }
16794         }
16795         
16796         
16797     },
16798       /**
16799      * Unselects nodes.
16800      * @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
16801      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16802      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16803      */
16804     unselect : function(nodeInfo, keepExisting, suppressEvent)
16805     {
16806         if(nodeInfo instanceof Array){
16807             Roo.each(this.selections, function(s) {
16808                 this.unselect(s, nodeInfo);
16809             }, this);
16810             return;
16811         }
16812         var node = this.getNode(nodeInfo);
16813         if(!node || !this.isSelected(node)){
16814             //Roo.log("not selected");
16815             return; // not selected.
16816         }
16817         // fireevent???
16818         var ns = [];
16819         Roo.each(this.selections, function(s) {
16820             if (s == node ) {
16821                 Roo.fly(node).removeClass(this.selectedClass);
16822
16823                 return;
16824             }
16825             ns.push(s);
16826         },this);
16827         
16828         this.selections= ns;
16829         this.fireEvent("selectionchange", this, this.selections);
16830     },
16831
16832     /**
16833      * Gets a template node.
16834      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16835      * @return {HTMLElement} The node or null if it wasn't found
16836      */
16837     getNode : function(nodeInfo){
16838         if(typeof nodeInfo == "string"){
16839             return document.getElementById(nodeInfo);
16840         }else if(typeof nodeInfo == "number"){
16841             return this.nodes[nodeInfo];
16842         }
16843         return nodeInfo;
16844     },
16845
16846     /**
16847      * Gets a range template nodes.
16848      * @param {Number} startIndex
16849      * @param {Number} endIndex
16850      * @return {Array} An array of nodes
16851      */
16852     getNodes : function(start, end){
16853         var ns = this.nodes;
16854         start = start || 0;
16855         end = typeof end == "undefined" ? ns.length - 1 : end;
16856         var nodes = [];
16857         if(start <= end){
16858             for(var i = start; i <= end; i++){
16859                 nodes.push(ns[i]);
16860             }
16861         } else{
16862             for(var i = start; i >= end; i--){
16863                 nodes.push(ns[i]);
16864             }
16865         }
16866         return nodes;
16867     },
16868
16869     /**
16870      * Finds the index of the passed node
16871      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16872      * @return {Number} The index of the node or -1
16873      */
16874     indexOf : function(node){
16875         node = this.getNode(node);
16876         if(typeof node.nodeIndex == "number"){
16877             return node.nodeIndex;
16878         }
16879         var ns = this.nodes;
16880         for(var i = 0, len = ns.length; i < len; i++){
16881             if(ns[i] == node){
16882                 return i;
16883             }
16884         }
16885         return -1;
16886     }
16887 });
16888 /*
16889  * - LGPL
16890  *
16891  * based on jquery fullcalendar
16892  * 
16893  */
16894
16895 Roo.bootstrap = Roo.bootstrap || {};
16896 /**
16897  * @class Roo.bootstrap.Calendar
16898  * @extends Roo.bootstrap.Component
16899  * Bootstrap Calendar class
16900  * @cfg {Boolean} loadMask (true|false) default false
16901  * @cfg {Object} header generate the user specific header of the calendar, default false
16902
16903  * @constructor
16904  * Create a new Container
16905  * @param {Object} config The config object
16906  */
16907
16908
16909
16910 Roo.bootstrap.Calendar = function(config){
16911     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16912      this.addEvents({
16913         /**
16914              * @event select
16915              * Fires when a date is selected
16916              * @param {DatePicker} this
16917              * @param {Date} date The selected date
16918              */
16919         'select': true,
16920         /**
16921              * @event monthchange
16922              * Fires when the displayed month changes 
16923              * @param {DatePicker} this
16924              * @param {Date} date The selected month
16925              */
16926         'monthchange': true,
16927         /**
16928              * @event evententer
16929              * Fires when mouse over an event
16930              * @param {Calendar} this
16931              * @param {event} Event
16932              */
16933         'evententer': true,
16934         /**
16935              * @event eventleave
16936              * Fires when the mouse leaves an
16937              * @param {Calendar} this
16938              * @param {event}
16939              */
16940         'eventleave': true,
16941         /**
16942              * @event eventclick
16943              * Fires when the mouse click an
16944              * @param {Calendar} this
16945              * @param {event}
16946              */
16947         'eventclick': true
16948         
16949     });
16950
16951 };
16952
16953 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16954     
16955      /**
16956      * @cfg {Number} startDay
16957      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16958      */
16959     startDay : 0,
16960     
16961     loadMask : false,
16962     
16963     header : false,
16964       
16965     getAutoCreate : function(){
16966         
16967         
16968         var fc_button = function(name, corner, style, content ) {
16969             return Roo.apply({},{
16970                 tag : 'span',
16971                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16972                          (corner.length ?
16973                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16974                             ''
16975                         ),
16976                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16977                 unselectable: 'on'
16978             });
16979         };
16980         
16981         var header = {};
16982         
16983         if(!this.header){
16984             header = {
16985                 tag : 'table',
16986                 cls : 'fc-header',
16987                 style : 'width:100%',
16988                 cn : [
16989                     {
16990                         tag: 'tr',
16991                         cn : [
16992                             {
16993                                 tag : 'td',
16994                                 cls : 'fc-header-left',
16995                                 cn : [
16996                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16997                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16998                                     { tag: 'span', cls: 'fc-header-space' },
16999                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17000
17001
17002                                 ]
17003                             },
17004
17005                             {
17006                                 tag : 'td',
17007                                 cls : 'fc-header-center',
17008                                 cn : [
17009                                     {
17010                                         tag: 'span',
17011                                         cls: 'fc-header-title',
17012                                         cn : {
17013                                             tag: 'H2',
17014                                             html : 'month / year'
17015                                         }
17016                                     }
17017
17018                                 ]
17019                             },
17020                             {
17021                                 tag : 'td',
17022                                 cls : 'fc-header-right',
17023                                 cn : [
17024                               /*      fc_button('month', 'left', '', 'month' ),
17025                                     fc_button('week', '', '', 'week' ),
17026                                     fc_button('day', 'right', '', 'day' )
17027                                 */    
17028
17029                                 ]
17030                             }
17031
17032                         ]
17033                     }
17034                 ]
17035             };
17036         }
17037         
17038         header = this.header;
17039         
17040        
17041         var cal_heads = function() {
17042             var ret = [];
17043             // fixme - handle this.
17044             
17045             for (var i =0; i < Date.dayNames.length; i++) {
17046                 var d = Date.dayNames[i];
17047                 ret.push({
17048                     tag: 'th',
17049                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17050                     html : d.substring(0,3)
17051                 });
17052                 
17053             }
17054             ret[0].cls += ' fc-first';
17055             ret[6].cls += ' fc-last';
17056             return ret;
17057         };
17058         var cal_cell = function(n) {
17059             return  {
17060                 tag: 'td',
17061                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17062                 cn : [
17063                     {
17064                         cn : [
17065                             {
17066                                 cls: 'fc-day-number',
17067                                 html: 'D'
17068                             },
17069                             {
17070                                 cls: 'fc-day-content',
17071                              
17072                                 cn : [
17073                                      {
17074                                         style: 'position: relative;' // height: 17px;
17075                                     }
17076                                 ]
17077                             }
17078                             
17079                             
17080                         ]
17081                     }
17082                 ]
17083                 
17084             }
17085         };
17086         var cal_rows = function() {
17087             
17088             var ret = [];
17089             for (var r = 0; r < 6; r++) {
17090                 var row= {
17091                     tag : 'tr',
17092                     cls : 'fc-week',
17093                     cn : []
17094                 };
17095                 
17096                 for (var i =0; i < Date.dayNames.length; i++) {
17097                     var d = Date.dayNames[i];
17098                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17099
17100                 }
17101                 row.cn[0].cls+=' fc-first';
17102                 row.cn[0].cn[0].style = 'min-height:90px';
17103                 row.cn[6].cls+=' fc-last';
17104                 ret.push(row);
17105                 
17106             }
17107             ret[0].cls += ' fc-first';
17108             ret[4].cls += ' fc-prev-last';
17109             ret[5].cls += ' fc-last';
17110             return ret;
17111             
17112         };
17113         
17114         var cal_table = {
17115             tag: 'table',
17116             cls: 'fc-border-separate',
17117             style : 'width:100%',
17118             cellspacing  : 0,
17119             cn : [
17120                 { 
17121                     tag: 'thead',
17122                     cn : [
17123                         { 
17124                             tag: 'tr',
17125                             cls : 'fc-first fc-last',
17126                             cn : cal_heads()
17127                         }
17128                     ]
17129                 },
17130                 { 
17131                     tag: 'tbody',
17132                     cn : cal_rows()
17133                 }
17134                   
17135             ]
17136         };
17137          
17138          var cfg = {
17139             cls : 'fc fc-ltr',
17140             cn : [
17141                 header,
17142                 {
17143                     cls : 'fc-content',
17144                     style : "position: relative;",
17145                     cn : [
17146                         {
17147                             cls : 'fc-view fc-view-month fc-grid',
17148                             style : 'position: relative',
17149                             unselectable : 'on',
17150                             cn : [
17151                                 {
17152                                     cls : 'fc-event-container',
17153                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17154                                 },
17155                                 cal_table
17156                             ]
17157                         }
17158                     ]
17159     
17160                 }
17161            ] 
17162             
17163         };
17164         
17165          
17166         
17167         return cfg;
17168     },
17169     
17170     
17171     initEvents : function()
17172     {
17173         if(!this.store){
17174             throw "can not find store for calendar";
17175         }
17176         
17177         var mark = {
17178             tag: "div",
17179             cls:"x-dlg-mask",
17180             style: "text-align:center",
17181             cn: [
17182                 {
17183                     tag: "div",
17184                     style: "background-color:white;width:50%;margin:250 auto",
17185                     cn: [
17186                         {
17187                             tag: "img",
17188                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17189                         },
17190                         {
17191                             tag: "span",
17192                             html: "Loading"
17193                         }
17194                         
17195                     ]
17196                 }
17197             ]
17198         };
17199         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17200         
17201         var size = this.el.select('.fc-content', true).first().getSize();
17202         this.maskEl.setSize(size.width, size.height);
17203         this.maskEl.enableDisplayMode("block");
17204         if(!this.loadMask){
17205             this.maskEl.hide();
17206         }
17207         
17208         this.store = Roo.factory(this.store, Roo.data);
17209         this.store.on('load', this.onLoad, this);
17210         this.store.on('beforeload', this.onBeforeLoad, this);
17211         
17212         this.resize();
17213         
17214         this.cells = this.el.select('.fc-day',true);
17215         //Roo.log(this.cells);
17216         this.textNodes = this.el.query('.fc-day-number');
17217         this.cells.addClassOnOver('fc-state-hover');
17218         
17219         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17220         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17221         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17222         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17223         
17224         this.on('monthchange', this.onMonthChange, this);
17225         
17226         this.update(new Date().clearTime());
17227     },
17228     
17229     resize : function() {
17230         var sz  = this.el.getSize();
17231         
17232         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17233         this.el.select('.fc-day-content div',true).setHeight(34);
17234     },
17235     
17236     
17237     // private
17238     showPrevMonth : function(e){
17239         this.update(this.activeDate.add("mo", -1));
17240     },
17241     showToday : function(e){
17242         this.update(new Date().clearTime());
17243     },
17244     // private
17245     showNextMonth : function(e){
17246         this.update(this.activeDate.add("mo", 1));
17247     },
17248
17249     // private
17250     showPrevYear : function(){
17251         this.update(this.activeDate.add("y", -1));
17252     },
17253
17254     // private
17255     showNextYear : function(){
17256         this.update(this.activeDate.add("y", 1));
17257     },
17258
17259     
17260    // private
17261     update : function(date)
17262     {
17263         var vd = this.activeDate;
17264         this.activeDate = date;
17265 //        if(vd && this.el){
17266 //            var t = date.getTime();
17267 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17268 //                Roo.log('using add remove');
17269 //                
17270 //                this.fireEvent('monthchange', this, date);
17271 //                
17272 //                this.cells.removeClass("fc-state-highlight");
17273 //                this.cells.each(function(c){
17274 //                   if(c.dateValue == t){
17275 //                       c.addClass("fc-state-highlight");
17276 //                       setTimeout(function(){
17277 //                            try{c.dom.firstChild.focus();}catch(e){}
17278 //                       }, 50);
17279 //                       return false;
17280 //                   }
17281 //                   return true;
17282 //                });
17283 //                return;
17284 //            }
17285 //        }
17286         
17287         var days = date.getDaysInMonth();
17288         
17289         var firstOfMonth = date.getFirstDateOfMonth();
17290         var startingPos = firstOfMonth.getDay()-this.startDay;
17291         
17292         if(startingPos < this.startDay){
17293             startingPos += 7;
17294         }
17295         
17296         var pm = date.add(Date.MONTH, -1);
17297         var prevStart = pm.getDaysInMonth()-startingPos;
17298 //        
17299         this.cells = this.el.select('.fc-day',true);
17300         this.textNodes = this.el.query('.fc-day-number');
17301         this.cells.addClassOnOver('fc-state-hover');
17302         
17303         var cells = this.cells.elements;
17304         var textEls = this.textNodes;
17305         
17306         Roo.each(cells, function(cell){
17307             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17308         });
17309         
17310         days += startingPos;
17311
17312         // convert everything to numbers so it's fast
17313         var day = 86400000;
17314         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17315         //Roo.log(d);
17316         //Roo.log(pm);
17317         //Roo.log(prevStart);
17318         
17319         var today = new Date().clearTime().getTime();
17320         var sel = date.clearTime().getTime();
17321         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17322         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17323         var ddMatch = this.disabledDatesRE;
17324         var ddText = this.disabledDatesText;
17325         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17326         var ddaysText = this.disabledDaysText;
17327         var format = this.format;
17328         
17329         var setCellClass = function(cal, cell){
17330             cell.row = 0;
17331             cell.events = [];
17332             cell.more = [];
17333             //Roo.log('set Cell Class');
17334             cell.title = "";
17335             var t = d.getTime();
17336             
17337             //Roo.log(d);
17338             
17339             cell.dateValue = t;
17340             if(t == today){
17341                 cell.className += " fc-today";
17342                 cell.className += " fc-state-highlight";
17343                 cell.title = cal.todayText;
17344             }
17345             if(t == sel){
17346                 // disable highlight in other month..
17347                 //cell.className += " fc-state-highlight";
17348                 
17349             }
17350             // disabling
17351             if(t < min) {
17352                 cell.className = " fc-state-disabled";
17353                 cell.title = cal.minText;
17354                 return;
17355             }
17356             if(t > max) {
17357                 cell.className = " fc-state-disabled";
17358                 cell.title = cal.maxText;
17359                 return;
17360             }
17361             if(ddays){
17362                 if(ddays.indexOf(d.getDay()) != -1){
17363                     cell.title = ddaysText;
17364                     cell.className = " fc-state-disabled";
17365                 }
17366             }
17367             if(ddMatch && format){
17368                 var fvalue = d.dateFormat(format);
17369                 if(ddMatch.test(fvalue)){
17370                     cell.title = ddText.replace("%0", fvalue);
17371                     cell.className = " fc-state-disabled";
17372                 }
17373             }
17374             
17375             if (!cell.initialClassName) {
17376                 cell.initialClassName = cell.dom.className;
17377             }
17378             
17379             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17380         };
17381
17382         var i = 0;
17383         
17384         for(; i < startingPos; i++) {
17385             textEls[i].innerHTML = (++prevStart);
17386             d.setDate(d.getDate()+1);
17387             
17388             cells[i].className = "fc-past fc-other-month";
17389             setCellClass(this, cells[i]);
17390         }
17391         
17392         var intDay = 0;
17393         
17394         for(; i < days; i++){
17395             intDay = i - startingPos + 1;
17396             textEls[i].innerHTML = (intDay);
17397             d.setDate(d.getDate()+1);
17398             
17399             cells[i].className = ''; // "x-date-active";
17400             setCellClass(this, cells[i]);
17401         }
17402         var extraDays = 0;
17403         
17404         for(; i < 42; i++) {
17405             textEls[i].innerHTML = (++extraDays);
17406             d.setDate(d.getDate()+1);
17407             
17408             cells[i].className = "fc-future fc-other-month";
17409             setCellClass(this, cells[i]);
17410         }
17411         
17412         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17413         
17414         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17415         
17416         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17417         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17418         
17419         if(totalRows != 6){
17420             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17421             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17422         }
17423         
17424         this.fireEvent('monthchange', this, date);
17425         
17426         
17427         /*
17428         if(!this.internalRender){
17429             var main = this.el.dom.firstChild;
17430             var w = main.offsetWidth;
17431             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17432             Roo.fly(main).setWidth(w);
17433             this.internalRender = true;
17434             // opera does not respect the auto grow header center column
17435             // then, after it gets a width opera refuses to recalculate
17436             // without a second pass
17437             if(Roo.isOpera && !this.secondPass){
17438                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17439                 this.secondPass = true;
17440                 this.update.defer(10, this, [date]);
17441             }
17442         }
17443         */
17444         
17445     },
17446     
17447     findCell : function(dt) {
17448         dt = dt.clearTime().getTime();
17449         var ret = false;
17450         this.cells.each(function(c){
17451             //Roo.log("check " +c.dateValue + '?=' + dt);
17452             if(c.dateValue == dt){
17453                 ret = c;
17454                 return false;
17455             }
17456             return true;
17457         });
17458         
17459         return ret;
17460     },
17461     
17462     findCells : function(ev) {
17463         var s = ev.start.clone().clearTime().getTime();
17464        // Roo.log(s);
17465         var e= ev.end.clone().clearTime().getTime();
17466        // Roo.log(e);
17467         var ret = [];
17468         this.cells.each(function(c){
17469              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17470             
17471             if(c.dateValue > e){
17472                 return ;
17473             }
17474             if(c.dateValue < s){
17475                 return ;
17476             }
17477             ret.push(c);
17478         });
17479         
17480         return ret;    
17481     },
17482     
17483 //    findBestRow: function(cells)
17484 //    {
17485 //        var ret = 0;
17486 //        
17487 //        for (var i =0 ; i < cells.length;i++) {
17488 //            ret  = Math.max(cells[i].rows || 0,ret);
17489 //        }
17490 //        return ret;
17491 //        
17492 //    },
17493     
17494     
17495     addItem : function(ev)
17496     {
17497         // look for vertical location slot in
17498         var cells = this.findCells(ev);
17499         
17500 //        ev.row = this.findBestRow(cells);
17501         
17502         // work out the location.
17503         
17504         var crow = false;
17505         var rows = [];
17506         for(var i =0; i < cells.length; i++) {
17507             
17508             cells[i].row = cells[0].row;
17509             
17510             if(i == 0){
17511                 cells[i].row = cells[i].row + 1;
17512             }
17513             
17514             if (!crow) {
17515                 crow = {
17516                     start : cells[i],
17517                     end :  cells[i]
17518                 };
17519                 continue;
17520             }
17521             if (crow.start.getY() == cells[i].getY()) {
17522                 // on same row.
17523                 crow.end = cells[i];
17524                 continue;
17525             }
17526             // different row.
17527             rows.push(crow);
17528             crow = {
17529                 start: cells[i],
17530                 end : cells[i]
17531             };
17532             
17533         }
17534         
17535         rows.push(crow);
17536         ev.els = [];
17537         ev.rows = rows;
17538         ev.cells = cells;
17539         
17540         cells[0].events.push(ev);
17541         
17542         this.calevents.push(ev);
17543     },
17544     
17545     clearEvents: function() {
17546         
17547         if(!this.calevents){
17548             return;
17549         }
17550         
17551         Roo.each(this.cells.elements, function(c){
17552             c.row = 0;
17553             c.events = [];
17554             c.more = [];
17555         });
17556         
17557         Roo.each(this.calevents, function(e) {
17558             Roo.each(e.els, function(el) {
17559                 el.un('mouseenter' ,this.onEventEnter, this);
17560                 el.un('mouseleave' ,this.onEventLeave, this);
17561                 el.remove();
17562             },this);
17563         },this);
17564         
17565         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17566             e.remove();
17567         });
17568         
17569     },
17570     
17571     renderEvents: function()
17572     {   
17573         var _this = this;
17574         
17575         this.cells.each(function(c) {
17576             
17577             if(c.row < 5){
17578                 return;
17579             }
17580             
17581             var ev = c.events;
17582             
17583             var r = 4;
17584             if(c.row != c.events.length){
17585                 r = 4 - (4 - (c.row - c.events.length));
17586             }
17587             
17588             c.events = ev.slice(0, r);
17589             c.more = ev.slice(r);
17590             
17591             if(c.more.length && c.more.length == 1){
17592                 c.events.push(c.more.pop());
17593             }
17594             
17595             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17596             
17597         });
17598             
17599         this.cells.each(function(c) {
17600             
17601             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17602             
17603             
17604             for (var e = 0; e < c.events.length; e++){
17605                 var ev = c.events[e];
17606                 var rows = ev.rows;
17607                 
17608                 for(var i = 0; i < rows.length; i++) {
17609                 
17610                     // how many rows should it span..
17611
17612                     var  cfg = {
17613                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17614                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17615
17616                         unselectable : "on",
17617                         cn : [
17618                             {
17619                                 cls: 'fc-event-inner',
17620                                 cn : [
17621     //                                {
17622     //                                  tag:'span',
17623     //                                  cls: 'fc-event-time',
17624     //                                  html : cells.length > 1 ? '' : ev.time
17625     //                                },
17626                                     {
17627                                       tag:'span',
17628                                       cls: 'fc-event-title',
17629                                       html : String.format('{0}', ev.title)
17630                                     }
17631
17632
17633                                 ]
17634                             },
17635                             {
17636                                 cls: 'ui-resizable-handle ui-resizable-e',
17637                                 html : '&nbsp;&nbsp;&nbsp'
17638                             }
17639
17640                         ]
17641                     };
17642
17643                     if (i == 0) {
17644                         cfg.cls += ' fc-event-start';
17645                     }
17646                     if ((i+1) == rows.length) {
17647                         cfg.cls += ' fc-event-end';
17648                     }
17649
17650                     var ctr = _this.el.select('.fc-event-container',true).first();
17651                     var cg = ctr.createChild(cfg);
17652
17653                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17654                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17655
17656                     var r = (c.more.length) ? 1 : 0;
17657                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17658                     cg.setWidth(ebox.right - sbox.x -2);
17659
17660                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17661                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17662                     cg.on('click', _this.onEventClick, _this, ev);
17663
17664                     ev.els.push(cg);
17665                     
17666                 }
17667                 
17668             }
17669             
17670             
17671             if(c.more.length){
17672                 var  cfg = {
17673                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17674                     style : 'position: absolute',
17675                     unselectable : "on",
17676                     cn : [
17677                         {
17678                             cls: 'fc-event-inner',
17679                             cn : [
17680                                 {
17681                                   tag:'span',
17682                                   cls: 'fc-event-title',
17683                                   html : 'More'
17684                                 }
17685
17686
17687                             ]
17688                         },
17689                         {
17690                             cls: 'ui-resizable-handle ui-resizable-e',
17691                             html : '&nbsp;&nbsp;&nbsp'
17692                         }
17693
17694                     ]
17695                 };
17696
17697                 var ctr = _this.el.select('.fc-event-container',true).first();
17698                 var cg = ctr.createChild(cfg);
17699
17700                 var sbox = c.select('.fc-day-content',true).first().getBox();
17701                 var ebox = c.select('.fc-day-content',true).first().getBox();
17702                 //Roo.log(cg);
17703                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17704                 cg.setWidth(ebox.right - sbox.x -2);
17705
17706                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17707                 
17708             }
17709             
17710         });
17711         
17712         
17713         
17714     },
17715     
17716     onEventEnter: function (e, el,event,d) {
17717         this.fireEvent('evententer', this, el, event);
17718     },
17719     
17720     onEventLeave: function (e, el,event,d) {
17721         this.fireEvent('eventleave', this, el, event);
17722     },
17723     
17724     onEventClick: function (e, el,event,d) {
17725         this.fireEvent('eventclick', this, el, event);
17726     },
17727     
17728     onMonthChange: function () {
17729         this.store.load();
17730     },
17731     
17732     onMoreEventClick: function(e, el, more)
17733     {
17734         var _this = this;
17735         
17736         this.calpopover.placement = 'right';
17737         this.calpopover.setTitle('More');
17738         
17739         this.calpopover.setContent('');
17740         
17741         var ctr = this.calpopover.el.select('.popover-content', true).first();
17742         
17743         Roo.each(more, function(m){
17744             var cfg = {
17745                 cls : 'fc-event-hori fc-event-draggable',
17746                 html : m.title
17747             };
17748             var cg = ctr.createChild(cfg);
17749             
17750             cg.on('click', _this.onEventClick, _this, m);
17751         });
17752         
17753         this.calpopover.show(el);
17754         
17755         
17756     },
17757     
17758     onLoad: function () 
17759     {   
17760         this.calevents = [];
17761         var cal = this;
17762         
17763         if(this.store.getCount() > 0){
17764             this.store.data.each(function(d){
17765                cal.addItem({
17766                     id : d.data.id,
17767                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17768                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17769                     time : d.data.start_time,
17770                     title : d.data.title,
17771                     description : d.data.description,
17772                     venue : d.data.venue
17773                 });
17774             });
17775         }
17776         
17777         this.renderEvents();
17778         
17779         if(this.calevents.length && this.loadMask){
17780             this.maskEl.hide();
17781         }
17782     },
17783     
17784     onBeforeLoad: function()
17785     {
17786         this.clearEvents();
17787         if(this.loadMask){
17788             this.maskEl.show();
17789         }
17790     }
17791 });
17792
17793  
17794  /*
17795  * - LGPL
17796  *
17797  * element
17798  * 
17799  */
17800
17801 /**
17802  * @class Roo.bootstrap.Popover
17803  * @extends Roo.bootstrap.Component
17804  * Bootstrap Popover class
17805  * @cfg {String} html contents of the popover   (or false to use children..)
17806  * @cfg {String} title of popover (or false to hide)
17807  * @cfg {String} placement how it is placed
17808  * @cfg {String} trigger click || hover (or false to trigger manually)
17809  * @cfg {String} over what (parent or false to trigger manually.)
17810  * @cfg {Number} delay - delay before showing
17811  
17812  * @constructor
17813  * Create a new Popover
17814  * @param {Object} config The config object
17815  */
17816
17817 Roo.bootstrap.Popover = function(config){
17818     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17819     
17820     this.addEvents({
17821         // raw events
17822          /**
17823          * @event show
17824          * After the popover show
17825          * 
17826          * @param {Roo.bootstrap.Popover} this
17827          */
17828         "show" : true,
17829         /**
17830          * @event hide
17831          * After the popover hide
17832          * 
17833          * @param {Roo.bootstrap.Popover} this
17834          */
17835         "hide" : true
17836     });
17837 };
17838
17839 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17840     
17841     title: 'Fill in a title',
17842     html: false,
17843     
17844     placement : 'right',
17845     trigger : 'hover', // hover
17846     
17847     delay : 0,
17848     
17849     over: 'parent',
17850     
17851     can_build_overlaid : false,
17852     
17853     getChildContainer : function()
17854     {
17855         return this.el.select('.popover-content',true).first();
17856     },
17857     
17858     getAutoCreate : function(){
17859          
17860         var cfg = {
17861            cls : 'popover roo-dynamic',
17862            style: 'display:block',
17863            cn : [
17864                 {
17865                     cls : 'arrow'
17866                 },
17867                 {
17868                     cls : 'popover-inner',
17869                     cn : [
17870                         {
17871                             tag: 'h3',
17872                             cls: 'popover-title popover-header',
17873                             html : this.title
17874                         },
17875                         {
17876                             cls : 'popover-content popover-body',
17877                             html : this.html
17878                         }
17879                     ]
17880                     
17881                 }
17882            ]
17883         };
17884         
17885         return cfg;
17886     },
17887     setTitle: function(str)
17888     {
17889         this.title = str;
17890         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17891     },
17892     setContent: function(str)
17893     {
17894         this.html = str;
17895         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17896     },
17897     // as it get's added to the bottom of the page.
17898     onRender : function(ct, position)
17899     {
17900         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17901         if(!this.el){
17902             var cfg = Roo.apply({},  this.getAutoCreate());
17903             cfg.id = Roo.id();
17904             
17905             if (this.cls) {
17906                 cfg.cls += ' ' + this.cls;
17907             }
17908             if (this.style) {
17909                 cfg.style = this.style;
17910             }
17911             //Roo.log("adding to ");
17912             this.el = Roo.get(document.body).createChild(cfg, position);
17913 //            Roo.log(this.el);
17914         }
17915         this.initEvents();
17916     },
17917     
17918     initEvents : function()
17919     {
17920         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17921         this.el.enableDisplayMode('block');
17922         this.el.hide();
17923         if (this.over === false) {
17924             return; 
17925         }
17926         if (this.triggers === false) {
17927             return;
17928         }
17929         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17930         var triggers = this.trigger ? this.trigger.split(' ') : [];
17931         Roo.each(triggers, function(trigger) {
17932         
17933             if (trigger == 'click') {
17934                 on_el.on('click', this.toggle, this);
17935             } else if (trigger != 'manual') {
17936                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17937                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17938       
17939                 on_el.on(eventIn  ,this.enter, this);
17940                 on_el.on(eventOut, this.leave, this);
17941             }
17942         }, this);
17943         
17944     },
17945     
17946     
17947     // private
17948     timeout : null,
17949     hoverState : null,
17950     
17951     toggle : function () {
17952         this.hoverState == 'in' ? this.leave() : this.enter();
17953     },
17954     
17955     enter : function () {
17956         
17957         clearTimeout(this.timeout);
17958     
17959         this.hoverState = 'in';
17960     
17961         if (!this.delay || !this.delay.show) {
17962             this.show();
17963             return;
17964         }
17965         var _t = this;
17966         this.timeout = setTimeout(function () {
17967             if (_t.hoverState == 'in') {
17968                 _t.show();
17969             }
17970         }, this.delay.show)
17971     },
17972     
17973     leave : function() {
17974         clearTimeout(this.timeout);
17975     
17976         this.hoverState = 'out';
17977     
17978         if (!this.delay || !this.delay.hide) {
17979             this.hide();
17980             return;
17981         }
17982         var _t = this;
17983         this.timeout = setTimeout(function () {
17984             if (_t.hoverState == 'out') {
17985                 _t.hide();
17986             }
17987         }, this.delay.hide)
17988     },
17989     
17990     show : function (on_el)
17991     {
17992         if (!on_el) {
17993             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17994         }
17995         
17996         // set content.
17997         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17998         if (this.html !== false) {
17999             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18000         }
18001         this.el.removeClass([
18002             'fade','top','bottom', 'left', 'right','in',
18003             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18004         ]);
18005         if (!this.title.length) {
18006             this.el.select('.popover-title',true).hide();
18007         }
18008         
18009         var placement = typeof this.placement == 'function' ?
18010             this.placement.call(this, this.el, on_el) :
18011             this.placement;
18012             
18013         var autoToken = /\s?auto?\s?/i;
18014         var autoPlace = autoToken.test(placement);
18015         if (autoPlace) {
18016             placement = placement.replace(autoToken, '') || 'top';
18017         }
18018         
18019         //this.el.detach()
18020         //this.el.setXY([0,0]);
18021         this.el.show();
18022         this.el.dom.style.display='block';
18023         this.el.addClass(placement);
18024         
18025         //this.el.appendTo(on_el);
18026         
18027         var p = this.getPosition();
18028         var box = this.el.getBox();
18029         
18030         if (autoPlace) {
18031             // fixme..
18032         }
18033         var align = Roo.bootstrap.Popover.alignment[placement];
18034         
18035 //        Roo.log(align);
18036         this.el.alignTo(on_el, align[0],align[1]);
18037         //var arrow = this.el.select('.arrow',true).first();
18038         //arrow.set(align[2], 
18039         
18040         this.el.addClass('in');
18041         
18042         
18043         if (this.el.hasClass('fade')) {
18044             // fade it?
18045         }
18046         
18047         this.hoverState = 'in';
18048         
18049         this.fireEvent('show', this);
18050         
18051     },
18052     hide : function()
18053     {
18054         this.el.setXY([0,0]);
18055         this.el.removeClass('in');
18056         this.el.hide();
18057         this.hoverState = null;
18058         
18059         this.fireEvent('hide', this);
18060     }
18061     
18062 });
18063
18064 Roo.bootstrap.Popover.alignment = {
18065     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18066     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18067     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18068     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18069 };
18070
18071  /*
18072  * - LGPL
18073  *
18074  * Progress
18075  * 
18076  */
18077
18078 /**
18079  * @class Roo.bootstrap.Progress
18080  * @extends Roo.bootstrap.Component
18081  * Bootstrap Progress class
18082  * @cfg {Boolean} striped striped of the progress bar
18083  * @cfg {Boolean} active animated of the progress bar
18084  * 
18085  * 
18086  * @constructor
18087  * Create a new Progress
18088  * @param {Object} config The config object
18089  */
18090
18091 Roo.bootstrap.Progress = function(config){
18092     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18093 };
18094
18095 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18096     
18097     striped : false,
18098     active: false,
18099     
18100     getAutoCreate : function(){
18101         var cfg = {
18102             tag: 'div',
18103             cls: 'progress'
18104         };
18105         
18106         
18107         if(this.striped){
18108             cfg.cls += ' progress-striped';
18109         }
18110       
18111         if(this.active){
18112             cfg.cls += ' active';
18113         }
18114         
18115         
18116         return cfg;
18117     }
18118    
18119 });
18120
18121  
18122
18123  /*
18124  * - LGPL
18125  *
18126  * ProgressBar
18127  * 
18128  */
18129
18130 /**
18131  * @class Roo.bootstrap.ProgressBar
18132  * @extends Roo.bootstrap.Component
18133  * Bootstrap ProgressBar class
18134  * @cfg {Number} aria_valuenow aria-value now
18135  * @cfg {Number} aria_valuemin aria-value min
18136  * @cfg {Number} aria_valuemax aria-value max
18137  * @cfg {String} label label for the progress bar
18138  * @cfg {String} panel (success | info | warning | danger )
18139  * @cfg {String} role role of the progress bar
18140  * @cfg {String} sr_only text
18141  * 
18142  * 
18143  * @constructor
18144  * Create a new ProgressBar
18145  * @param {Object} config The config object
18146  */
18147
18148 Roo.bootstrap.ProgressBar = function(config){
18149     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18150 };
18151
18152 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18153     
18154     aria_valuenow : 0,
18155     aria_valuemin : 0,
18156     aria_valuemax : 100,
18157     label : false,
18158     panel : false,
18159     role : false,
18160     sr_only: false,
18161     
18162     getAutoCreate : function()
18163     {
18164         
18165         var cfg = {
18166             tag: 'div',
18167             cls: 'progress-bar',
18168             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18169         };
18170         
18171         if(this.sr_only){
18172             cfg.cn = {
18173                 tag: 'span',
18174                 cls: 'sr-only',
18175                 html: this.sr_only
18176             }
18177         }
18178         
18179         if(this.role){
18180             cfg.role = this.role;
18181         }
18182         
18183         if(this.aria_valuenow){
18184             cfg['aria-valuenow'] = this.aria_valuenow;
18185         }
18186         
18187         if(this.aria_valuemin){
18188             cfg['aria-valuemin'] = this.aria_valuemin;
18189         }
18190         
18191         if(this.aria_valuemax){
18192             cfg['aria-valuemax'] = this.aria_valuemax;
18193         }
18194         
18195         if(this.label && !this.sr_only){
18196             cfg.html = this.label;
18197         }
18198         
18199         if(this.panel){
18200             cfg.cls += ' progress-bar-' + this.panel;
18201         }
18202         
18203         return cfg;
18204     },
18205     
18206     update : function(aria_valuenow)
18207     {
18208         this.aria_valuenow = aria_valuenow;
18209         
18210         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18211     }
18212    
18213 });
18214
18215  
18216
18217  /*
18218  * - LGPL
18219  *
18220  * column
18221  * 
18222  */
18223
18224 /**
18225  * @class Roo.bootstrap.TabGroup
18226  * @extends Roo.bootstrap.Column
18227  * Bootstrap Column class
18228  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18229  * @cfg {Boolean} carousel true to make the group behave like a carousel
18230  * @cfg {Boolean} bullets show bullets for the panels
18231  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18232  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18233  * @cfg {Boolean} showarrow (true|false) show arrow default true
18234  * 
18235  * @constructor
18236  * Create a new TabGroup
18237  * @param {Object} config The config object
18238  */
18239
18240 Roo.bootstrap.TabGroup = function(config){
18241     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18242     if (!this.navId) {
18243         this.navId = Roo.id();
18244     }
18245     this.tabs = [];
18246     Roo.bootstrap.TabGroup.register(this);
18247     
18248 };
18249
18250 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18251     
18252     carousel : false,
18253     transition : false,
18254     bullets : 0,
18255     timer : 0,
18256     autoslide : false,
18257     slideFn : false,
18258     slideOnTouch : false,
18259     showarrow : true,
18260     
18261     getAutoCreate : function()
18262     {
18263         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18264         
18265         cfg.cls += ' tab-content';
18266         
18267         if (this.carousel) {
18268             cfg.cls += ' carousel slide';
18269             
18270             cfg.cn = [{
18271                cls : 'carousel-inner',
18272                cn : []
18273             }];
18274         
18275             if(this.bullets  && !Roo.isTouch){
18276                 
18277                 var bullets = {
18278                     cls : 'carousel-bullets',
18279                     cn : []
18280                 };
18281                
18282                 if(this.bullets_cls){
18283                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18284                 }
18285                 
18286                 bullets.cn.push({
18287                     cls : 'clear'
18288                 });
18289                 
18290                 cfg.cn[0].cn.push(bullets);
18291             }
18292             
18293             if(this.showarrow){
18294                 cfg.cn[0].cn.push({
18295                     tag : 'div',
18296                     class : 'carousel-arrow',
18297                     cn : [
18298                         {
18299                             tag : 'div',
18300                             class : 'carousel-prev',
18301                             cn : [
18302                                 {
18303                                     tag : 'i',
18304                                     class : 'fa fa-chevron-left'
18305                                 }
18306                             ]
18307                         },
18308                         {
18309                             tag : 'div',
18310                             class : 'carousel-next',
18311                             cn : [
18312                                 {
18313                                     tag : 'i',
18314                                     class : 'fa fa-chevron-right'
18315                                 }
18316                             ]
18317                         }
18318                     ]
18319                 });
18320             }
18321             
18322         }
18323         
18324         return cfg;
18325     },
18326     
18327     initEvents:  function()
18328     {
18329 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18330 //            this.el.on("touchstart", this.onTouchStart, this);
18331 //        }
18332         
18333         if(this.autoslide){
18334             var _this = this;
18335             
18336             this.slideFn = window.setInterval(function() {
18337                 _this.showPanelNext();
18338             }, this.timer);
18339         }
18340         
18341         if(this.showarrow){
18342             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18343             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18344         }
18345         
18346         
18347     },
18348     
18349 //    onTouchStart : function(e, el, o)
18350 //    {
18351 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18352 //            return;
18353 //        }
18354 //        
18355 //        this.showPanelNext();
18356 //    },
18357     
18358     
18359     getChildContainer : function()
18360     {
18361         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18362     },
18363     
18364     /**
18365     * register a Navigation item
18366     * @param {Roo.bootstrap.NavItem} the navitem to add
18367     */
18368     register : function(item)
18369     {
18370         this.tabs.push( item);
18371         item.navId = this.navId; // not really needed..
18372         this.addBullet();
18373     
18374     },
18375     
18376     getActivePanel : function()
18377     {
18378         var r = false;
18379         Roo.each(this.tabs, function(t) {
18380             if (t.active) {
18381                 r = t;
18382                 return false;
18383             }
18384             return null;
18385         });
18386         return r;
18387         
18388     },
18389     getPanelByName : function(n)
18390     {
18391         var r = false;
18392         Roo.each(this.tabs, function(t) {
18393             if (t.tabId == n) {
18394                 r = t;
18395                 return false;
18396             }
18397             return null;
18398         });
18399         return r;
18400     },
18401     indexOfPanel : function(p)
18402     {
18403         var r = false;
18404         Roo.each(this.tabs, function(t,i) {
18405             if (t.tabId == p.tabId) {
18406                 r = i;
18407                 return false;
18408             }
18409             return null;
18410         });
18411         return r;
18412     },
18413     /**
18414      * show a specific panel
18415      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18416      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18417      */
18418     showPanel : function (pan)
18419     {
18420         if(this.transition || typeof(pan) == 'undefined'){
18421             Roo.log("waiting for the transitionend");
18422             return false;
18423         }
18424         
18425         if (typeof(pan) == 'number') {
18426             pan = this.tabs[pan];
18427         }
18428         
18429         if (typeof(pan) == 'string') {
18430             pan = this.getPanelByName(pan);
18431         }
18432         
18433         var cur = this.getActivePanel();
18434         
18435         if(!pan || !cur){
18436             Roo.log('pan or acitve pan is undefined');
18437             return false;
18438         }
18439         
18440         if (pan.tabId == this.getActivePanel().tabId) {
18441             return true;
18442         }
18443         
18444         if (false === cur.fireEvent('beforedeactivate')) {
18445             return false;
18446         }
18447         
18448         if(this.bullets > 0 && !Roo.isTouch){
18449             this.setActiveBullet(this.indexOfPanel(pan));
18450         }
18451         
18452         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18453             
18454             //class="carousel-item carousel-item-next carousel-item-left"
18455             
18456             this.transition = true;
18457             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18458             var lr = dir == 'next' ? 'left' : 'right';
18459             pan.el.addClass(dir); // or prev
18460             pan.el.addClass('carousel-item-' + dir); // or prev
18461             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18462             cur.el.addClass(lr); // or right
18463             pan.el.addClass(lr);
18464             cur.el.addClass('carousel-item-' +lr); // or right
18465             pan.el.addClass('carousel-item-' +lr);
18466             
18467             
18468             var _this = this;
18469             cur.el.on('transitionend', function() {
18470                 Roo.log("trans end?");
18471                 
18472                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18473                 pan.setActive(true);
18474                 
18475                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18476                 cur.setActive(false);
18477                 
18478                 _this.transition = false;
18479                 
18480             }, this, { single:  true } );
18481             
18482             return true;
18483         }
18484         
18485         cur.setActive(false);
18486         pan.setActive(true);
18487         
18488         return true;
18489         
18490     },
18491     showPanelNext : function()
18492     {
18493         var i = this.indexOfPanel(this.getActivePanel());
18494         
18495         if (i >= this.tabs.length - 1 && !this.autoslide) {
18496             return;
18497         }
18498         
18499         if (i >= this.tabs.length - 1 && this.autoslide) {
18500             i = -1;
18501         }
18502         
18503         this.showPanel(this.tabs[i+1]);
18504     },
18505     
18506     showPanelPrev : function()
18507     {
18508         var i = this.indexOfPanel(this.getActivePanel());
18509         
18510         if (i  < 1 && !this.autoslide) {
18511             return;
18512         }
18513         
18514         if (i < 1 && this.autoslide) {
18515             i = this.tabs.length;
18516         }
18517         
18518         this.showPanel(this.tabs[i-1]);
18519     },
18520     
18521     
18522     addBullet: function()
18523     {
18524         if(!this.bullets || Roo.isTouch){
18525             return;
18526         }
18527         var ctr = this.el.select('.carousel-bullets',true).first();
18528         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18529         var bullet = ctr.createChild({
18530             cls : 'bullet bullet-' + i
18531         },ctr.dom.lastChild);
18532         
18533         
18534         var _this = this;
18535         
18536         bullet.on('click', (function(e, el, o, ii, t){
18537
18538             e.preventDefault();
18539
18540             this.showPanel(ii);
18541
18542             if(this.autoslide && this.slideFn){
18543                 clearInterval(this.slideFn);
18544                 this.slideFn = window.setInterval(function() {
18545                     _this.showPanelNext();
18546                 }, this.timer);
18547             }
18548
18549         }).createDelegate(this, [i, bullet], true));
18550                 
18551         
18552     },
18553      
18554     setActiveBullet : function(i)
18555     {
18556         if(Roo.isTouch){
18557             return;
18558         }
18559         
18560         Roo.each(this.el.select('.bullet', true).elements, function(el){
18561             el.removeClass('selected');
18562         });
18563
18564         var bullet = this.el.select('.bullet-' + i, true).first();
18565         
18566         if(!bullet){
18567             return;
18568         }
18569         
18570         bullet.addClass('selected');
18571     }
18572     
18573     
18574   
18575 });
18576
18577  
18578
18579  
18580  
18581 Roo.apply(Roo.bootstrap.TabGroup, {
18582     
18583     groups: {},
18584      /**
18585     * register a Navigation Group
18586     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18587     */
18588     register : function(navgrp)
18589     {
18590         this.groups[navgrp.navId] = navgrp;
18591         
18592     },
18593     /**
18594     * fetch a Navigation Group based on the navigation ID
18595     * if one does not exist , it will get created.
18596     * @param {string} the navgroup to add
18597     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18598     */
18599     get: function(navId) {
18600         if (typeof(this.groups[navId]) == 'undefined') {
18601             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18602         }
18603         return this.groups[navId] ;
18604     }
18605     
18606     
18607     
18608 });
18609
18610  /*
18611  * - LGPL
18612  *
18613  * TabPanel
18614  * 
18615  */
18616
18617 /**
18618  * @class Roo.bootstrap.TabPanel
18619  * @extends Roo.bootstrap.Component
18620  * Bootstrap TabPanel class
18621  * @cfg {Boolean} active panel active
18622  * @cfg {String} html panel content
18623  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18624  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18625  * @cfg {String} href click to link..
18626  * 
18627  * 
18628  * @constructor
18629  * Create a new TabPanel
18630  * @param {Object} config The config object
18631  */
18632
18633 Roo.bootstrap.TabPanel = function(config){
18634     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18635     this.addEvents({
18636         /**
18637              * @event changed
18638              * Fires when the active status changes
18639              * @param {Roo.bootstrap.TabPanel} this
18640              * @param {Boolean} state the new state
18641             
18642          */
18643         'changed': true,
18644         /**
18645              * @event beforedeactivate
18646              * Fires before a tab is de-activated - can be used to do validation on a form.
18647              * @param {Roo.bootstrap.TabPanel} this
18648              * @return {Boolean} false if there is an error
18649             
18650          */
18651         'beforedeactivate': true
18652      });
18653     
18654     this.tabId = this.tabId || Roo.id();
18655   
18656 };
18657
18658 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18659     
18660     active: false,
18661     html: false,
18662     tabId: false,
18663     navId : false,
18664     href : '',
18665     
18666     getAutoCreate : function(){
18667         
18668         
18669         var cfg = {
18670             tag: 'div',
18671             // item is needed for carousel - not sure if it has any effect otherwise
18672             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18673             html: this.html || ''
18674         };
18675         
18676         if(this.active){
18677             cfg.cls += ' active';
18678         }
18679         
18680         if(this.tabId){
18681             cfg.tabId = this.tabId;
18682         }
18683         
18684         
18685         
18686         return cfg;
18687     },
18688     
18689     initEvents:  function()
18690     {
18691         var p = this.parent();
18692         
18693         this.navId = this.navId || p.navId;
18694         
18695         if (typeof(this.navId) != 'undefined') {
18696             // not really needed.. but just in case.. parent should be a NavGroup.
18697             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18698             
18699             tg.register(this);
18700             
18701             var i = tg.tabs.length - 1;
18702             
18703             if(this.active && tg.bullets > 0 && i < tg.bullets){
18704                 tg.setActiveBullet(i);
18705             }
18706         }
18707         
18708         this.el.on('click', this.onClick, this);
18709         
18710         if(Roo.isTouch){
18711             this.el.on("touchstart", this.onTouchStart, this);
18712             this.el.on("touchmove", this.onTouchMove, this);
18713             this.el.on("touchend", this.onTouchEnd, this);
18714         }
18715         
18716     },
18717     
18718     onRender : function(ct, position)
18719     {
18720         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18721     },
18722     
18723     setActive : function(state)
18724     {
18725         Roo.log("panel - set active " + this.tabId + "=" + state);
18726         
18727         this.active = state;
18728         if (!state) {
18729             this.el.removeClass('active');
18730             
18731         } else  if (!this.el.hasClass('active')) {
18732             this.el.addClass('active');
18733         }
18734         
18735         this.fireEvent('changed', this, state);
18736     },
18737     
18738     onClick : function(e)
18739     {
18740         e.preventDefault();
18741         
18742         if(!this.href.length){
18743             return;
18744         }
18745         
18746         window.location.href = this.href;
18747     },
18748     
18749     startX : 0,
18750     startY : 0,
18751     endX : 0,
18752     endY : 0,
18753     swiping : false,
18754     
18755     onTouchStart : function(e)
18756     {
18757         this.swiping = false;
18758         
18759         this.startX = e.browserEvent.touches[0].clientX;
18760         this.startY = e.browserEvent.touches[0].clientY;
18761     },
18762     
18763     onTouchMove : function(e)
18764     {
18765         this.swiping = true;
18766         
18767         this.endX = e.browserEvent.touches[0].clientX;
18768         this.endY = e.browserEvent.touches[0].clientY;
18769     },
18770     
18771     onTouchEnd : function(e)
18772     {
18773         if(!this.swiping){
18774             this.onClick(e);
18775             return;
18776         }
18777         
18778         var tabGroup = this.parent();
18779         
18780         if(this.endX > this.startX){ // swiping right
18781             tabGroup.showPanelPrev();
18782             return;
18783         }
18784         
18785         if(this.startX > this.endX){ // swiping left
18786             tabGroup.showPanelNext();
18787             return;
18788         }
18789     }
18790     
18791     
18792 });
18793  
18794
18795  
18796
18797  /*
18798  * - LGPL
18799  *
18800  * DateField
18801  * 
18802  */
18803
18804 /**
18805  * @class Roo.bootstrap.DateField
18806  * @extends Roo.bootstrap.Input
18807  * Bootstrap DateField class
18808  * @cfg {Number} weekStart default 0
18809  * @cfg {String} viewMode default empty, (months|years)
18810  * @cfg {String} minViewMode default empty, (months|years)
18811  * @cfg {Number} startDate default -Infinity
18812  * @cfg {Number} endDate default Infinity
18813  * @cfg {Boolean} todayHighlight default false
18814  * @cfg {Boolean} todayBtn default false
18815  * @cfg {Boolean} calendarWeeks default false
18816  * @cfg {Object} daysOfWeekDisabled default empty
18817  * @cfg {Boolean} singleMode default false (true | false)
18818  * 
18819  * @cfg {Boolean} keyboardNavigation default true
18820  * @cfg {String} language default en
18821  * 
18822  * @constructor
18823  * Create a new DateField
18824  * @param {Object} config The config object
18825  */
18826
18827 Roo.bootstrap.DateField = function(config){
18828     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18829      this.addEvents({
18830             /**
18831              * @event show
18832              * Fires when this field show.
18833              * @param {Roo.bootstrap.DateField} this
18834              * @param {Mixed} date The date value
18835              */
18836             show : true,
18837             /**
18838              * @event show
18839              * Fires when this field hide.
18840              * @param {Roo.bootstrap.DateField} this
18841              * @param {Mixed} date The date value
18842              */
18843             hide : true,
18844             /**
18845              * @event select
18846              * Fires when select a date.
18847              * @param {Roo.bootstrap.DateField} this
18848              * @param {Mixed} date The date value
18849              */
18850             select : true,
18851             /**
18852              * @event beforeselect
18853              * Fires when before select a date.
18854              * @param {Roo.bootstrap.DateField} this
18855              * @param {Mixed} date The date value
18856              */
18857             beforeselect : true
18858         });
18859 };
18860
18861 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18862     
18863     /**
18864      * @cfg {String} format
18865      * The default date format string which can be overriden for localization support.  The format must be
18866      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18867      */
18868     format : "m/d/y",
18869     /**
18870      * @cfg {String} altFormats
18871      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18872      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18873      */
18874     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18875     
18876     weekStart : 0,
18877     
18878     viewMode : '',
18879     
18880     minViewMode : '',
18881     
18882     todayHighlight : false,
18883     
18884     todayBtn: false,
18885     
18886     language: 'en',
18887     
18888     keyboardNavigation: true,
18889     
18890     calendarWeeks: false,
18891     
18892     startDate: -Infinity,
18893     
18894     endDate: Infinity,
18895     
18896     daysOfWeekDisabled: [],
18897     
18898     _events: [],
18899     
18900     singleMode : false,
18901     
18902     UTCDate: function()
18903     {
18904         return new Date(Date.UTC.apply(Date, arguments));
18905     },
18906     
18907     UTCToday: function()
18908     {
18909         var today = new Date();
18910         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18911     },
18912     
18913     getDate: function() {
18914             var d = this.getUTCDate();
18915             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18916     },
18917     
18918     getUTCDate: function() {
18919             return this.date;
18920     },
18921     
18922     setDate: function(d) {
18923             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18924     },
18925     
18926     setUTCDate: function(d) {
18927             this.date = d;
18928             this.setValue(this.formatDate(this.date));
18929     },
18930         
18931     onRender: function(ct, position)
18932     {
18933         
18934         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18935         
18936         this.language = this.language || 'en';
18937         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18938         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18939         
18940         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18941         this.format = this.format || 'm/d/y';
18942         this.isInline = false;
18943         this.isInput = true;
18944         this.component = this.el.select('.add-on', true).first() || false;
18945         this.component = (this.component && this.component.length === 0) ? false : this.component;
18946         this.hasInput = this.component && this.inputEl().length;
18947         
18948         if (typeof(this.minViewMode === 'string')) {
18949             switch (this.minViewMode) {
18950                 case 'months':
18951                     this.minViewMode = 1;
18952                     break;
18953                 case 'years':
18954                     this.minViewMode = 2;
18955                     break;
18956                 default:
18957                     this.minViewMode = 0;
18958                     break;
18959             }
18960         }
18961         
18962         if (typeof(this.viewMode === 'string')) {
18963             switch (this.viewMode) {
18964                 case 'months':
18965                     this.viewMode = 1;
18966                     break;
18967                 case 'years':
18968                     this.viewMode = 2;
18969                     break;
18970                 default:
18971                     this.viewMode = 0;
18972                     break;
18973             }
18974         }
18975                 
18976         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18977         
18978 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18979         
18980         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18981         
18982         this.picker().on('mousedown', this.onMousedown, this);
18983         this.picker().on('click', this.onClick, this);
18984         
18985         this.picker().addClass('datepicker-dropdown');
18986         
18987         this.startViewMode = this.viewMode;
18988         
18989         if(this.singleMode){
18990             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18991                 v.setVisibilityMode(Roo.Element.DISPLAY);
18992                 v.hide();
18993             });
18994             
18995             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18996                 v.setStyle('width', '189px');
18997             });
18998         }
18999         
19000         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19001             if(!this.calendarWeeks){
19002                 v.remove();
19003                 return;
19004             }
19005             
19006             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19007             v.attr('colspan', function(i, val){
19008                 return parseInt(val) + 1;
19009             });
19010         });
19011                         
19012         
19013         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19014         
19015         this.setStartDate(this.startDate);
19016         this.setEndDate(this.endDate);
19017         
19018         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19019         
19020         this.fillDow();
19021         this.fillMonths();
19022         this.update();
19023         this.showMode();
19024         
19025         if(this.isInline) {
19026             this.showPopup();
19027         }
19028     },
19029     
19030     picker : function()
19031     {
19032         return this.pickerEl;
19033 //        return this.el.select('.datepicker', true).first();
19034     },
19035     
19036     fillDow: function()
19037     {
19038         var dowCnt = this.weekStart;
19039         
19040         var dow = {
19041             tag: 'tr',
19042             cn: [
19043                 
19044             ]
19045         };
19046         
19047         if(this.calendarWeeks){
19048             dow.cn.push({
19049                 tag: 'th',
19050                 cls: 'cw',
19051                 html: '&nbsp;'
19052             })
19053         }
19054         
19055         while (dowCnt < this.weekStart + 7) {
19056             dow.cn.push({
19057                 tag: 'th',
19058                 cls: 'dow',
19059                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19060             });
19061         }
19062         
19063         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19064     },
19065     
19066     fillMonths: function()
19067     {    
19068         var i = 0;
19069         var months = this.picker().select('>.datepicker-months td', true).first();
19070         
19071         months.dom.innerHTML = '';
19072         
19073         while (i < 12) {
19074             var month = {
19075                 tag: 'span',
19076                 cls: 'month',
19077                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19078             };
19079             
19080             months.createChild(month);
19081         }
19082         
19083     },
19084     
19085     update: function()
19086     {
19087         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;
19088         
19089         if (this.date < this.startDate) {
19090             this.viewDate = new Date(this.startDate);
19091         } else if (this.date > this.endDate) {
19092             this.viewDate = new Date(this.endDate);
19093         } else {
19094             this.viewDate = new Date(this.date);
19095         }
19096         
19097         this.fill();
19098     },
19099     
19100     fill: function() 
19101     {
19102         var d = new Date(this.viewDate),
19103                 year = d.getUTCFullYear(),
19104                 month = d.getUTCMonth(),
19105                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19106                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19107                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19108                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19109                 currentDate = this.date && this.date.valueOf(),
19110                 today = this.UTCToday();
19111         
19112         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19113         
19114 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19115         
19116 //        this.picker.select('>tfoot th.today').
19117 //                                              .text(dates[this.language].today)
19118 //                                              .toggle(this.todayBtn !== false);
19119     
19120         this.updateNavArrows();
19121         this.fillMonths();
19122                                                 
19123         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19124         
19125         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19126          
19127         prevMonth.setUTCDate(day);
19128         
19129         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19130         
19131         var nextMonth = new Date(prevMonth);
19132         
19133         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19134         
19135         nextMonth = nextMonth.valueOf();
19136         
19137         var fillMonths = false;
19138         
19139         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19140         
19141         while(prevMonth.valueOf() <= nextMonth) {
19142             var clsName = '';
19143             
19144             if (prevMonth.getUTCDay() === this.weekStart) {
19145                 if(fillMonths){
19146                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19147                 }
19148                     
19149                 fillMonths = {
19150                     tag: 'tr',
19151                     cn: []
19152                 };
19153                 
19154                 if(this.calendarWeeks){
19155                     // ISO 8601: First week contains first thursday.
19156                     // ISO also states week starts on Monday, but we can be more abstract here.
19157                     var
19158                     // Start of current week: based on weekstart/current date
19159                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19160                     // Thursday of this week
19161                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19162                     // First Thursday of year, year from thursday
19163                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19164                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19165                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19166                     
19167                     fillMonths.cn.push({
19168                         tag: 'td',
19169                         cls: 'cw',
19170                         html: calWeek
19171                     });
19172                 }
19173             }
19174             
19175             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19176                 clsName += ' old';
19177             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19178                 clsName += ' new';
19179             }
19180             if (this.todayHighlight &&
19181                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19182                 prevMonth.getUTCMonth() == today.getMonth() &&
19183                 prevMonth.getUTCDate() == today.getDate()) {
19184                 clsName += ' today';
19185             }
19186             
19187             if (currentDate && prevMonth.valueOf() === currentDate) {
19188                 clsName += ' active';
19189             }
19190             
19191             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19192                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19193                     clsName += ' disabled';
19194             }
19195             
19196             fillMonths.cn.push({
19197                 tag: 'td',
19198                 cls: 'day ' + clsName,
19199                 html: prevMonth.getDate()
19200             });
19201             
19202             prevMonth.setDate(prevMonth.getDate()+1);
19203         }
19204           
19205         var currentYear = this.date && this.date.getUTCFullYear();
19206         var currentMonth = this.date && this.date.getUTCMonth();
19207         
19208         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19209         
19210         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19211             v.removeClass('active');
19212             
19213             if(currentYear === year && k === currentMonth){
19214                 v.addClass('active');
19215             }
19216             
19217             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19218                 v.addClass('disabled');
19219             }
19220             
19221         });
19222         
19223         
19224         year = parseInt(year/10, 10) * 10;
19225         
19226         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19227         
19228         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19229         
19230         year -= 1;
19231         for (var i = -1; i < 11; i++) {
19232             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19233                 tag: 'span',
19234                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19235                 html: year
19236             });
19237             
19238             year += 1;
19239         }
19240     },
19241     
19242     showMode: function(dir) 
19243     {
19244         if (dir) {
19245             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19246         }
19247         
19248         Roo.each(this.picker().select('>div',true).elements, function(v){
19249             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19250             v.hide();
19251         });
19252         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19253     },
19254     
19255     place: function()
19256     {
19257         if(this.isInline) {
19258             return;
19259         }
19260         
19261         this.picker().removeClass(['bottom', 'top']);
19262         
19263         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19264             /*
19265              * place to the top of element!
19266              *
19267              */
19268             
19269             this.picker().addClass('top');
19270             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19271             
19272             return;
19273         }
19274         
19275         this.picker().addClass('bottom');
19276         
19277         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19278     },
19279     
19280     parseDate : function(value)
19281     {
19282         if(!value || value instanceof Date){
19283             return value;
19284         }
19285         var v = Date.parseDate(value, this.format);
19286         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19287             v = Date.parseDate(value, 'Y-m-d');
19288         }
19289         if(!v && this.altFormats){
19290             if(!this.altFormatsArray){
19291                 this.altFormatsArray = this.altFormats.split("|");
19292             }
19293             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19294                 v = Date.parseDate(value, this.altFormatsArray[i]);
19295             }
19296         }
19297         return v;
19298     },
19299     
19300     formatDate : function(date, fmt)
19301     {   
19302         return (!date || !(date instanceof Date)) ?
19303         date : date.dateFormat(fmt || this.format);
19304     },
19305     
19306     onFocus : function()
19307     {
19308         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19309         this.showPopup();
19310     },
19311     
19312     onBlur : function()
19313     {
19314         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19315         
19316         var d = this.inputEl().getValue();
19317         
19318         this.setValue(d);
19319                 
19320         this.hidePopup();
19321     },
19322     
19323     showPopup : function()
19324     {
19325         this.picker().show();
19326         this.update();
19327         this.place();
19328         
19329         this.fireEvent('showpopup', this, this.date);
19330     },
19331     
19332     hidePopup : function()
19333     {
19334         if(this.isInline) {
19335             return;
19336         }
19337         this.picker().hide();
19338         this.viewMode = this.startViewMode;
19339         this.showMode();
19340         
19341         this.fireEvent('hidepopup', this, this.date);
19342         
19343     },
19344     
19345     onMousedown: function(e)
19346     {
19347         e.stopPropagation();
19348         e.preventDefault();
19349     },
19350     
19351     keyup: function(e)
19352     {
19353         Roo.bootstrap.DateField.superclass.keyup.call(this);
19354         this.update();
19355     },
19356
19357     setValue: function(v)
19358     {
19359         if(this.fireEvent('beforeselect', this, v) !== false){
19360             var d = new Date(this.parseDate(v) ).clearTime();
19361         
19362             if(isNaN(d.getTime())){
19363                 this.date = this.viewDate = '';
19364                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19365                 return;
19366             }
19367
19368             v = this.formatDate(d);
19369
19370             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19371
19372             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19373
19374             this.update();
19375
19376             this.fireEvent('select', this, this.date);
19377         }
19378     },
19379     
19380     getValue: function()
19381     {
19382         return this.formatDate(this.date);
19383     },
19384     
19385     fireKey: function(e)
19386     {
19387         if (!this.picker().isVisible()){
19388             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19389                 this.showPopup();
19390             }
19391             return;
19392         }
19393         
19394         var dateChanged = false,
19395         dir, day, month,
19396         newDate, newViewDate;
19397         
19398         switch(e.keyCode){
19399             case 27: // escape
19400                 this.hidePopup();
19401                 e.preventDefault();
19402                 break;
19403             case 37: // left
19404             case 39: // right
19405                 if (!this.keyboardNavigation) {
19406                     break;
19407                 }
19408                 dir = e.keyCode == 37 ? -1 : 1;
19409                 
19410                 if (e.ctrlKey){
19411                     newDate = this.moveYear(this.date, dir);
19412                     newViewDate = this.moveYear(this.viewDate, dir);
19413                 } else if (e.shiftKey){
19414                     newDate = this.moveMonth(this.date, dir);
19415                     newViewDate = this.moveMonth(this.viewDate, dir);
19416                 } else {
19417                     newDate = new Date(this.date);
19418                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19419                     newViewDate = new Date(this.viewDate);
19420                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19421                 }
19422                 if (this.dateWithinRange(newDate)){
19423                     this.date = newDate;
19424                     this.viewDate = newViewDate;
19425                     this.setValue(this.formatDate(this.date));
19426 //                    this.update();
19427                     e.preventDefault();
19428                     dateChanged = true;
19429                 }
19430                 break;
19431             case 38: // up
19432             case 40: // down
19433                 if (!this.keyboardNavigation) {
19434                     break;
19435                 }
19436                 dir = e.keyCode == 38 ? -1 : 1;
19437                 if (e.ctrlKey){
19438                     newDate = this.moveYear(this.date, dir);
19439                     newViewDate = this.moveYear(this.viewDate, dir);
19440                 } else if (e.shiftKey){
19441                     newDate = this.moveMonth(this.date, dir);
19442                     newViewDate = this.moveMonth(this.viewDate, dir);
19443                 } else {
19444                     newDate = new Date(this.date);
19445                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19446                     newViewDate = new Date(this.viewDate);
19447                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19448                 }
19449                 if (this.dateWithinRange(newDate)){
19450                     this.date = newDate;
19451                     this.viewDate = newViewDate;
19452                     this.setValue(this.formatDate(this.date));
19453 //                    this.update();
19454                     e.preventDefault();
19455                     dateChanged = true;
19456                 }
19457                 break;
19458             case 13: // enter
19459                 this.setValue(this.formatDate(this.date));
19460                 this.hidePopup();
19461                 e.preventDefault();
19462                 break;
19463             case 9: // tab
19464                 this.setValue(this.formatDate(this.date));
19465                 this.hidePopup();
19466                 break;
19467             case 16: // shift
19468             case 17: // ctrl
19469             case 18: // alt
19470                 break;
19471             default :
19472                 this.hidePopup();
19473                 
19474         }
19475     },
19476     
19477     
19478     onClick: function(e) 
19479     {
19480         e.stopPropagation();
19481         e.preventDefault();
19482         
19483         var target = e.getTarget();
19484         
19485         if(target.nodeName.toLowerCase() === 'i'){
19486             target = Roo.get(target).dom.parentNode;
19487         }
19488         
19489         var nodeName = target.nodeName;
19490         var className = target.className;
19491         var html = target.innerHTML;
19492         //Roo.log(nodeName);
19493         
19494         switch(nodeName.toLowerCase()) {
19495             case 'th':
19496                 switch(className) {
19497                     case 'switch':
19498                         this.showMode(1);
19499                         break;
19500                     case 'prev':
19501                     case 'next':
19502                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19503                         switch(this.viewMode){
19504                                 case 0:
19505                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19506                                         break;
19507                                 case 1:
19508                                 case 2:
19509                                         this.viewDate = this.moveYear(this.viewDate, dir);
19510                                         break;
19511                         }
19512                         this.fill();
19513                         break;
19514                     case 'today':
19515                         var date = new Date();
19516                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19517 //                        this.fill()
19518                         this.setValue(this.formatDate(this.date));
19519                         
19520                         this.hidePopup();
19521                         break;
19522                 }
19523                 break;
19524             case 'span':
19525                 if (className.indexOf('disabled') < 0) {
19526                     this.viewDate.setUTCDate(1);
19527                     if (className.indexOf('month') > -1) {
19528                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19529                     } else {
19530                         var year = parseInt(html, 10) || 0;
19531                         this.viewDate.setUTCFullYear(year);
19532                         
19533                     }
19534                     
19535                     if(this.singleMode){
19536                         this.setValue(this.formatDate(this.viewDate));
19537                         this.hidePopup();
19538                         return;
19539                     }
19540                     
19541                     this.showMode(-1);
19542                     this.fill();
19543                 }
19544                 break;
19545                 
19546             case 'td':
19547                 //Roo.log(className);
19548                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19549                     var day = parseInt(html, 10) || 1;
19550                     var year = this.viewDate.getUTCFullYear(),
19551                         month = this.viewDate.getUTCMonth();
19552
19553                     if (className.indexOf('old') > -1) {
19554                         if(month === 0 ){
19555                             month = 11;
19556                             year -= 1;
19557                         }else{
19558                             month -= 1;
19559                         }
19560                     } else if (className.indexOf('new') > -1) {
19561                         if (month == 11) {
19562                             month = 0;
19563                             year += 1;
19564                         } else {
19565                             month += 1;
19566                         }
19567                     }
19568                     //Roo.log([year,month,day]);
19569                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19570                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19571 //                    this.fill();
19572                     //Roo.log(this.formatDate(this.date));
19573                     this.setValue(this.formatDate(this.date));
19574                     this.hidePopup();
19575                 }
19576                 break;
19577         }
19578     },
19579     
19580     setStartDate: function(startDate)
19581     {
19582         this.startDate = startDate || -Infinity;
19583         if (this.startDate !== -Infinity) {
19584             this.startDate = this.parseDate(this.startDate);
19585         }
19586         this.update();
19587         this.updateNavArrows();
19588     },
19589
19590     setEndDate: function(endDate)
19591     {
19592         this.endDate = endDate || Infinity;
19593         if (this.endDate !== Infinity) {
19594             this.endDate = this.parseDate(this.endDate);
19595         }
19596         this.update();
19597         this.updateNavArrows();
19598     },
19599     
19600     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19601     {
19602         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19603         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19604             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19605         }
19606         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19607             return parseInt(d, 10);
19608         });
19609         this.update();
19610         this.updateNavArrows();
19611     },
19612     
19613     updateNavArrows: function() 
19614     {
19615         if(this.singleMode){
19616             return;
19617         }
19618         
19619         var d = new Date(this.viewDate),
19620         year = d.getUTCFullYear(),
19621         month = d.getUTCMonth();
19622         
19623         Roo.each(this.picker().select('.prev', true).elements, function(v){
19624             v.show();
19625             switch (this.viewMode) {
19626                 case 0:
19627
19628                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19629                         v.hide();
19630                     }
19631                     break;
19632                 case 1:
19633                 case 2:
19634                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19635                         v.hide();
19636                     }
19637                     break;
19638             }
19639         });
19640         
19641         Roo.each(this.picker().select('.next', true).elements, function(v){
19642             v.show();
19643             switch (this.viewMode) {
19644                 case 0:
19645
19646                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19647                         v.hide();
19648                     }
19649                     break;
19650                 case 1:
19651                 case 2:
19652                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19653                         v.hide();
19654                     }
19655                     break;
19656             }
19657         })
19658     },
19659     
19660     moveMonth: function(date, dir)
19661     {
19662         if (!dir) {
19663             return date;
19664         }
19665         var new_date = new Date(date.valueOf()),
19666         day = new_date.getUTCDate(),
19667         month = new_date.getUTCMonth(),
19668         mag = Math.abs(dir),
19669         new_month, test;
19670         dir = dir > 0 ? 1 : -1;
19671         if (mag == 1){
19672             test = dir == -1
19673             // If going back one month, make sure month is not current month
19674             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19675             ? function(){
19676                 return new_date.getUTCMonth() == month;
19677             }
19678             // If going forward one month, make sure month is as expected
19679             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19680             : function(){
19681                 return new_date.getUTCMonth() != new_month;
19682             };
19683             new_month = month + dir;
19684             new_date.setUTCMonth(new_month);
19685             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19686             if (new_month < 0 || new_month > 11) {
19687                 new_month = (new_month + 12) % 12;
19688             }
19689         } else {
19690             // For magnitudes >1, move one month at a time...
19691             for (var i=0; i<mag; i++) {
19692                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19693                 new_date = this.moveMonth(new_date, dir);
19694             }
19695             // ...then reset the day, keeping it in the new month
19696             new_month = new_date.getUTCMonth();
19697             new_date.setUTCDate(day);
19698             test = function(){
19699                 return new_month != new_date.getUTCMonth();
19700             };
19701         }
19702         // Common date-resetting loop -- if date is beyond end of month, make it
19703         // end of month
19704         while (test()){
19705             new_date.setUTCDate(--day);
19706             new_date.setUTCMonth(new_month);
19707         }
19708         return new_date;
19709     },
19710
19711     moveYear: function(date, dir)
19712     {
19713         return this.moveMonth(date, dir*12);
19714     },
19715
19716     dateWithinRange: function(date)
19717     {
19718         return date >= this.startDate && date <= this.endDate;
19719     },
19720
19721     
19722     remove: function() 
19723     {
19724         this.picker().remove();
19725     },
19726     
19727     validateValue : function(value)
19728     {
19729         if(this.getVisibilityEl().hasClass('hidden')){
19730             return true;
19731         }
19732         
19733         if(value.length < 1)  {
19734             if(this.allowBlank){
19735                 return true;
19736             }
19737             return false;
19738         }
19739         
19740         if(value.length < this.minLength){
19741             return false;
19742         }
19743         if(value.length > this.maxLength){
19744             return false;
19745         }
19746         if(this.vtype){
19747             var vt = Roo.form.VTypes;
19748             if(!vt[this.vtype](value, this)){
19749                 return false;
19750             }
19751         }
19752         if(typeof this.validator == "function"){
19753             var msg = this.validator(value);
19754             if(msg !== true){
19755                 return false;
19756             }
19757         }
19758         
19759         if(this.regex && !this.regex.test(value)){
19760             return false;
19761         }
19762         
19763         if(typeof(this.parseDate(value)) == 'undefined'){
19764             return false;
19765         }
19766         
19767         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19768             return false;
19769         }      
19770         
19771         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19772             return false;
19773         } 
19774         
19775         
19776         return true;
19777     },
19778     
19779     reset : function()
19780     {
19781         this.date = this.viewDate = '';
19782         
19783         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19784     }
19785    
19786 });
19787
19788 Roo.apply(Roo.bootstrap.DateField,  {
19789     
19790     head : {
19791         tag: 'thead',
19792         cn: [
19793         {
19794             tag: 'tr',
19795             cn: [
19796             {
19797                 tag: 'th',
19798                 cls: 'prev',
19799                 html: '<i class="fa fa-arrow-left"/>'
19800             },
19801             {
19802                 tag: 'th',
19803                 cls: 'switch',
19804                 colspan: '5'
19805             },
19806             {
19807                 tag: 'th',
19808                 cls: 'next',
19809                 html: '<i class="fa fa-arrow-right"/>'
19810             }
19811
19812             ]
19813         }
19814         ]
19815     },
19816     
19817     content : {
19818         tag: 'tbody',
19819         cn: [
19820         {
19821             tag: 'tr',
19822             cn: [
19823             {
19824                 tag: 'td',
19825                 colspan: '7'
19826             }
19827             ]
19828         }
19829         ]
19830     },
19831     
19832     footer : {
19833         tag: 'tfoot',
19834         cn: [
19835         {
19836             tag: 'tr',
19837             cn: [
19838             {
19839                 tag: 'th',
19840                 colspan: '7',
19841                 cls: 'today'
19842             }
19843                     
19844             ]
19845         }
19846         ]
19847     },
19848     
19849     dates:{
19850         en: {
19851             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19852             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19853             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19854             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19855             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19856             today: "Today"
19857         }
19858     },
19859     
19860     modes: [
19861     {
19862         clsName: 'days',
19863         navFnc: 'Month',
19864         navStep: 1
19865     },
19866     {
19867         clsName: 'months',
19868         navFnc: 'FullYear',
19869         navStep: 1
19870     },
19871     {
19872         clsName: 'years',
19873         navFnc: 'FullYear',
19874         navStep: 10
19875     }]
19876 });
19877
19878 Roo.apply(Roo.bootstrap.DateField,  {
19879   
19880     template : {
19881         tag: 'div',
19882         cls: 'datepicker dropdown-menu roo-dynamic',
19883         cn: [
19884         {
19885             tag: 'div',
19886             cls: 'datepicker-days',
19887             cn: [
19888             {
19889                 tag: 'table',
19890                 cls: 'table-condensed',
19891                 cn:[
19892                 Roo.bootstrap.DateField.head,
19893                 {
19894                     tag: 'tbody'
19895                 },
19896                 Roo.bootstrap.DateField.footer
19897                 ]
19898             }
19899             ]
19900         },
19901         {
19902             tag: 'div',
19903             cls: 'datepicker-months',
19904             cn: [
19905             {
19906                 tag: 'table',
19907                 cls: 'table-condensed',
19908                 cn:[
19909                 Roo.bootstrap.DateField.head,
19910                 Roo.bootstrap.DateField.content,
19911                 Roo.bootstrap.DateField.footer
19912                 ]
19913             }
19914             ]
19915         },
19916         {
19917             tag: 'div',
19918             cls: 'datepicker-years',
19919             cn: [
19920             {
19921                 tag: 'table',
19922                 cls: 'table-condensed',
19923                 cn:[
19924                 Roo.bootstrap.DateField.head,
19925                 Roo.bootstrap.DateField.content,
19926                 Roo.bootstrap.DateField.footer
19927                 ]
19928             }
19929             ]
19930         }
19931         ]
19932     }
19933 });
19934
19935  
19936
19937  /*
19938  * - LGPL
19939  *
19940  * TimeField
19941  * 
19942  */
19943
19944 /**
19945  * @class Roo.bootstrap.TimeField
19946  * @extends Roo.bootstrap.Input
19947  * Bootstrap DateField class
19948  * 
19949  * 
19950  * @constructor
19951  * Create a new TimeField
19952  * @param {Object} config The config object
19953  */
19954
19955 Roo.bootstrap.TimeField = function(config){
19956     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19957     this.addEvents({
19958             /**
19959              * @event show
19960              * Fires when this field show.
19961              * @param {Roo.bootstrap.DateField} thisthis
19962              * @param {Mixed} date The date value
19963              */
19964             show : true,
19965             /**
19966              * @event show
19967              * Fires when this field hide.
19968              * @param {Roo.bootstrap.DateField} this
19969              * @param {Mixed} date The date value
19970              */
19971             hide : true,
19972             /**
19973              * @event select
19974              * Fires when select a date.
19975              * @param {Roo.bootstrap.DateField} this
19976              * @param {Mixed} date The date value
19977              */
19978             select : true
19979         });
19980 };
19981
19982 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19983     
19984     /**
19985      * @cfg {String} format
19986      * The default time format string which can be overriden for localization support.  The format must be
19987      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19988      */
19989     format : "H:i",
19990        
19991     onRender: function(ct, position)
19992     {
19993         
19994         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19995                 
19996         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19997         
19998         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19999         
20000         this.pop = this.picker().select('>.datepicker-time',true).first();
20001         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20002         
20003         this.picker().on('mousedown', this.onMousedown, this);
20004         this.picker().on('click', this.onClick, this);
20005         
20006         this.picker().addClass('datepicker-dropdown');
20007     
20008         this.fillTime();
20009         this.update();
20010             
20011         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20012         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20013         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20014         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20015         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20016         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20017
20018     },
20019     
20020     fireKey: function(e){
20021         if (!this.picker().isVisible()){
20022             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20023                 this.show();
20024             }
20025             return;
20026         }
20027
20028         e.preventDefault();
20029         
20030         switch(e.keyCode){
20031             case 27: // escape
20032                 this.hide();
20033                 break;
20034             case 37: // left
20035             case 39: // right
20036                 this.onTogglePeriod();
20037                 break;
20038             case 38: // up
20039                 this.onIncrementMinutes();
20040                 break;
20041             case 40: // down
20042                 this.onDecrementMinutes();
20043                 break;
20044             case 13: // enter
20045             case 9: // tab
20046                 this.setTime();
20047                 break;
20048         }
20049     },
20050     
20051     onClick: function(e) {
20052         e.stopPropagation();
20053         e.preventDefault();
20054     },
20055     
20056     picker : function()
20057     {
20058         return this.el.select('.datepicker', true).first();
20059     },
20060     
20061     fillTime: function()
20062     {    
20063         var time = this.pop.select('tbody', true).first();
20064         
20065         time.dom.innerHTML = '';
20066         
20067         time.createChild({
20068             tag: 'tr',
20069             cn: [
20070                 {
20071                     tag: 'td',
20072                     cn: [
20073                         {
20074                             tag: 'a',
20075                             href: '#',
20076                             cls: 'btn',
20077                             cn: [
20078                                 {
20079                                     tag: 'span',
20080                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20081                                 }
20082                             ]
20083                         } 
20084                     ]
20085                 },
20086                 {
20087                     tag: 'td',
20088                     cls: 'separator'
20089                 },
20090                 {
20091                     tag: 'td',
20092                     cn: [
20093                         {
20094                             tag: 'a',
20095                             href: '#',
20096                             cls: 'btn',
20097                             cn: [
20098                                 {
20099                                     tag: 'span',
20100                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20101                                 }
20102                             ]
20103                         }
20104                     ]
20105                 },
20106                 {
20107                     tag: 'td',
20108                     cls: 'separator'
20109                 }
20110             ]
20111         });
20112         
20113         time.createChild({
20114             tag: 'tr',
20115             cn: [
20116                 {
20117                     tag: 'td',
20118                     cn: [
20119                         {
20120                             tag: 'span',
20121                             cls: 'timepicker-hour',
20122                             html: '00'
20123                         }  
20124                     ]
20125                 },
20126                 {
20127                     tag: 'td',
20128                     cls: 'separator',
20129                     html: ':'
20130                 },
20131                 {
20132                     tag: 'td',
20133                     cn: [
20134                         {
20135                             tag: 'span',
20136                             cls: 'timepicker-minute',
20137                             html: '00'
20138                         }  
20139                     ]
20140                 },
20141                 {
20142                     tag: 'td',
20143                     cls: 'separator'
20144                 },
20145                 {
20146                     tag: 'td',
20147                     cn: [
20148                         {
20149                             tag: 'button',
20150                             type: 'button',
20151                             cls: 'btn btn-primary period',
20152                             html: 'AM'
20153                             
20154                         }
20155                     ]
20156                 }
20157             ]
20158         });
20159         
20160         time.createChild({
20161             tag: 'tr',
20162             cn: [
20163                 {
20164                     tag: 'td',
20165                     cn: [
20166                         {
20167                             tag: 'a',
20168                             href: '#',
20169                             cls: 'btn',
20170                             cn: [
20171                                 {
20172                                     tag: 'span',
20173                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20174                                 }
20175                             ]
20176                         }
20177                     ]
20178                 },
20179                 {
20180                     tag: 'td',
20181                     cls: 'separator'
20182                 },
20183                 {
20184                     tag: 'td',
20185                     cn: [
20186                         {
20187                             tag: 'a',
20188                             href: '#',
20189                             cls: 'btn',
20190                             cn: [
20191                                 {
20192                                     tag: 'span',
20193                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20194                                 }
20195                             ]
20196                         }
20197                     ]
20198                 },
20199                 {
20200                     tag: 'td',
20201                     cls: 'separator'
20202                 }
20203             ]
20204         });
20205         
20206     },
20207     
20208     update: function()
20209     {
20210         
20211         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20212         
20213         this.fill();
20214     },
20215     
20216     fill: function() 
20217     {
20218         var hours = this.time.getHours();
20219         var minutes = this.time.getMinutes();
20220         var period = 'AM';
20221         
20222         if(hours > 11){
20223             period = 'PM';
20224         }
20225         
20226         if(hours == 0){
20227             hours = 12;
20228         }
20229         
20230         
20231         if(hours > 12){
20232             hours = hours - 12;
20233         }
20234         
20235         if(hours < 10){
20236             hours = '0' + hours;
20237         }
20238         
20239         if(minutes < 10){
20240             minutes = '0' + minutes;
20241         }
20242         
20243         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20244         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20245         this.pop.select('button', true).first().dom.innerHTML = period;
20246         
20247     },
20248     
20249     place: function()
20250     {   
20251         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20252         
20253         var cls = ['bottom'];
20254         
20255         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20256             cls.pop();
20257             cls.push('top');
20258         }
20259         
20260         cls.push('right');
20261         
20262         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20263             cls.pop();
20264             cls.push('left');
20265         }
20266         
20267         this.picker().addClass(cls.join('-'));
20268         
20269         var _this = this;
20270         
20271         Roo.each(cls, function(c){
20272             if(c == 'bottom'){
20273                 _this.picker().setTop(_this.inputEl().getHeight());
20274                 return;
20275             }
20276             if(c == 'top'){
20277                 _this.picker().setTop(0 - _this.picker().getHeight());
20278                 return;
20279             }
20280             
20281             if(c == 'left'){
20282                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20283                 return;
20284             }
20285             if(c == 'right'){
20286                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20287                 return;
20288             }
20289         });
20290         
20291     },
20292   
20293     onFocus : function()
20294     {
20295         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20296         this.show();
20297     },
20298     
20299     onBlur : function()
20300     {
20301         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20302         this.hide();
20303     },
20304     
20305     show : function()
20306     {
20307         this.picker().show();
20308         this.pop.show();
20309         this.update();
20310         this.place();
20311         
20312         this.fireEvent('show', this, this.date);
20313     },
20314     
20315     hide : function()
20316     {
20317         this.picker().hide();
20318         this.pop.hide();
20319         
20320         this.fireEvent('hide', this, this.date);
20321     },
20322     
20323     setTime : function()
20324     {
20325         this.hide();
20326         this.setValue(this.time.format(this.format));
20327         
20328         this.fireEvent('select', this, this.date);
20329         
20330         
20331     },
20332     
20333     onMousedown: function(e){
20334         e.stopPropagation();
20335         e.preventDefault();
20336     },
20337     
20338     onIncrementHours: function()
20339     {
20340         Roo.log('onIncrementHours');
20341         this.time = this.time.add(Date.HOUR, 1);
20342         this.update();
20343         
20344     },
20345     
20346     onDecrementHours: function()
20347     {
20348         Roo.log('onDecrementHours');
20349         this.time = this.time.add(Date.HOUR, -1);
20350         this.update();
20351     },
20352     
20353     onIncrementMinutes: function()
20354     {
20355         Roo.log('onIncrementMinutes');
20356         this.time = this.time.add(Date.MINUTE, 1);
20357         this.update();
20358     },
20359     
20360     onDecrementMinutes: function()
20361     {
20362         Roo.log('onDecrementMinutes');
20363         this.time = this.time.add(Date.MINUTE, -1);
20364         this.update();
20365     },
20366     
20367     onTogglePeriod: function()
20368     {
20369         Roo.log('onTogglePeriod');
20370         this.time = this.time.add(Date.HOUR, 12);
20371         this.update();
20372     }
20373     
20374    
20375 });
20376
20377 Roo.apply(Roo.bootstrap.TimeField,  {
20378     
20379     content : {
20380         tag: 'tbody',
20381         cn: [
20382             {
20383                 tag: 'tr',
20384                 cn: [
20385                 {
20386                     tag: 'td',
20387                     colspan: '7'
20388                 }
20389                 ]
20390             }
20391         ]
20392     },
20393     
20394     footer : {
20395         tag: 'tfoot',
20396         cn: [
20397             {
20398                 tag: 'tr',
20399                 cn: [
20400                 {
20401                     tag: 'th',
20402                     colspan: '7',
20403                     cls: '',
20404                     cn: [
20405                         {
20406                             tag: 'button',
20407                             cls: 'btn btn-info ok',
20408                             html: 'OK'
20409                         }
20410                     ]
20411                 }
20412
20413                 ]
20414             }
20415         ]
20416     }
20417 });
20418
20419 Roo.apply(Roo.bootstrap.TimeField,  {
20420   
20421     template : {
20422         tag: 'div',
20423         cls: 'datepicker dropdown-menu',
20424         cn: [
20425             {
20426                 tag: 'div',
20427                 cls: 'datepicker-time',
20428                 cn: [
20429                 {
20430                     tag: 'table',
20431                     cls: 'table-condensed',
20432                     cn:[
20433                     Roo.bootstrap.TimeField.content,
20434                     Roo.bootstrap.TimeField.footer
20435                     ]
20436                 }
20437                 ]
20438             }
20439         ]
20440     }
20441 });
20442
20443  
20444
20445  /*
20446  * - LGPL
20447  *
20448  * MonthField
20449  * 
20450  */
20451
20452 /**
20453  * @class Roo.bootstrap.MonthField
20454  * @extends Roo.bootstrap.Input
20455  * Bootstrap MonthField class
20456  * 
20457  * @cfg {String} language default en
20458  * 
20459  * @constructor
20460  * Create a new MonthField
20461  * @param {Object} config The config object
20462  */
20463
20464 Roo.bootstrap.MonthField = function(config){
20465     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20466     
20467     this.addEvents({
20468         /**
20469          * @event show
20470          * Fires when this field show.
20471          * @param {Roo.bootstrap.MonthField} this
20472          * @param {Mixed} date The date value
20473          */
20474         show : true,
20475         /**
20476          * @event show
20477          * Fires when this field hide.
20478          * @param {Roo.bootstrap.MonthField} this
20479          * @param {Mixed} date The date value
20480          */
20481         hide : true,
20482         /**
20483          * @event select
20484          * Fires when select a date.
20485          * @param {Roo.bootstrap.MonthField} this
20486          * @param {String} oldvalue The old value
20487          * @param {String} newvalue The new value
20488          */
20489         select : true
20490     });
20491 };
20492
20493 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20494     
20495     onRender: function(ct, position)
20496     {
20497         
20498         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20499         
20500         this.language = this.language || 'en';
20501         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20502         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20503         
20504         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20505         this.isInline = false;
20506         this.isInput = true;
20507         this.component = this.el.select('.add-on', true).first() || false;
20508         this.component = (this.component && this.component.length === 0) ? false : this.component;
20509         this.hasInput = this.component && this.inputEL().length;
20510         
20511         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20512         
20513         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20514         
20515         this.picker().on('mousedown', this.onMousedown, this);
20516         this.picker().on('click', this.onClick, this);
20517         
20518         this.picker().addClass('datepicker-dropdown');
20519         
20520         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20521             v.setStyle('width', '189px');
20522         });
20523         
20524         this.fillMonths();
20525         
20526         this.update();
20527         
20528         if(this.isInline) {
20529             this.show();
20530         }
20531         
20532     },
20533     
20534     setValue: function(v, suppressEvent)
20535     {   
20536         var o = this.getValue();
20537         
20538         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20539         
20540         this.update();
20541
20542         if(suppressEvent !== true){
20543             this.fireEvent('select', this, o, v);
20544         }
20545         
20546     },
20547     
20548     getValue: function()
20549     {
20550         return this.value;
20551     },
20552     
20553     onClick: function(e) 
20554     {
20555         e.stopPropagation();
20556         e.preventDefault();
20557         
20558         var target = e.getTarget();
20559         
20560         if(target.nodeName.toLowerCase() === 'i'){
20561             target = Roo.get(target).dom.parentNode;
20562         }
20563         
20564         var nodeName = target.nodeName;
20565         var className = target.className;
20566         var html = target.innerHTML;
20567         
20568         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20569             return;
20570         }
20571         
20572         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20573         
20574         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20575         
20576         this.hide();
20577                         
20578     },
20579     
20580     picker : function()
20581     {
20582         return this.pickerEl;
20583     },
20584     
20585     fillMonths: function()
20586     {    
20587         var i = 0;
20588         var months = this.picker().select('>.datepicker-months td', true).first();
20589         
20590         months.dom.innerHTML = '';
20591         
20592         while (i < 12) {
20593             var month = {
20594                 tag: 'span',
20595                 cls: 'month',
20596                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20597             };
20598             
20599             months.createChild(month);
20600         }
20601         
20602     },
20603     
20604     update: function()
20605     {
20606         var _this = this;
20607         
20608         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20609             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20610         }
20611         
20612         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20613             e.removeClass('active');
20614             
20615             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20616                 e.addClass('active');
20617             }
20618         })
20619     },
20620     
20621     place: function()
20622     {
20623         if(this.isInline) {
20624             return;
20625         }
20626         
20627         this.picker().removeClass(['bottom', 'top']);
20628         
20629         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20630             /*
20631              * place to the top of element!
20632              *
20633              */
20634             
20635             this.picker().addClass('top');
20636             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20637             
20638             return;
20639         }
20640         
20641         this.picker().addClass('bottom');
20642         
20643         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20644     },
20645     
20646     onFocus : function()
20647     {
20648         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20649         this.show();
20650     },
20651     
20652     onBlur : function()
20653     {
20654         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20655         
20656         var d = this.inputEl().getValue();
20657         
20658         this.setValue(d);
20659                 
20660         this.hide();
20661     },
20662     
20663     show : function()
20664     {
20665         this.picker().show();
20666         this.picker().select('>.datepicker-months', true).first().show();
20667         this.update();
20668         this.place();
20669         
20670         this.fireEvent('show', this, this.date);
20671     },
20672     
20673     hide : function()
20674     {
20675         if(this.isInline) {
20676             return;
20677         }
20678         this.picker().hide();
20679         this.fireEvent('hide', this, this.date);
20680         
20681     },
20682     
20683     onMousedown: function(e)
20684     {
20685         e.stopPropagation();
20686         e.preventDefault();
20687     },
20688     
20689     keyup: function(e)
20690     {
20691         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20692         this.update();
20693     },
20694
20695     fireKey: function(e)
20696     {
20697         if (!this.picker().isVisible()){
20698             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20699                 this.show();
20700             }
20701             return;
20702         }
20703         
20704         var dir;
20705         
20706         switch(e.keyCode){
20707             case 27: // escape
20708                 this.hide();
20709                 e.preventDefault();
20710                 break;
20711             case 37: // left
20712             case 39: // right
20713                 dir = e.keyCode == 37 ? -1 : 1;
20714                 
20715                 this.vIndex = this.vIndex + dir;
20716                 
20717                 if(this.vIndex < 0){
20718                     this.vIndex = 0;
20719                 }
20720                 
20721                 if(this.vIndex > 11){
20722                     this.vIndex = 11;
20723                 }
20724                 
20725                 if(isNaN(this.vIndex)){
20726                     this.vIndex = 0;
20727                 }
20728                 
20729                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20730                 
20731                 break;
20732             case 38: // up
20733             case 40: // down
20734                 
20735                 dir = e.keyCode == 38 ? -1 : 1;
20736                 
20737                 this.vIndex = this.vIndex + dir * 4;
20738                 
20739                 if(this.vIndex < 0){
20740                     this.vIndex = 0;
20741                 }
20742                 
20743                 if(this.vIndex > 11){
20744                     this.vIndex = 11;
20745                 }
20746                 
20747                 if(isNaN(this.vIndex)){
20748                     this.vIndex = 0;
20749                 }
20750                 
20751                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20752                 break;
20753                 
20754             case 13: // enter
20755                 
20756                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20757                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20758                 }
20759                 
20760                 this.hide();
20761                 e.preventDefault();
20762                 break;
20763             case 9: // tab
20764                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20765                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20766                 }
20767                 this.hide();
20768                 break;
20769             case 16: // shift
20770             case 17: // ctrl
20771             case 18: // alt
20772                 break;
20773             default :
20774                 this.hide();
20775                 
20776         }
20777     },
20778     
20779     remove: function() 
20780     {
20781         this.picker().remove();
20782     }
20783    
20784 });
20785
20786 Roo.apply(Roo.bootstrap.MonthField,  {
20787     
20788     content : {
20789         tag: 'tbody',
20790         cn: [
20791         {
20792             tag: 'tr',
20793             cn: [
20794             {
20795                 tag: 'td',
20796                 colspan: '7'
20797             }
20798             ]
20799         }
20800         ]
20801     },
20802     
20803     dates:{
20804         en: {
20805             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20806             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20807         }
20808     }
20809 });
20810
20811 Roo.apply(Roo.bootstrap.MonthField,  {
20812   
20813     template : {
20814         tag: 'div',
20815         cls: 'datepicker dropdown-menu roo-dynamic',
20816         cn: [
20817             {
20818                 tag: 'div',
20819                 cls: 'datepicker-months',
20820                 cn: [
20821                 {
20822                     tag: 'table',
20823                     cls: 'table-condensed',
20824                     cn:[
20825                         Roo.bootstrap.DateField.content
20826                     ]
20827                 }
20828                 ]
20829             }
20830         ]
20831     }
20832 });
20833
20834  
20835
20836  
20837  /*
20838  * - LGPL
20839  *
20840  * CheckBox
20841  * 
20842  */
20843
20844 /**
20845  * @class Roo.bootstrap.CheckBox
20846  * @extends Roo.bootstrap.Input
20847  * Bootstrap CheckBox class
20848  * 
20849  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20850  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20851  * @cfg {String} boxLabel The text that appears beside the checkbox
20852  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20853  * @cfg {Boolean} checked initnal the element
20854  * @cfg {Boolean} inline inline the element (default false)
20855  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20856  * @cfg {String} tooltip label tooltip
20857  * 
20858  * @constructor
20859  * Create a new CheckBox
20860  * @param {Object} config The config object
20861  */
20862
20863 Roo.bootstrap.CheckBox = function(config){
20864     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20865    
20866     this.addEvents({
20867         /**
20868         * @event check
20869         * Fires when the element is checked or unchecked.
20870         * @param {Roo.bootstrap.CheckBox} this This input
20871         * @param {Boolean} checked The new checked value
20872         */
20873        check : true,
20874        /**
20875         * @event click
20876         * Fires when the element is click.
20877         * @param {Roo.bootstrap.CheckBox} this This input
20878         */
20879        click : true
20880     });
20881     
20882 };
20883
20884 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20885   
20886     inputType: 'checkbox',
20887     inputValue: 1,
20888     valueOff: 0,
20889     boxLabel: false,
20890     checked: false,
20891     weight : false,
20892     inline: false,
20893     tooltip : '',
20894     
20895     getAutoCreate : function()
20896     {
20897         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20898         
20899         var id = Roo.id();
20900         
20901         var cfg = {};
20902         
20903         cfg.cls = 'form-group ' + this.inputType; //input-group
20904         
20905         if(this.inline){
20906             cfg.cls += ' ' + this.inputType + '-inline';
20907         }
20908         
20909         var input =  {
20910             tag: 'input',
20911             id : id,
20912             type : this.inputType,
20913             value : this.inputValue,
20914             cls : 'roo-' + this.inputType, //'form-box',
20915             placeholder : this.placeholder || ''
20916             
20917         };
20918         
20919         if(this.inputType != 'radio'){
20920             var hidden =  {
20921                 tag: 'input',
20922                 type : 'hidden',
20923                 cls : 'roo-hidden-value',
20924                 value : this.checked ? this.inputValue : this.valueOff
20925             };
20926         }
20927         
20928             
20929         if (this.weight) { // Validity check?
20930             cfg.cls += " " + this.inputType + "-" + this.weight;
20931         }
20932         
20933         if (this.disabled) {
20934             input.disabled=true;
20935         }
20936         
20937         if(this.checked){
20938             input.checked = this.checked;
20939         }
20940         
20941         if (this.name) {
20942             
20943             input.name = this.name;
20944             
20945             if(this.inputType != 'radio'){
20946                 hidden.name = this.name;
20947                 input.name = '_hidden_' + this.name;
20948             }
20949         }
20950         
20951         if (this.size) {
20952             input.cls += ' input-' + this.size;
20953         }
20954         
20955         var settings=this;
20956         
20957         ['xs','sm','md','lg'].map(function(size){
20958             if (settings[size]) {
20959                 cfg.cls += ' col-' + size + '-' + settings[size];
20960             }
20961         });
20962         
20963         var inputblock = input;
20964          
20965         if (this.before || this.after) {
20966             
20967             inputblock = {
20968                 cls : 'input-group',
20969                 cn :  [] 
20970             };
20971             
20972             if (this.before) {
20973                 inputblock.cn.push({
20974                     tag :'span',
20975                     cls : 'input-group-addon',
20976                     html : this.before
20977                 });
20978             }
20979             
20980             inputblock.cn.push(input);
20981             
20982             if(this.inputType != 'radio'){
20983                 inputblock.cn.push(hidden);
20984             }
20985             
20986             if (this.after) {
20987                 inputblock.cn.push({
20988                     tag :'span',
20989                     cls : 'input-group-addon',
20990                     html : this.after
20991                 });
20992             }
20993             
20994         }
20995         
20996         if (align ==='left' && this.fieldLabel.length) {
20997 //                Roo.log("left and has label");
20998             cfg.cn = [
20999                 {
21000                     tag: 'label',
21001                     'for' :  id,
21002                     cls : 'control-label',
21003                     html : this.fieldLabel
21004                 },
21005                 {
21006                     cls : "", 
21007                     cn: [
21008                         inputblock
21009                     ]
21010                 }
21011             ];
21012             
21013             if(this.labelWidth > 12){
21014                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21015             }
21016             
21017             if(this.labelWidth < 13 && this.labelmd == 0){
21018                 this.labelmd = this.labelWidth;
21019             }
21020             
21021             if(this.labellg > 0){
21022                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21023                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21024             }
21025             
21026             if(this.labelmd > 0){
21027                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21028                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21029             }
21030             
21031             if(this.labelsm > 0){
21032                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21033                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21034             }
21035             
21036             if(this.labelxs > 0){
21037                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21038                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21039             }
21040             
21041         } else if ( this.fieldLabel.length) {
21042 //                Roo.log(" label");
21043                 cfg.cn = [
21044                    
21045                     {
21046                         tag: this.boxLabel ? 'span' : 'label',
21047                         'for': id,
21048                         cls: 'control-label box-input-label',
21049                         //cls : 'input-group-addon',
21050                         html : this.fieldLabel
21051                     },
21052                     
21053                     inputblock
21054                     
21055                 ];
21056
21057         } else {
21058             
21059 //                Roo.log(" no label && no align");
21060                 cfg.cn = [  inputblock ] ;
21061                 
21062                 
21063         }
21064         
21065         if(this.boxLabel){
21066              var boxLabelCfg = {
21067                 tag: 'label',
21068                 //'for': id, // box label is handled by onclick - so no for...
21069                 cls: 'box-label',
21070                 html: this.boxLabel
21071             };
21072             
21073             if(this.tooltip){
21074                 boxLabelCfg.tooltip = this.tooltip;
21075             }
21076              
21077             cfg.cn.push(boxLabelCfg);
21078         }
21079         
21080         if(this.inputType != 'radio'){
21081             cfg.cn.push(hidden);
21082         }
21083         
21084         return cfg;
21085         
21086     },
21087     
21088     /**
21089      * return the real input element.
21090      */
21091     inputEl: function ()
21092     {
21093         return this.el.select('input.roo-' + this.inputType,true).first();
21094     },
21095     hiddenEl: function ()
21096     {
21097         return this.el.select('input.roo-hidden-value',true).first();
21098     },
21099     
21100     labelEl: function()
21101     {
21102         return this.el.select('label.control-label',true).first();
21103     },
21104     /* depricated... */
21105     
21106     label: function()
21107     {
21108         return this.labelEl();
21109     },
21110     
21111     boxLabelEl: function()
21112     {
21113         return this.el.select('label.box-label',true).first();
21114     },
21115     
21116     initEvents : function()
21117     {
21118 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21119         
21120         this.inputEl().on('click', this.onClick,  this);
21121         
21122         if (this.boxLabel) { 
21123             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21124         }
21125         
21126         this.startValue = this.getValue();
21127         
21128         if(this.groupId){
21129             Roo.bootstrap.CheckBox.register(this);
21130         }
21131     },
21132     
21133     onClick : function(e)
21134     {   
21135         if(this.fireEvent('click', this, e) !== false){
21136             this.setChecked(!this.checked);
21137         }
21138         
21139     },
21140     
21141     setChecked : function(state,suppressEvent)
21142     {
21143         this.startValue = this.getValue();
21144
21145         if(this.inputType == 'radio'){
21146             
21147             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21148                 e.dom.checked = false;
21149             });
21150             
21151             this.inputEl().dom.checked = true;
21152             
21153             this.inputEl().dom.value = this.inputValue;
21154             
21155             if(suppressEvent !== true){
21156                 this.fireEvent('check', this, true);
21157             }
21158             
21159             this.validate();
21160             
21161             return;
21162         }
21163         
21164         this.checked = state;
21165         
21166         this.inputEl().dom.checked = state;
21167         
21168         
21169         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21170         
21171         if(suppressEvent !== true){
21172             this.fireEvent('check', this, state);
21173         }
21174         
21175         this.validate();
21176     },
21177     
21178     getValue : function()
21179     {
21180         if(this.inputType == 'radio'){
21181             return this.getGroupValue();
21182         }
21183         
21184         return this.hiddenEl().dom.value;
21185         
21186     },
21187     
21188     getGroupValue : function()
21189     {
21190         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21191             return '';
21192         }
21193         
21194         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21195     },
21196     
21197     setValue : function(v,suppressEvent)
21198     {
21199         if(this.inputType == 'radio'){
21200             this.setGroupValue(v, suppressEvent);
21201             return;
21202         }
21203         
21204         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21205         
21206         this.validate();
21207     },
21208     
21209     setGroupValue : function(v, suppressEvent)
21210     {
21211         this.startValue = this.getValue();
21212         
21213         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21214             e.dom.checked = false;
21215             
21216             if(e.dom.value == v){
21217                 e.dom.checked = true;
21218             }
21219         });
21220         
21221         if(suppressEvent !== true){
21222             this.fireEvent('check', this, true);
21223         }
21224
21225         this.validate();
21226         
21227         return;
21228     },
21229     
21230     validate : function()
21231     {
21232         if(this.getVisibilityEl().hasClass('hidden')){
21233             return true;
21234         }
21235         
21236         if(
21237                 this.disabled || 
21238                 (this.inputType == 'radio' && this.validateRadio()) ||
21239                 (this.inputType == 'checkbox' && this.validateCheckbox())
21240         ){
21241             this.markValid();
21242             return true;
21243         }
21244         
21245         this.markInvalid();
21246         return false;
21247     },
21248     
21249     validateRadio : function()
21250     {
21251         if(this.getVisibilityEl().hasClass('hidden')){
21252             return true;
21253         }
21254         
21255         if(this.allowBlank){
21256             return true;
21257         }
21258         
21259         var valid = false;
21260         
21261         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21262             if(!e.dom.checked){
21263                 return;
21264             }
21265             
21266             valid = true;
21267             
21268             return false;
21269         });
21270         
21271         return valid;
21272     },
21273     
21274     validateCheckbox : function()
21275     {
21276         if(!this.groupId){
21277             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21278             //return (this.getValue() == this.inputValue) ? true : false;
21279         }
21280         
21281         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21282         
21283         if(!group){
21284             return false;
21285         }
21286         
21287         var r = false;
21288         
21289         for(var i in group){
21290             if(group[i].el.isVisible(true)){
21291                 r = false;
21292                 break;
21293             }
21294             
21295             r = true;
21296         }
21297         
21298         for(var i in group){
21299             if(r){
21300                 break;
21301             }
21302             
21303             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21304         }
21305         
21306         return r;
21307     },
21308     
21309     /**
21310      * Mark this field as valid
21311      */
21312     markValid : function()
21313     {
21314         var _this = this;
21315         
21316         this.fireEvent('valid', this);
21317         
21318         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21319         
21320         if(this.groupId){
21321             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21322         }
21323         
21324         if(label){
21325             label.markValid();
21326         }
21327
21328         if(this.inputType == 'radio'){
21329             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21330                 var fg = e.findParent('.form-group', false, true);
21331                 if (Roo.bootstrap.version == 3) {
21332                     fg.removeClass([_this.invalidClass, _this.validClass]);
21333                     fg.addClass(_this.validClass);
21334                 } else {
21335                     fg.removeClass(['is-valid', 'is-invalid']);
21336                     fg.addClass('is-valid');
21337                 }
21338             });
21339             
21340             return;
21341         }
21342
21343         if(!this.groupId){
21344             var fg = this.el.findParent('.form-group', false, true);
21345             if (Roo.bootstrap.version == 3) {
21346                 fg.removeClass([this.invalidClass, this.validClass]);
21347                 fg.addClass(this.validClass);
21348             } else {
21349                 fg.removeClass(['is-valid', 'is-invalid']);
21350                 fg.addClass('is-valid');
21351             }
21352             return;
21353         }
21354         
21355         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21356         
21357         if(!group){
21358             return;
21359         }
21360         
21361         for(var i in group){
21362             var fg = group[i].el.findParent('.form-group', false, true);
21363             if (Roo.bootstrap.version == 3) {
21364                 fg.removeClass([this.invalidClass, this.validClass]);
21365                 fg.addClass(this.validClass);
21366             } else {
21367                 fg.removeClass(['is-valid', 'is-invalid']);
21368                 fg.addClass('is-valid');
21369             }
21370         }
21371     },
21372     
21373      /**
21374      * Mark this field as invalid
21375      * @param {String} msg The validation message
21376      */
21377     markInvalid : function(msg)
21378     {
21379         if(this.allowBlank){
21380             return;
21381         }
21382         
21383         var _this = this;
21384         
21385         this.fireEvent('invalid', this, msg);
21386         
21387         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21388         
21389         if(this.groupId){
21390             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21391         }
21392         
21393         if(label){
21394             label.markInvalid();
21395         }
21396             
21397         if(this.inputType == 'radio'){
21398             
21399             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21400                 var fg = e.findParent('.form-group', false, true);
21401                 if (Roo.bootstrap.version == 3) {
21402                     fg.removeClass([_this.invalidClass, _this.validClass]);
21403                     fg.addClass(_this.invalidClass);
21404                 } else {
21405                     fg.removeClass(['is-invalid', 'is-valid']);
21406                     fg.addClass('is-invalid');
21407                 }
21408             });
21409             
21410             return;
21411         }
21412         
21413         if(!this.groupId){
21414             var fg = this.el.findParent('.form-group', false, true);
21415             if (Roo.bootstrap.version == 3) {
21416                 fg.removeClass([_this.invalidClass, _this.validClass]);
21417                 fg.addClass(_this.invalidClass);
21418             } else {
21419                 fg.removeClass(['is-invalid', 'is-valid']);
21420                 fg.addClass('is-invalid');
21421             }
21422             return;
21423         }
21424         
21425         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21426         
21427         if(!group){
21428             return;
21429         }
21430         
21431         for(var i in group){
21432             var fg = group[i].el.findParent('.form-group', false, true);
21433             if (Roo.bootstrap.version == 3) {
21434                 fg.removeClass([_this.invalidClass, _this.validClass]);
21435                 fg.addClass(_this.invalidClass);
21436             } else {
21437                 fg.removeClass(['is-invalid', 'is-valid']);
21438                 fg.addClass('is-invalid');
21439             }
21440         }
21441         
21442     },
21443     
21444     clearInvalid : function()
21445     {
21446         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21447         
21448         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21449         
21450         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21451         
21452         if (label && label.iconEl) {
21453             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21454             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21455         }
21456     },
21457     
21458     disable : function()
21459     {
21460         if(this.inputType != 'radio'){
21461             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21462             return;
21463         }
21464         
21465         var _this = this;
21466         
21467         if(this.rendered){
21468             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21469                 _this.getActionEl().addClass(this.disabledClass);
21470                 e.dom.disabled = true;
21471             });
21472         }
21473         
21474         this.disabled = true;
21475         this.fireEvent("disable", this);
21476         return this;
21477     },
21478
21479     enable : function()
21480     {
21481         if(this.inputType != 'radio'){
21482             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21483             return;
21484         }
21485         
21486         var _this = this;
21487         
21488         if(this.rendered){
21489             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21490                 _this.getActionEl().removeClass(this.disabledClass);
21491                 e.dom.disabled = false;
21492             });
21493         }
21494         
21495         this.disabled = false;
21496         this.fireEvent("enable", this);
21497         return this;
21498     },
21499     
21500     setBoxLabel : function(v)
21501     {
21502         this.boxLabel = v;
21503         
21504         if(this.rendered){
21505             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21506         }
21507     }
21508
21509 });
21510
21511 Roo.apply(Roo.bootstrap.CheckBox, {
21512     
21513     groups: {},
21514     
21515      /**
21516     * register a CheckBox Group
21517     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21518     */
21519     register : function(checkbox)
21520     {
21521         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21522             this.groups[checkbox.groupId] = {};
21523         }
21524         
21525         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21526             return;
21527         }
21528         
21529         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21530         
21531     },
21532     /**
21533     * fetch a CheckBox Group based on the group ID
21534     * @param {string} the group ID
21535     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21536     */
21537     get: function(groupId) {
21538         if (typeof(this.groups[groupId]) == 'undefined') {
21539             return false;
21540         }
21541         
21542         return this.groups[groupId] ;
21543     }
21544     
21545     
21546 });
21547 /*
21548  * - LGPL
21549  *
21550  * RadioItem
21551  * 
21552  */
21553
21554 /**
21555  * @class Roo.bootstrap.Radio
21556  * @extends Roo.bootstrap.Component
21557  * Bootstrap Radio class
21558  * @cfg {String} boxLabel - the label associated
21559  * @cfg {String} value - the value of radio
21560  * 
21561  * @constructor
21562  * Create a new Radio
21563  * @param {Object} config The config object
21564  */
21565 Roo.bootstrap.Radio = function(config){
21566     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21567     
21568 };
21569
21570 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21571     
21572     boxLabel : '',
21573     
21574     value : '',
21575     
21576     getAutoCreate : function()
21577     {
21578         var cfg = {
21579             tag : 'div',
21580             cls : 'form-group radio',
21581             cn : [
21582                 {
21583                     tag : 'label',
21584                     cls : 'box-label',
21585                     html : this.boxLabel
21586                 }
21587             ]
21588         };
21589         
21590         return cfg;
21591     },
21592     
21593     initEvents : function() 
21594     {
21595         this.parent().register(this);
21596         
21597         this.el.on('click', this.onClick, this);
21598         
21599     },
21600     
21601     onClick : function(e)
21602     {
21603         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21604             this.setChecked(true);
21605         }
21606     },
21607     
21608     setChecked : function(state, suppressEvent)
21609     {
21610         this.parent().setValue(this.value, suppressEvent);
21611         
21612     },
21613     
21614     setBoxLabel : function(v)
21615     {
21616         this.boxLabel = v;
21617         
21618         if(this.rendered){
21619             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21620         }
21621     }
21622     
21623 });
21624  
21625
21626  /*
21627  * - LGPL
21628  *
21629  * Input
21630  * 
21631  */
21632
21633 /**
21634  * @class Roo.bootstrap.SecurePass
21635  * @extends Roo.bootstrap.Input
21636  * Bootstrap SecurePass class
21637  *
21638  * 
21639  * @constructor
21640  * Create a new SecurePass
21641  * @param {Object} config The config object
21642  */
21643  
21644 Roo.bootstrap.SecurePass = function (config) {
21645     // these go here, so the translation tool can replace them..
21646     this.errors = {
21647         PwdEmpty: "Please type a password, and then retype it to confirm.",
21648         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21649         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21650         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21651         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21652         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21653         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21654         TooWeak: "Your password is Too Weak."
21655     },
21656     this.meterLabel = "Password strength:";
21657     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21658     this.meterClass = [
21659         "roo-password-meter-tooweak", 
21660         "roo-password-meter-weak", 
21661         "roo-password-meter-medium", 
21662         "roo-password-meter-strong", 
21663         "roo-password-meter-grey"
21664     ];
21665     
21666     this.errors = {};
21667     
21668     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21669 }
21670
21671 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21672     /**
21673      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21674      * {
21675      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21676      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21677      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21678      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21679      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21680      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21681      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21682      * })
21683      */
21684     // private
21685     
21686     meterWidth: 300,
21687     errorMsg :'',    
21688     errors: false,
21689     imageRoot: '/',
21690     /**
21691      * @cfg {String/Object} Label for the strength meter (defaults to
21692      * 'Password strength:')
21693      */
21694     // private
21695     meterLabel: '',
21696     /**
21697      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21698      * ['Weak', 'Medium', 'Strong'])
21699      */
21700     // private    
21701     pwdStrengths: false,    
21702     // private
21703     strength: 0,
21704     // private
21705     _lastPwd: null,
21706     // private
21707     kCapitalLetter: 0,
21708     kSmallLetter: 1,
21709     kDigit: 2,
21710     kPunctuation: 3,
21711     
21712     insecure: false,
21713     // private
21714     initEvents: function ()
21715     {
21716         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21717
21718         if (this.el.is('input[type=password]') && Roo.isSafari) {
21719             this.el.on('keydown', this.SafariOnKeyDown, this);
21720         }
21721
21722         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21723     },
21724     // private
21725     onRender: function (ct, position)
21726     {
21727         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21728         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21729         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21730
21731         this.trigger.createChild({
21732                    cn: [
21733                     {
21734                     //id: 'PwdMeter',
21735                     tag: 'div',
21736                     cls: 'roo-password-meter-grey col-xs-12',
21737                     style: {
21738                         //width: 0,
21739                         //width: this.meterWidth + 'px'                                                
21740                         }
21741                     },
21742                     {                            
21743                          cls: 'roo-password-meter-text'                          
21744                     }
21745                 ]            
21746         });
21747
21748          
21749         if (this.hideTrigger) {
21750             this.trigger.setDisplayed(false);
21751         }
21752         this.setSize(this.width || '', this.height || '');
21753     },
21754     // private
21755     onDestroy: function ()
21756     {
21757         if (this.trigger) {
21758             this.trigger.removeAllListeners();
21759             this.trigger.remove();
21760         }
21761         if (this.wrap) {
21762             this.wrap.remove();
21763         }
21764         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21765     },
21766     // private
21767     checkStrength: function ()
21768     {
21769         var pwd = this.inputEl().getValue();
21770         if (pwd == this._lastPwd) {
21771             return;
21772         }
21773
21774         var strength;
21775         if (this.ClientSideStrongPassword(pwd)) {
21776             strength = 3;
21777         } else if (this.ClientSideMediumPassword(pwd)) {
21778             strength = 2;
21779         } else if (this.ClientSideWeakPassword(pwd)) {
21780             strength = 1;
21781         } else {
21782             strength = 0;
21783         }
21784         
21785         Roo.log('strength1: ' + strength);
21786         
21787         //var pm = this.trigger.child('div/div/div').dom;
21788         var pm = this.trigger.child('div/div');
21789         pm.removeClass(this.meterClass);
21790         pm.addClass(this.meterClass[strength]);
21791                 
21792         
21793         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21794                 
21795         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21796         
21797         this._lastPwd = pwd;
21798     },
21799     reset: function ()
21800     {
21801         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21802         
21803         this._lastPwd = '';
21804         
21805         var pm = this.trigger.child('div/div');
21806         pm.removeClass(this.meterClass);
21807         pm.addClass('roo-password-meter-grey');        
21808         
21809         
21810         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21811         
21812         pt.innerHTML = '';
21813         this.inputEl().dom.type='password';
21814     },
21815     // private
21816     validateValue: function (value)
21817     {
21818         
21819         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21820             return false;
21821         }
21822         if (value.length == 0) {
21823             if (this.allowBlank) {
21824                 this.clearInvalid();
21825                 return true;
21826             }
21827
21828             this.markInvalid(this.errors.PwdEmpty);
21829             this.errorMsg = this.errors.PwdEmpty;
21830             return false;
21831         }
21832         
21833         if(this.insecure){
21834             return true;
21835         }
21836         
21837         if ('[\x21-\x7e]*'.match(value)) {
21838             this.markInvalid(this.errors.PwdBadChar);
21839             this.errorMsg = this.errors.PwdBadChar;
21840             return false;
21841         }
21842         if (value.length < 6) {
21843             this.markInvalid(this.errors.PwdShort);
21844             this.errorMsg = this.errors.PwdShort;
21845             return false;
21846         }
21847         if (value.length > 16) {
21848             this.markInvalid(this.errors.PwdLong);
21849             this.errorMsg = this.errors.PwdLong;
21850             return false;
21851         }
21852         var strength;
21853         if (this.ClientSideStrongPassword(value)) {
21854             strength = 3;
21855         } else if (this.ClientSideMediumPassword(value)) {
21856             strength = 2;
21857         } else if (this.ClientSideWeakPassword(value)) {
21858             strength = 1;
21859         } else {
21860             strength = 0;
21861         }
21862
21863         
21864         if (strength < 2) {
21865             //this.markInvalid(this.errors.TooWeak);
21866             this.errorMsg = this.errors.TooWeak;
21867             //return false;
21868         }
21869         
21870         
21871         console.log('strength2: ' + strength);
21872         
21873         //var pm = this.trigger.child('div/div/div').dom;
21874         
21875         var pm = this.trigger.child('div/div');
21876         pm.removeClass(this.meterClass);
21877         pm.addClass(this.meterClass[strength]);
21878                 
21879         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21880                 
21881         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21882         
21883         this.errorMsg = ''; 
21884         return true;
21885     },
21886     // private
21887     CharacterSetChecks: function (type)
21888     {
21889         this.type = type;
21890         this.fResult = false;
21891     },
21892     // private
21893     isctype: function (character, type)
21894     {
21895         switch (type) {  
21896             case this.kCapitalLetter:
21897                 if (character >= 'A' && character <= 'Z') {
21898                     return true;
21899                 }
21900                 break;
21901             
21902             case this.kSmallLetter:
21903                 if (character >= 'a' && character <= 'z') {
21904                     return true;
21905                 }
21906                 break;
21907             
21908             case this.kDigit:
21909                 if (character >= '0' && character <= '9') {
21910                     return true;
21911                 }
21912                 break;
21913             
21914             case this.kPunctuation:
21915                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21916                     return true;
21917                 }
21918                 break;
21919             
21920             default:
21921                 return false;
21922         }
21923
21924     },
21925     // private
21926     IsLongEnough: function (pwd, size)
21927     {
21928         return !(pwd == null || isNaN(size) || pwd.length < size);
21929     },
21930     // private
21931     SpansEnoughCharacterSets: function (word, nb)
21932     {
21933         if (!this.IsLongEnough(word, nb))
21934         {
21935             return false;
21936         }
21937
21938         var characterSetChecks = new Array(
21939             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21940             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21941         );
21942         
21943         for (var index = 0; index < word.length; ++index) {
21944             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21945                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21946                     characterSetChecks[nCharSet].fResult = true;
21947                     break;
21948                 }
21949             }
21950         }
21951
21952         var nCharSets = 0;
21953         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21954             if (characterSetChecks[nCharSet].fResult) {
21955                 ++nCharSets;
21956             }
21957         }
21958
21959         if (nCharSets < nb) {
21960             return false;
21961         }
21962         return true;
21963     },
21964     // private
21965     ClientSideStrongPassword: function (pwd)
21966     {
21967         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21968     },
21969     // private
21970     ClientSideMediumPassword: function (pwd)
21971     {
21972         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21973     },
21974     // private
21975     ClientSideWeakPassword: function (pwd)
21976     {
21977         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21978     }
21979           
21980 })//<script type="text/javascript">
21981
21982 /*
21983  * Based  Ext JS Library 1.1.1
21984  * Copyright(c) 2006-2007, Ext JS, LLC.
21985  * LGPL
21986  *
21987  */
21988  
21989 /**
21990  * @class Roo.HtmlEditorCore
21991  * @extends Roo.Component
21992  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21993  *
21994  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21995  */
21996
21997 Roo.HtmlEditorCore = function(config){
21998     
21999     
22000     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22001     
22002     
22003     this.addEvents({
22004         /**
22005          * @event initialize
22006          * Fires when the editor is fully initialized (including the iframe)
22007          * @param {Roo.HtmlEditorCore} this
22008          */
22009         initialize: true,
22010         /**
22011          * @event activate
22012          * Fires when the editor is first receives the focus. Any insertion must wait
22013          * until after this event.
22014          * @param {Roo.HtmlEditorCore} this
22015          */
22016         activate: true,
22017          /**
22018          * @event beforesync
22019          * Fires before the textarea is updated with content from the editor iframe. Return false
22020          * to cancel the sync.
22021          * @param {Roo.HtmlEditorCore} this
22022          * @param {String} html
22023          */
22024         beforesync: true,
22025          /**
22026          * @event beforepush
22027          * Fires before the iframe editor is updated with content from the textarea. Return false
22028          * to cancel the push.
22029          * @param {Roo.HtmlEditorCore} this
22030          * @param {String} html
22031          */
22032         beforepush: true,
22033          /**
22034          * @event sync
22035          * Fires when the textarea is updated with content from the editor iframe.
22036          * @param {Roo.HtmlEditorCore} this
22037          * @param {String} html
22038          */
22039         sync: true,
22040          /**
22041          * @event push
22042          * Fires when the iframe editor is updated with content from the textarea.
22043          * @param {Roo.HtmlEditorCore} this
22044          * @param {String} html
22045          */
22046         push: true,
22047         
22048         /**
22049          * @event editorevent
22050          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22051          * @param {Roo.HtmlEditorCore} this
22052          */
22053         editorevent: true
22054         
22055     });
22056     
22057     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22058     
22059     // defaults : white / black...
22060     this.applyBlacklists();
22061     
22062     
22063     
22064 };
22065
22066
22067 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22068
22069
22070      /**
22071      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22072      */
22073     
22074     owner : false,
22075     
22076      /**
22077      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22078      *                        Roo.resizable.
22079      */
22080     resizable : false,
22081      /**
22082      * @cfg {Number} height (in pixels)
22083      */   
22084     height: 300,
22085    /**
22086      * @cfg {Number} width (in pixels)
22087      */   
22088     width: 500,
22089     
22090     /**
22091      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22092      * 
22093      */
22094     stylesheets: false,
22095     
22096     // id of frame..
22097     frameId: false,
22098     
22099     // private properties
22100     validationEvent : false,
22101     deferHeight: true,
22102     initialized : false,
22103     activated : false,
22104     sourceEditMode : false,
22105     onFocus : Roo.emptyFn,
22106     iframePad:3,
22107     hideMode:'offsets',
22108     
22109     clearUp: true,
22110     
22111     // blacklist + whitelisted elements..
22112     black: false,
22113     white: false,
22114      
22115     bodyCls : '',
22116
22117     /**
22118      * Protected method that will not generally be called directly. It
22119      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22120      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22121      */
22122     getDocMarkup : function(){
22123         // body styles..
22124         var st = '';
22125         
22126         // inherit styels from page...?? 
22127         if (this.stylesheets === false) {
22128             
22129             Roo.get(document.head).select('style').each(function(node) {
22130                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22131             });
22132             
22133             Roo.get(document.head).select('link').each(function(node) { 
22134                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22135             });
22136             
22137         } else if (!this.stylesheets.length) {
22138                 // simple..
22139                 st = '<style type="text/css">' +
22140                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22141                    '</style>';
22142         } else { 
22143             st = '<style type="text/css">' +
22144                     this.stylesheets +
22145                 '</style>';
22146         }
22147         
22148         st +=  '<style type="text/css">' +
22149             'IMG { cursor: pointer } ' +
22150         '</style>';
22151
22152         var cls = 'roo-htmleditor-body';
22153         
22154         if(this.bodyCls.length){
22155             cls += ' ' + this.bodyCls;
22156         }
22157         
22158         return '<html><head>' + st  +
22159             //<style type="text/css">' +
22160             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22161             //'</style>' +
22162             ' </head><body class="' +  cls + '"></body></html>';
22163     },
22164
22165     // private
22166     onRender : function(ct, position)
22167     {
22168         var _t = this;
22169         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22170         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22171         
22172         
22173         this.el.dom.style.border = '0 none';
22174         this.el.dom.setAttribute('tabIndex', -1);
22175         this.el.addClass('x-hidden hide');
22176         
22177         
22178         
22179         if(Roo.isIE){ // fix IE 1px bogus margin
22180             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22181         }
22182        
22183         
22184         this.frameId = Roo.id();
22185         
22186          
22187         
22188         var iframe = this.owner.wrap.createChild({
22189             tag: 'iframe',
22190             cls: 'form-control', // bootstrap..
22191             id: this.frameId,
22192             name: this.frameId,
22193             frameBorder : 'no',
22194             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22195         }, this.el
22196         );
22197         
22198         
22199         this.iframe = iframe.dom;
22200
22201          this.assignDocWin();
22202         
22203         this.doc.designMode = 'on';
22204        
22205         this.doc.open();
22206         this.doc.write(this.getDocMarkup());
22207         this.doc.close();
22208
22209         
22210         var task = { // must defer to wait for browser to be ready
22211             run : function(){
22212                 //console.log("run task?" + this.doc.readyState);
22213                 this.assignDocWin();
22214                 if(this.doc.body || this.doc.readyState == 'complete'){
22215                     try {
22216                         this.doc.designMode="on";
22217                     } catch (e) {
22218                         return;
22219                     }
22220                     Roo.TaskMgr.stop(task);
22221                     this.initEditor.defer(10, this);
22222                 }
22223             },
22224             interval : 10,
22225             duration: 10000,
22226             scope: this
22227         };
22228         Roo.TaskMgr.start(task);
22229
22230     },
22231
22232     // private
22233     onResize : function(w, h)
22234     {
22235          Roo.log('resize: ' +w + ',' + h );
22236         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22237         if(!this.iframe){
22238             return;
22239         }
22240         if(typeof w == 'number'){
22241             
22242             this.iframe.style.width = w + 'px';
22243         }
22244         if(typeof h == 'number'){
22245             
22246             this.iframe.style.height = h + 'px';
22247             if(this.doc){
22248                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22249             }
22250         }
22251         
22252     },
22253
22254     /**
22255      * Toggles the editor between standard and source edit mode.
22256      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22257      */
22258     toggleSourceEdit : function(sourceEditMode){
22259         
22260         this.sourceEditMode = sourceEditMode === true;
22261         
22262         if(this.sourceEditMode){
22263  
22264             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22265             
22266         }else{
22267             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22268             //this.iframe.className = '';
22269             this.deferFocus();
22270         }
22271         //this.setSize(this.owner.wrap.getSize());
22272         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22273     },
22274
22275     
22276   
22277
22278     /**
22279      * Protected method that will not generally be called directly. If you need/want
22280      * custom HTML cleanup, this is the method you should override.
22281      * @param {String} html The HTML to be cleaned
22282      * return {String} The cleaned HTML
22283      */
22284     cleanHtml : function(html){
22285         html = String(html);
22286         if(html.length > 5){
22287             if(Roo.isSafari){ // strip safari nonsense
22288                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22289             }
22290         }
22291         if(html == '&nbsp;'){
22292             html = '';
22293         }
22294         return html;
22295     },
22296
22297     /**
22298      * HTML Editor -> Textarea
22299      * Protected method that will not generally be called directly. Syncs the contents
22300      * of the editor iframe with the textarea.
22301      */
22302     syncValue : function(){
22303         if(this.initialized){
22304             var bd = (this.doc.body || this.doc.documentElement);
22305             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22306             var html = bd.innerHTML;
22307             if(Roo.isSafari){
22308                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22309                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22310                 if(m && m[1]){
22311                     html = '<div style="'+m[0]+'">' + html + '</div>';
22312                 }
22313             }
22314             html = this.cleanHtml(html);
22315             // fix up the special chars.. normaly like back quotes in word...
22316             // however we do not want to do this with chinese..
22317             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22318                 var cc = b.charCodeAt();
22319                 if (
22320                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22321                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22322                     (cc >= 0xf900 && cc < 0xfb00 )
22323                 ) {
22324                         return b;
22325                 }
22326                 return "&#"+cc+";" 
22327             });
22328             if(this.owner.fireEvent('beforesync', this, html) !== false){
22329                 this.el.dom.value = html;
22330                 this.owner.fireEvent('sync', this, html);
22331             }
22332         }
22333     },
22334
22335     /**
22336      * Protected method that will not generally be called directly. Pushes the value of the textarea
22337      * into the iframe editor.
22338      */
22339     pushValue : function(){
22340         if(this.initialized){
22341             var v = this.el.dom.value.trim();
22342             
22343 //            if(v.length < 1){
22344 //                v = '&#160;';
22345 //            }
22346             
22347             if(this.owner.fireEvent('beforepush', this, v) !== false){
22348                 var d = (this.doc.body || this.doc.documentElement);
22349                 d.innerHTML = v;
22350                 this.cleanUpPaste();
22351                 this.el.dom.value = d.innerHTML;
22352                 this.owner.fireEvent('push', this, v);
22353             }
22354         }
22355     },
22356
22357     // private
22358     deferFocus : function(){
22359         this.focus.defer(10, this);
22360     },
22361
22362     // doc'ed in Field
22363     focus : function(){
22364         if(this.win && !this.sourceEditMode){
22365             this.win.focus();
22366         }else{
22367             this.el.focus();
22368         }
22369     },
22370     
22371     assignDocWin: function()
22372     {
22373         var iframe = this.iframe;
22374         
22375          if(Roo.isIE){
22376             this.doc = iframe.contentWindow.document;
22377             this.win = iframe.contentWindow;
22378         } else {
22379 //            if (!Roo.get(this.frameId)) {
22380 //                return;
22381 //            }
22382 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22383 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22384             
22385             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22386                 return;
22387             }
22388             
22389             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22390             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22391         }
22392     },
22393     
22394     // private
22395     initEditor : function(){
22396         //console.log("INIT EDITOR");
22397         this.assignDocWin();
22398         
22399         
22400         
22401         this.doc.designMode="on";
22402         this.doc.open();
22403         this.doc.write(this.getDocMarkup());
22404         this.doc.close();
22405         
22406         var dbody = (this.doc.body || this.doc.documentElement);
22407         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22408         // this copies styles from the containing element into thsi one..
22409         // not sure why we need all of this..
22410         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22411         
22412         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22413         //ss['background-attachment'] = 'fixed'; // w3c
22414         dbody.bgProperties = 'fixed'; // ie
22415         //Roo.DomHelper.applyStyles(dbody, ss);
22416         Roo.EventManager.on(this.doc, {
22417             //'mousedown': this.onEditorEvent,
22418             'mouseup': this.onEditorEvent,
22419             'dblclick': this.onEditorEvent,
22420             'click': this.onEditorEvent,
22421             'keyup': this.onEditorEvent,
22422             buffer:100,
22423             scope: this
22424         });
22425         if(Roo.isGecko){
22426             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22427         }
22428         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22429             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22430         }
22431         this.initialized = true;
22432
22433         this.owner.fireEvent('initialize', this);
22434         this.pushValue();
22435     },
22436
22437     // private
22438     onDestroy : function(){
22439         
22440         
22441         
22442         if(this.rendered){
22443             
22444             //for (var i =0; i < this.toolbars.length;i++) {
22445             //    // fixme - ask toolbars for heights?
22446             //    this.toolbars[i].onDestroy();
22447            // }
22448             
22449             //this.wrap.dom.innerHTML = '';
22450             //this.wrap.remove();
22451         }
22452     },
22453
22454     // private
22455     onFirstFocus : function(){
22456         
22457         this.assignDocWin();
22458         
22459         
22460         this.activated = true;
22461          
22462     
22463         if(Roo.isGecko){ // prevent silly gecko errors
22464             this.win.focus();
22465             var s = this.win.getSelection();
22466             if(!s.focusNode || s.focusNode.nodeType != 3){
22467                 var r = s.getRangeAt(0);
22468                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22469                 r.collapse(true);
22470                 this.deferFocus();
22471             }
22472             try{
22473                 this.execCmd('useCSS', true);
22474                 this.execCmd('styleWithCSS', false);
22475             }catch(e){}
22476         }
22477         this.owner.fireEvent('activate', this);
22478     },
22479
22480     // private
22481     adjustFont: function(btn){
22482         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22483         //if(Roo.isSafari){ // safari
22484         //    adjust *= 2;
22485        // }
22486         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22487         if(Roo.isSafari){ // safari
22488             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22489             v =  (v < 10) ? 10 : v;
22490             v =  (v > 48) ? 48 : v;
22491             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22492             
22493         }
22494         
22495         
22496         v = Math.max(1, v+adjust);
22497         
22498         this.execCmd('FontSize', v  );
22499     },
22500
22501     onEditorEvent : function(e)
22502     {
22503         this.owner.fireEvent('editorevent', this, e);
22504       //  this.updateToolbar();
22505         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22506     },
22507
22508     insertTag : function(tg)
22509     {
22510         // could be a bit smarter... -> wrap the current selected tRoo..
22511         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22512             
22513             range = this.createRange(this.getSelection());
22514             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22515             wrappingNode.appendChild(range.extractContents());
22516             range.insertNode(wrappingNode);
22517
22518             return;
22519             
22520             
22521             
22522         }
22523         this.execCmd("formatblock",   tg);
22524         
22525     },
22526     
22527     insertText : function(txt)
22528     {
22529         
22530         
22531         var range = this.createRange();
22532         range.deleteContents();
22533                //alert(Sender.getAttribute('label'));
22534                
22535         range.insertNode(this.doc.createTextNode(txt));
22536     } ,
22537     
22538      
22539
22540     /**
22541      * Executes a Midas editor command on the editor document and performs necessary focus and
22542      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22543      * @param {String} cmd The Midas command
22544      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22545      */
22546     relayCmd : function(cmd, value){
22547         this.win.focus();
22548         this.execCmd(cmd, value);
22549         this.owner.fireEvent('editorevent', this);
22550         //this.updateToolbar();
22551         this.owner.deferFocus();
22552     },
22553
22554     /**
22555      * Executes a Midas editor command directly on the editor document.
22556      * For visual commands, you should use {@link #relayCmd} instead.
22557      * <b>This should only be called after the editor is initialized.</b>
22558      * @param {String} cmd The Midas command
22559      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22560      */
22561     execCmd : function(cmd, value){
22562         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22563         this.syncValue();
22564     },
22565  
22566  
22567    
22568     /**
22569      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22570      * to insert tRoo.
22571      * @param {String} text | dom node.. 
22572      */
22573     insertAtCursor : function(text)
22574     {
22575         
22576         if(!this.activated){
22577             return;
22578         }
22579         /*
22580         if(Roo.isIE){
22581             this.win.focus();
22582             var r = this.doc.selection.createRange();
22583             if(r){
22584                 r.collapse(true);
22585                 r.pasteHTML(text);
22586                 this.syncValue();
22587                 this.deferFocus();
22588             
22589             }
22590             return;
22591         }
22592         */
22593         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22594             this.win.focus();
22595             
22596             
22597             // from jquery ui (MIT licenced)
22598             var range, node;
22599             var win = this.win;
22600             
22601             if (win.getSelection && win.getSelection().getRangeAt) {
22602                 range = win.getSelection().getRangeAt(0);
22603                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22604                 range.insertNode(node);
22605             } else if (win.document.selection && win.document.selection.createRange) {
22606                 // no firefox support
22607                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22608                 win.document.selection.createRange().pasteHTML(txt);
22609             } else {
22610                 // no firefox support
22611                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22612                 this.execCmd('InsertHTML', txt);
22613             } 
22614             
22615             this.syncValue();
22616             
22617             this.deferFocus();
22618         }
22619     },
22620  // private
22621     mozKeyPress : function(e){
22622         if(e.ctrlKey){
22623             var c = e.getCharCode(), cmd;
22624           
22625             if(c > 0){
22626                 c = String.fromCharCode(c).toLowerCase();
22627                 switch(c){
22628                     case 'b':
22629                         cmd = 'bold';
22630                         break;
22631                     case 'i':
22632                         cmd = 'italic';
22633                         break;
22634                     
22635                     case 'u':
22636                         cmd = 'underline';
22637                         break;
22638                     
22639                     case 'v':
22640                         this.cleanUpPaste.defer(100, this);
22641                         return;
22642                         
22643                 }
22644                 if(cmd){
22645                     this.win.focus();
22646                     this.execCmd(cmd);
22647                     this.deferFocus();
22648                     e.preventDefault();
22649                 }
22650                 
22651             }
22652         }
22653     },
22654
22655     // private
22656     fixKeys : function(){ // load time branching for fastest keydown performance
22657         if(Roo.isIE){
22658             return function(e){
22659                 var k = e.getKey(), r;
22660                 if(k == e.TAB){
22661                     e.stopEvent();
22662                     r = this.doc.selection.createRange();
22663                     if(r){
22664                         r.collapse(true);
22665                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22666                         this.deferFocus();
22667                     }
22668                     return;
22669                 }
22670                 
22671                 if(k == e.ENTER){
22672                     r = this.doc.selection.createRange();
22673                     if(r){
22674                         var target = r.parentElement();
22675                         if(!target || target.tagName.toLowerCase() != 'li'){
22676                             e.stopEvent();
22677                             r.pasteHTML('<br />');
22678                             r.collapse(false);
22679                             r.select();
22680                         }
22681                     }
22682                 }
22683                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22684                     this.cleanUpPaste.defer(100, this);
22685                     return;
22686                 }
22687                 
22688                 
22689             };
22690         }else if(Roo.isOpera){
22691             return function(e){
22692                 var k = e.getKey();
22693                 if(k == e.TAB){
22694                     e.stopEvent();
22695                     this.win.focus();
22696                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22697                     this.deferFocus();
22698                 }
22699                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22700                     this.cleanUpPaste.defer(100, this);
22701                     return;
22702                 }
22703                 
22704             };
22705         }else if(Roo.isSafari){
22706             return function(e){
22707                 var k = e.getKey();
22708                 
22709                 if(k == e.TAB){
22710                     e.stopEvent();
22711                     this.execCmd('InsertText','\t');
22712                     this.deferFocus();
22713                     return;
22714                 }
22715                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22716                     this.cleanUpPaste.defer(100, this);
22717                     return;
22718                 }
22719                 
22720              };
22721         }
22722     }(),
22723     
22724     getAllAncestors: function()
22725     {
22726         var p = this.getSelectedNode();
22727         var a = [];
22728         if (!p) {
22729             a.push(p); // push blank onto stack..
22730             p = this.getParentElement();
22731         }
22732         
22733         
22734         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22735             a.push(p);
22736             p = p.parentNode;
22737         }
22738         a.push(this.doc.body);
22739         return a;
22740     },
22741     lastSel : false,
22742     lastSelNode : false,
22743     
22744     
22745     getSelection : function() 
22746     {
22747         this.assignDocWin();
22748         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22749     },
22750     
22751     getSelectedNode: function() 
22752     {
22753         // this may only work on Gecko!!!
22754         
22755         // should we cache this!!!!
22756         
22757         
22758         
22759          
22760         var range = this.createRange(this.getSelection()).cloneRange();
22761         
22762         if (Roo.isIE) {
22763             var parent = range.parentElement();
22764             while (true) {
22765                 var testRange = range.duplicate();
22766                 testRange.moveToElementText(parent);
22767                 if (testRange.inRange(range)) {
22768                     break;
22769                 }
22770                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22771                     break;
22772                 }
22773                 parent = parent.parentElement;
22774             }
22775             return parent;
22776         }
22777         
22778         // is ancestor a text element.
22779         var ac =  range.commonAncestorContainer;
22780         if (ac.nodeType == 3) {
22781             ac = ac.parentNode;
22782         }
22783         
22784         var ar = ac.childNodes;
22785          
22786         var nodes = [];
22787         var other_nodes = [];
22788         var has_other_nodes = false;
22789         for (var i=0;i<ar.length;i++) {
22790             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22791                 continue;
22792             }
22793             // fullly contained node.
22794             
22795             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22796                 nodes.push(ar[i]);
22797                 continue;
22798             }
22799             
22800             // probably selected..
22801             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22802                 other_nodes.push(ar[i]);
22803                 continue;
22804             }
22805             // outer..
22806             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22807                 continue;
22808             }
22809             
22810             
22811             has_other_nodes = true;
22812         }
22813         if (!nodes.length && other_nodes.length) {
22814             nodes= other_nodes;
22815         }
22816         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22817             return false;
22818         }
22819         
22820         return nodes[0];
22821     },
22822     createRange: function(sel)
22823     {
22824         // this has strange effects when using with 
22825         // top toolbar - not sure if it's a great idea.
22826         //this.editor.contentWindow.focus();
22827         if (typeof sel != "undefined") {
22828             try {
22829                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22830             } catch(e) {
22831                 return this.doc.createRange();
22832             }
22833         } else {
22834             return this.doc.createRange();
22835         }
22836     },
22837     getParentElement: function()
22838     {
22839         
22840         this.assignDocWin();
22841         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22842         
22843         var range = this.createRange(sel);
22844          
22845         try {
22846             var p = range.commonAncestorContainer;
22847             while (p.nodeType == 3) { // text node
22848                 p = p.parentNode;
22849             }
22850             return p;
22851         } catch (e) {
22852             return null;
22853         }
22854     
22855     },
22856     /***
22857      *
22858      * Range intersection.. the hard stuff...
22859      *  '-1' = before
22860      *  '0' = hits..
22861      *  '1' = after.
22862      *         [ -- selected range --- ]
22863      *   [fail]                        [fail]
22864      *
22865      *    basically..
22866      *      if end is before start or  hits it. fail.
22867      *      if start is after end or hits it fail.
22868      *
22869      *   if either hits (but other is outside. - then it's not 
22870      *   
22871      *    
22872      **/
22873     
22874     
22875     // @see http://www.thismuchiknow.co.uk/?p=64.
22876     rangeIntersectsNode : function(range, node)
22877     {
22878         var nodeRange = node.ownerDocument.createRange();
22879         try {
22880             nodeRange.selectNode(node);
22881         } catch (e) {
22882             nodeRange.selectNodeContents(node);
22883         }
22884     
22885         var rangeStartRange = range.cloneRange();
22886         rangeStartRange.collapse(true);
22887     
22888         var rangeEndRange = range.cloneRange();
22889         rangeEndRange.collapse(false);
22890     
22891         var nodeStartRange = nodeRange.cloneRange();
22892         nodeStartRange.collapse(true);
22893     
22894         var nodeEndRange = nodeRange.cloneRange();
22895         nodeEndRange.collapse(false);
22896     
22897         return rangeStartRange.compareBoundaryPoints(
22898                  Range.START_TO_START, nodeEndRange) == -1 &&
22899                rangeEndRange.compareBoundaryPoints(
22900                  Range.START_TO_START, nodeStartRange) == 1;
22901         
22902          
22903     },
22904     rangeCompareNode : function(range, node)
22905     {
22906         var nodeRange = node.ownerDocument.createRange();
22907         try {
22908             nodeRange.selectNode(node);
22909         } catch (e) {
22910             nodeRange.selectNodeContents(node);
22911         }
22912         
22913         
22914         range.collapse(true);
22915     
22916         nodeRange.collapse(true);
22917      
22918         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22919         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22920          
22921         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22922         
22923         var nodeIsBefore   =  ss == 1;
22924         var nodeIsAfter    = ee == -1;
22925         
22926         if (nodeIsBefore && nodeIsAfter) {
22927             return 0; // outer
22928         }
22929         if (!nodeIsBefore && nodeIsAfter) {
22930             return 1; //right trailed.
22931         }
22932         
22933         if (nodeIsBefore && !nodeIsAfter) {
22934             return 2;  // left trailed.
22935         }
22936         // fully contined.
22937         return 3;
22938     },
22939
22940     // private? - in a new class?
22941     cleanUpPaste :  function()
22942     {
22943         // cleans up the whole document..
22944         Roo.log('cleanuppaste');
22945         
22946         this.cleanUpChildren(this.doc.body);
22947         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22948         if (clean != this.doc.body.innerHTML) {
22949             this.doc.body.innerHTML = clean;
22950         }
22951         
22952     },
22953     
22954     cleanWordChars : function(input) {// change the chars to hex code
22955         var he = Roo.HtmlEditorCore;
22956         
22957         var output = input;
22958         Roo.each(he.swapCodes, function(sw) { 
22959             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22960             
22961             output = output.replace(swapper, sw[1]);
22962         });
22963         
22964         return output;
22965     },
22966     
22967     
22968     cleanUpChildren : function (n)
22969     {
22970         if (!n.childNodes.length) {
22971             return;
22972         }
22973         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22974            this.cleanUpChild(n.childNodes[i]);
22975         }
22976     },
22977     
22978     
22979         
22980     
22981     cleanUpChild : function (node)
22982     {
22983         var ed = this;
22984         //console.log(node);
22985         if (node.nodeName == "#text") {
22986             // clean up silly Windows -- stuff?
22987             return; 
22988         }
22989         if (node.nodeName == "#comment") {
22990             node.parentNode.removeChild(node);
22991             // clean up silly Windows -- stuff?
22992             return; 
22993         }
22994         var lcname = node.tagName.toLowerCase();
22995         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22996         // whitelist of tags..
22997         
22998         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22999             // remove node.
23000             node.parentNode.removeChild(node);
23001             return;
23002             
23003         }
23004         
23005         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23006         
23007         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23008         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23009         
23010         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23011         //    remove_keep_children = true;
23012         //}
23013         
23014         if (remove_keep_children) {
23015             this.cleanUpChildren(node);
23016             // inserts everything just before this node...
23017             while (node.childNodes.length) {
23018                 var cn = node.childNodes[0];
23019                 node.removeChild(cn);
23020                 node.parentNode.insertBefore(cn, node);
23021             }
23022             node.parentNode.removeChild(node);
23023             return;
23024         }
23025         
23026         if (!node.attributes || !node.attributes.length) {
23027             this.cleanUpChildren(node);
23028             return;
23029         }
23030         
23031         function cleanAttr(n,v)
23032         {
23033             
23034             if (v.match(/^\./) || v.match(/^\//)) {
23035                 return;
23036             }
23037             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23038                 return;
23039             }
23040             if (v.match(/^#/)) {
23041                 return;
23042             }
23043 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23044             node.removeAttribute(n);
23045             
23046         }
23047         
23048         var cwhite = this.cwhite;
23049         var cblack = this.cblack;
23050             
23051         function cleanStyle(n,v)
23052         {
23053             if (v.match(/expression/)) { //XSS?? should we even bother..
23054                 node.removeAttribute(n);
23055                 return;
23056             }
23057             
23058             var parts = v.split(/;/);
23059             var clean = [];
23060             
23061             Roo.each(parts, function(p) {
23062                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23063                 if (!p.length) {
23064                     return true;
23065                 }
23066                 var l = p.split(':').shift().replace(/\s+/g,'');
23067                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23068                 
23069                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23070 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23071                     //node.removeAttribute(n);
23072                     return true;
23073                 }
23074                 //Roo.log()
23075                 // only allow 'c whitelisted system attributes'
23076                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23077 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23078                     //node.removeAttribute(n);
23079                     return true;
23080                 }
23081                 
23082                 
23083                  
23084                 
23085                 clean.push(p);
23086                 return true;
23087             });
23088             if (clean.length) { 
23089                 node.setAttribute(n, clean.join(';'));
23090             } else {
23091                 node.removeAttribute(n);
23092             }
23093             
23094         }
23095         
23096         
23097         for (var i = node.attributes.length-1; i > -1 ; i--) {
23098             var a = node.attributes[i];
23099             //console.log(a);
23100             
23101             if (a.name.toLowerCase().substr(0,2)=='on')  {
23102                 node.removeAttribute(a.name);
23103                 continue;
23104             }
23105             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23106                 node.removeAttribute(a.name);
23107                 continue;
23108             }
23109             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23110                 cleanAttr(a.name,a.value); // fixme..
23111                 continue;
23112             }
23113             if (a.name == 'style') {
23114                 cleanStyle(a.name,a.value);
23115                 continue;
23116             }
23117             /// clean up MS crap..
23118             // tecnically this should be a list of valid class'es..
23119             
23120             
23121             if (a.name == 'class') {
23122                 if (a.value.match(/^Mso/)) {
23123                     node.className = '';
23124                 }
23125                 
23126                 if (a.value.match(/^body$/)) {
23127                     node.className = '';
23128                 }
23129                 continue;
23130             }
23131             
23132             // style cleanup!?
23133             // class cleanup?
23134             
23135         }
23136         
23137         
23138         this.cleanUpChildren(node);
23139         
23140         
23141     },
23142     
23143     /**
23144      * Clean up MS wordisms...
23145      */
23146     cleanWord : function(node)
23147     {
23148         
23149         
23150         if (!node) {
23151             this.cleanWord(this.doc.body);
23152             return;
23153         }
23154         if (node.nodeName == "#text") {
23155             // clean up silly Windows -- stuff?
23156             return; 
23157         }
23158         if (node.nodeName == "#comment") {
23159             node.parentNode.removeChild(node);
23160             // clean up silly Windows -- stuff?
23161             return; 
23162         }
23163         
23164         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23165             node.parentNode.removeChild(node);
23166             return;
23167         }
23168         
23169         // remove - but keep children..
23170         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23171             while (node.childNodes.length) {
23172                 var cn = node.childNodes[0];
23173                 node.removeChild(cn);
23174                 node.parentNode.insertBefore(cn, node);
23175             }
23176             node.parentNode.removeChild(node);
23177             this.iterateChildren(node, this.cleanWord);
23178             return;
23179         }
23180         // clean styles
23181         if (node.className.length) {
23182             
23183             var cn = node.className.split(/\W+/);
23184             var cna = [];
23185             Roo.each(cn, function(cls) {
23186                 if (cls.match(/Mso[a-zA-Z]+/)) {
23187                     return;
23188                 }
23189                 cna.push(cls);
23190             });
23191             node.className = cna.length ? cna.join(' ') : '';
23192             if (!cna.length) {
23193                 node.removeAttribute("class");
23194             }
23195         }
23196         
23197         if (node.hasAttribute("lang")) {
23198             node.removeAttribute("lang");
23199         }
23200         
23201         if (node.hasAttribute("style")) {
23202             
23203             var styles = node.getAttribute("style").split(";");
23204             var nstyle = [];
23205             Roo.each(styles, function(s) {
23206                 if (!s.match(/:/)) {
23207                     return;
23208                 }
23209                 var kv = s.split(":");
23210                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23211                     return;
23212                 }
23213                 // what ever is left... we allow.
23214                 nstyle.push(s);
23215             });
23216             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23217             if (!nstyle.length) {
23218                 node.removeAttribute('style');
23219             }
23220         }
23221         this.iterateChildren(node, this.cleanWord);
23222         
23223         
23224         
23225     },
23226     /**
23227      * iterateChildren of a Node, calling fn each time, using this as the scole..
23228      * @param {DomNode} node node to iterate children of.
23229      * @param {Function} fn method of this class to call on each item.
23230      */
23231     iterateChildren : function(node, fn)
23232     {
23233         if (!node.childNodes.length) {
23234                 return;
23235         }
23236         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23237            fn.call(this, node.childNodes[i])
23238         }
23239     },
23240     
23241     
23242     /**
23243      * cleanTableWidths.
23244      *
23245      * Quite often pasting from word etc.. results in tables with column and widths.
23246      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23247      *
23248      */
23249     cleanTableWidths : function(node)
23250     {
23251          
23252          
23253         if (!node) {
23254             this.cleanTableWidths(this.doc.body);
23255             return;
23256         }
23257         
23258         // ignore list...
23259         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23260             return; 
23261         }
23262         Roo.log(node.tagName);
23263         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23264             this.iterateChildren(node, this.cleanTableWidths);
23265             return;
23266         }
23267         if (node.hasAttribute('width')) {
23268             node.removeAttribute('width');
23269         }
23270         
23271          
23272         if (node.hasAttribute("style")) {
23273             // pretty basic...
23274             
23275             var styles = node.getAttribute("style").split(";");
23276             var nstyle = [];
23277             Roo.each(styles, function(s) {
23278                 if (!s.match(/:/)) {
23279                     return;
23280                 }
23281                 var kv = s.split(":");
23282                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23283                     return;
23284                 }
23285                 // what ever is left... we allow.
23286                 nstyle.push(s);
23287             });
23288             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23289             if (!nstyle.length) {
23290                 node.removeAttribute('style');
23291             }
23292         }
23293         
23294         this.iterateChildren(node, this.cleanTableWidths);
23295         
23296         
23297     },
23298     
23299     
23300     
23301     
23302     domToHTML : function(currentElement, depth, nopadtext) {
23303         
23304         depth = depth || 0;
23305         nopadtext = nopadtext || false;
23306     
23307         if (!currentElement) {
23308             return this.domToHTML(this.doc.body);
23309         }
23310         
23311         //Roo.log(currentElement);
23312         var j;
23313         var allText = false;
23314         var nodeName = currentElement.nodeName;
23315         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23316         
23317         if  (nodeName == '#text') {
23318             
23319             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23320         }
23321         
23322         
23323         var ret = '';
23324         if (nodeName != 'BODY') {
23325              
23326             var i = 0;
23327             // Prints the node tagName, such as <A>, <IMG>, etc
23328             if (tagName) {
23329                 var attr = [];
23330                 for(i = 0; i < currentElement.attributes.length;i++) {
23331                     // quoting?
23332                     var aname = currentElement.attributes.item(i).name;
23333                     if (!currentElement.attributes.item(i).value.length) {
23334                         continue;
23335                     }
23336                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23337                 }
23338                 
23339                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23340             } 
23341             else {
23342                 
23343                 // eack
23344             }
23345         } else {
23346             tagName = false;
23347         }
23348         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23349             return ret;
23350         }
23351         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23352             nopadtext = true;
23353         }
23354         
23355         
23356         // Traverse the tree
23357         i = 0;
23358         var currentElementChild = currentElement.childNodes.item(i);
23359         var allText = true;
23360         var innerHTML  = '';
23361         lastnode = '';
23362         while (currentElementChild) {
23363             // Formatting code (indent the tree so it looks nice on the screen)
23364             var nopad = nopadtext;
23365             if (lastnode == 'SPAN') {
23366                 nopad  = true;
23367             }
23368             // text
23369             if  (currentElementChild.nodeName == '#text') {
23370                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23371                 toadd = nopadtext ? toadd : toadd.trim();
23372                 if (!nopad && toadd.length > 80) {
23373                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23374                 }
23375                 innerHTML  += toadd;
23376                 
23377                 i++;
23378                 currentElementChild = currentElement.childNodes.item(i);
23379                 lastNode = '';
23380                 continue;
23381             }
23382             allText = false;
23383             
23384             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23385                 
23386             // Recursively traverse the tree structure of the child node
23387             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23388             lastnode = currentElementChild.nodeName;
23389             i++;
23390             currentElementChild=currentElement.childNodes.item(i);
23391         }
23392         
23393         ret += innerHTML;
23394         
23395         if (!allText) {
23396                 // The remaining code is mostly for formatting the tree
23397             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23398         }
23399         
23400         
23401         if (tagName) {
23402             ret+= "</"+tagName+">";
23403         }
23404         return ret;
23405         
23406     },
23407         
23408     applyBlacklists : function()
23409     {
23410         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23411         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23412         
23413         this.white = [];
23414         this.black = [];
23415         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23416             if (b.indexOf(tag) > -1) {
23417                 return;
23418             }
23419             this.white.push(tag);
23420             
23421         }, this);
23422         
23423         Roo.each(w, function(tag) {
23424             if (b.indexOf(tag) > -1) {
23425                 return;
23426             }
23427             if (this.white.indexOf(tag) > -1) {
23428                 return;
23429             }
23430             this.white.push(tag);
23431             
23432         }, this);
23433         
23434         
23435         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23436             if (w.indexOf(tag) > -1) {
23437                 return;
23438             }
23439             this.black.push(tag);
23440             
23441         }, this);
23442         
23443         Roo.each(b, function(tag) {
23444             if (w.indexOf(tag) > -1) {
23445                 return;
23446             }
23447             if (this.black.indexOf(tag) > -1) {
23448                 return;
23449             }
23450             this.black.push(tag);
23451             
23452         }, this);
23453         
23454         
23455         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23456         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23457         
23458         this.cwhite = [];
23459         this.cblack = [];
23460         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23461             if (b.indexOf(tag) > -1) {
23462                 return;
23463             }
23464             this.cwhite.push(tag);
23465             
23466         }, this);
23467         
23468         Roo.each(w, function(tag) {
23469             if (b.indexOf(tag) > -1) {
23470                 return;
23471             }
23472             if (this.cwhite.indexOf(tag) > -1) {
23473                 return;
23474             }
23475             this.cwhite.push(tag);
23476             
23477         }, this);
23478         
23479         
23480         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23481             if (w.indexOf(tag) > -1) {
23482                 return;
23483             }
23484             this.cblack.push(tag);
23485             
23486         }, this);
23487         
23488         Roo.each(b, function(tag) {
23489             if (w.indexOf(tag) > -1) {
23490                 return;
23491             }
23492             if (this.cblack.indexOf(tag) > -1) {
23493                 return;
23494             }
23495             this.cblack.push(tag);
23496             
23497         }, this);
23498     },
23499     
23500     setStylesheets : function(stylesheets)
23501     {
23502         if(typeof(stylesheets) == 'string'){
23503             Roo.get(this.iframe.contentDocument.head).createChild({
23504                 tag : 'link',
23505                 rel : 'stylesheet',
23506                 type : 'text/css',
23507                 href : stylesheets
23508             });
23509             
23510             return;
23511         }
23512         var _this = this;
23513      
23514         Roo.each(stylesheets, function(s) {
23515             if(!s.length){
23516                 return;
23517             }
23518             
23519             Roo.get(_this.iframe.contentDocument.head).createChild({
23520                 tag : 'link',
23521                 rel : 'stylesheet',
23522                 type : 'text/css',
23523                 href : s
23524             });
23525         });
23526
23527         
23528     },
23529     
23530     removeStylesheets : function()
23531     {
23532         var _this = this;
23533         
23534         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23535             s.remove();
23536         });
23537     },
23538     
23539     setStyle : function(style)
23540     {
23541         Roo.get(this.iframe.contentDocument.head).createChild({
23542             tag : 'style',
23543             type : 'text/css',
23544             html : style
23545         });
23546
23547         return;
23548     }
23549     
23550     // hide stuff that is not compatible
23551     /**
23552      * @event blur
23553      * @hide
23554      */
23555     /**
23556      * @event change
23557      * @hide
23558      */
23559     /**
23560      * @event focus
23561      * @hide
23562      */
23563     /**
23564      * @event specialkey
23565      * @hide
23566      */
23567     /**
23568      * @cfg {String} fieldClass @hide
23569      */
23570     /**
23571      * @cfg {String} focusClass @hide
23572      */
23573     /**
23574      * @cfg {String} autoCreate @hide
23575      */
23576     /**
23577      * @cfg {String} inputType @hide
23578      */
23579     /**
23580      * @cfg {String} invalidClass @hide
23581      */
23582     /**
23583      * @cfg {String} invalidText @hide
23584      */
23585     /**
23586      * @cfg {String} msgFx @hide
23587      */
23588     /**
23589      * @cfg {String} validateOnBlur @hide
23590      */
23591 });
23592
23593 Roo.HtmlEditorCore.white = [
23594         'area', 'br', 'img', 'input', 'hr', 'wbr',
23595         
23596        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23597        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23598        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23599        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23600        'table',   'ul',         'xmp', 
23601        
23602        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23603       'thead',   'tr', 
23604      
23605       'dir', 'menu', 'ol', 'ul', 'dl',
23606        
23607       'embed',  'object'
23608 ];
23609
23610
23611 Roo.HtmlEditorCore.black = [
23612     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23613         'applet', // 
23614         'base',   'basefont', 'bgsound', 'blink',  'body', 
23615         'frame',  'frameset', 'head',    'html',   'ilayer', 
23616         'iframe', 'layer',  'link',     'meta',    'object',   
23617         'script', 'style' ,'title',  'xml' // clean later..
23618 ];
23619 Roo.HtmlEditorCore.clean = [
23620     'script', 'style', 'title', 'xml'
23621 ];
23622 Roo.HtmlEditorCore.remove = [
23623     'font'
23624 ];
23625 // attributes..
23626
23627 Roo.HtmlEditorCore.ablack = [
23628     'on'
23629 ];
23630     
23631 Roo.HtmlEditorCore.aclean = [ 
23632     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23633 ];
23634
23635 // protocols..
23636 Roo.HtmlEditorCore.pwhite= [
23637         'http',  'https',  'mailto'
23638 ];
23639
23640 // white listed style attributes.
23641 Roo.HtmlEditorCore.cwhite= [
23642       //  'text-align', /// default is to allow most things..
23643       
23644          
23645 //        'font-size'//??
23646 ];
23647
23648 // black listed style attributes.
23649 Roo.HtmlEditorCore.cblack= [
23650       //  'font-size' -- this can be set by the project 
23651 ];
23652
23653
23654 Roo.HtmlEditorCore.swapCodes   =[ 
23655     [    8211, "--" ], 
23656     [    8212, "--" ], 
23657     [    8216,  "'" ],  
23658     [    8217, "'" ],  
23659     [    8220, '"' ],  
23660     [    8221, '"' ],  
23661     [    8226, "*" ],  
23662     [    8230, "..." ]
23663 ]; 
23664
23665     /*
23666  * - LGPL
23667  *
23668  * HtmlEditor
23669  * 
23670  */
23671
23672 /**
23673  * @class Roo.bootstrap.HtmlEditor
23674  * @extends Roo.bootstrap.TextArea
23675  * Bootstrap HtmlEditor class
23676
23677  * @constructor
23678  * Create a new HtmlEditor
23679  * @param {Object} config The config object
23680  */
23681
23682 Roo.bootstrap.HtmlEditor = function(config){
23683     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23684     if (!this.toolbars) {
23685         this.toolbars = [];
23686     }
23687     
23688     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23689     this.addEvents({
23690             /**
23691              * @event initialize
23692              * Fires when the editor is fully initialized (including the iframe)
23693              * @param {HtmlEditor} this
23694              */
23695             initialize: true,
23696             /**
23697              * @event activate
23698              * Fires when the editor is first receives the focus. Any insertion must wait
23699              * until after this event.
23700              * @param {HtmlEditor} this
23701              */
23702             activate: true,
23703              /**
23704              * @event beforesync
23705              * Fires before the textarea is updated with content from the editor iframe. Return false
23706              * to cancel the sync.
23707              * @param {HtmlEditor} this
23708              * @param {String} html
23709              */
23710             beforesync: true,
23711              /**
23712              * @event beforepush
23713              * Fires before the iframe editor is updated with content from the textarea. Return false
23714              * to cancel the push.
23715              * @param {HtmlEditor} this
23716              * @param {String} html
23717              */
23718             beforepush: true,
23719              /**
23720              * @event sync
23721              * Fires when the textarea is updated with content from the editor iframe.
23722              * @param {HtmlEditor} this
23723              * @param {String} html
23724              */
23725             sync: true,
23726              /**
23727              * @event push
23728              * Fires when the iframe editor is updated with content from the textarea.
23729              * @param {HtmlEditor} this
23730              * @param {String} html
23731              */
23732             push: true,
23733              /**
23734              * @event editmodechange
23735              * Fires when the editor switches edit modes
23736              * @param {HtmlEditor} this
23737              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23738              */
23739             editmodechange: true,
23740             /**
23741              * @event editorevent
23742              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23743              * @param {HtmlEditor} this
23744              */
23745             editorevent: true,
23746             /**
23747              * @event firstfocus
23748              * Fires when on first focus - needed by toolbars..
23749              * @param {HtmlEditor} this
23750              */
23751             firstfocus: true,
23752             /**
23753              * @event autosave
23754              * Auto save the htmlEditor value as a file into Events
23755              * @param {HtmlEditor} this
23756              */
23757             autosave: true,
23758             /**
23759              * @event savedpreview
23760              * preview the saved version of htmlEditor
23761              * @param {HtmlEditor} this
23762              */
23763             savedpreview: true
23764         });
23765 };
23766
23767
23768 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23769     
23770     
23771       /**
23772      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23773      */
23774     toolbars : false,
23775     
23776      /**
23777     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23778     */
23779     btns : [],
23780    
23781      /**
23782      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23783      *                        Roo.resizable.
23784      */
23785     resizable : false,
23786      /**
23787      * @cfg {Number} height (in pixels)
23788      */   
23789     height: 300,
23790    /**
23791      * @cfg {Number} width (in pixels)
23792      */   
23793     width: false,
23794     
23795     /**
23796      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23797      * 
23798      */
23799     stylesheets: false,
23800     
23801     // id of frame..
23802     frameId: false,
23803     
23804     // private properties
23805     validationEvent : false,
23806     deferHeight: true,
23807     initialized : false,
23808     activated : false,
23809     
23810     onFocus : Roo.emptyFn,
23811     iframePad:3,
23812     hideMode:'offsets',
23813     
23814     tbContainer : false,
23815     
23816     bodyCls : '',
23817     
23818     toolbarContainer :function() {
23819         return this.wrap.select('.x-html-editor-tb',true).first();
23820     },
23821
23822     /**
23823      * Protected method that will not generally be called directly. It
23824      * is called when the editor creates its toolbar. Override this method if you need to
23825      * add custom toolbar buttons.
23826      * @param {HtmlEditor} editor
23827      */
23828     createToolbar : function(){
23829         Roo.log('renewing');
23830         Roo.log("create toolbars");
23831         
23832         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23833         this.toolbars[0].render(this.toolbarContainer());
23834         
23835         return;
23836         
23837 //        if (!editor.toolbars || !editor.toolbars.length) {
23838 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23839 //        }
23840 //        
23841 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23842 //            editor.toolbars[i] = Roo.factory(
23843 //                    typeof(editor.toolbars[i]) == 'string' ?
23844 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23845 //                Roo.bootstrap.HtmlEditor);
23846 //            editor.toolbars[i].init(editor);
23847 //        }
23848     },
23849
23850      
23851     // private
23852     onRender : function(ct, position)
23853     {
23854        // Roo.log("Call onRender: " + this.xtype);
23855         var _t = this;
23856         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23857       
23858         this.wrap = this.inputEl().wrap({
23859             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23860         });
23861         
23862         this.editorcore.onRender(ct, position);
23863          
23864         if (this.resizable) {
23865             this.resizeEl = new Roo.Resizable(this.wrap, {
23866                 pinned : true,
23867                 wrap: true,
23868                 dynamic : true,
23869                 minHeight : this.height,
23870                 height: this.height,
23871                 handles : this.resizable,
23872                 width: this.width,
23873                 listeners : {
23874                     resize : function(r, w, h) {
23875                         _t.onResize(w,h); // -something
23876                     }
23877                 }
23878             });
23879             
23880         }
23881         this.createToolbar(this);
23882        
23883         
23884         if(!this.width && this.resizable){
23885             this.setSize(this.wrap.getSize());
23886         }
23887         if (this.resizeEl) {
23888             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23889             // should trigger onReize..
23890         }
23891         
23892     },
23893
23894     // private
23895     onResize : function(w, h)
23896     {
23897         Roo.log('resize: ' +w + ',' + h );
23898         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23899         var ew = false;
23900         var eh = false;
23901         
23902         if(this.inputEl() ){
23903             if(typeof w == 'number'){
23904                 var aw = w - this.wrap.getFrameWidth('lr');
23905                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23906                 ew = aw;
23907             }
23908             if(typeof h == 'number'){
23909                  var tbh = -11;  // fixme it needs to tool bar size!
23910                 for (var i =0; i < this.toolbars.length;i++) {
23911                     // fixme - ask toolbars for heights?
23912                     tbh += this.toolbars[i].el.getHeight();
23913                     //if (this.toolbars[i].footer) {
23914                     //    tbh += this.toolbars[i].footer.el.getHeight();
23915                     //}
23916                 }
23917               
23918                 
23919                 
23920                 
23921                 
23922                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23923                 ah -= 5; // knock a few pixes off for look..
23924                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23925                 var eh = ah;
23926             }
23927         }
23928         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23929         this.editorcore.onResize(ew,eh);
23930         
23931     },
23932
23933     /**
23934      * Toggles the editor between standard and source edit mode.
23935      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23936      */
23937     toggleSourceEdit : function(sourceEditMode)
23938     {
23939         this.editorcore.toggleSourceEdit(sourceEditMode);
23940         
23941         if(this.editorcore.sourceEditMode){
23942             Roo.log('editor - showing textarea');
23943             
23944 //            Roo.log('in');
23945 //            Roo.log(this.syncValue());
23946             this.syncValue();
23947             this.inputEl().removeClass(['hide', 'x-hidden']);
23948             this.inputEl().dom.removeAttribute('tabIndex');
23949             this.inputEl().focus();
23950         }else{
23951             Roo.log('editor - hiding textarea');
23952 //            Roo.log('out')
23953 //            Roo.log(this.pushValue()); 
23954             this.pushValue();
23955             
23956             this.inputEl().addClass(['hide', 'x-hidden']);
23957             this.inputEl().dom.setAttribute('tabIndex', -1);
23958             //this.deferFocus();
23959         }
23960          
23961         if(this.resizable){
23962             this.setSize(this.wrap.getSize());
23963         }
23964         
23965         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23966     },
23967  
23968     // private (for BoxComponent)
23969     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23970
23971     // private (for BoxComponent)
23972     getResizeEl : function(){
23973         return this.wrap;
23974     },
23975
23976     // private (for BoxComponent)
23977     getPositionEl : function(){
23978         return this.wrap;
23979     },
23980
23981     // private
23982     initEvents : function(){
23983         this.originalValue = this.getValue();
23984     },
23985
23986 //    /**
23987 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23988 //     * @method
23989 //     */
23990 //    markInvalid : Roo.emptyFn,
23991 //    /**
23992 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23993 //     * @method
23994 //     */
23995 //    clearInvalid : Roo.emptyFn,
23996
23997     setValue : function(v){
23998         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23999         this.editorcore.pushValue();
24000     },
24001
24002      
24003     // private
24004     deferFocus : function(){
24005         this.focus.defer(10, this);
24006     },
24007
24008     // doc'ed in Field
24009     focus : function(){
24010         this.editorcore.focus();
24011         
24012     },
24013       
24014
24015     // private
24016     onDestroy : function(){
24017         
24018         
24019         
24020         if(this.rendered){
24021             
24022             for (var i =0; i < this.toolbars.length;i++) {
24023                 // fixme - ask toolbars for heights?
24024                 this.toolbars[i].onDestroy();
24025             }
24026             
24027             this.wrap.dom.innerHTML = '';
24028             this.wrap.remove();
24029         }
24030     },
24031
24032     // private
24033     onFirstFocus : function(){
24034         //Roo.log("onFirstFocus");
24035         this.editorcore.onFirstFocus();
24036          for (var i =0; i < this.toolbars.length;i++) {
24037             this.toolbars[i].onFirstFocus();
24038         }
24039         
24040     },
24041     
24042     // private
24043     syncValue : function()
24044     {   
24045         this.editorcore.syncValue();
24046     },
24047     
24048     pushValue : function()
24049     {   
24050         this.editorcore.pushValue();
24051     }
24052      
24053     
24054     // hide stuff that is not compatible
24055     /**
24056      * @event blur
24057      * @hide
24058      */
24059     /**
24060      * @event change
24061      * @hide
24062      */
24063     /**
24064      * @event focus
24065      * @hide
24066      */
24067     /**
24068      * @event specialkey
24069      * @hide
24070      */
24071     /**
24072      * @cfg {String} fieldClass @hide
24073      */
24074     /**
24075      * @cfg {String} focusClass @hide
24076      */
24077     /**
24078      * @cfg {String} autoCreate @hide
24079      */
24080     /**
24081      * @cfg {String} inputType @hide
24082      */
24083      
24084     /**
24085      * @cfg {String} invalidText @hide
24086      */
24087     /**
24088      * @cfg {String} msgFx @hide
24089      */
24090     /**
24091      * @cfg {String} validateOnBlur @hide
24092      */
24093 });
24094  
24095     
24096    
24097    
24098    
24099       
24100 Roo.namespace('Roo.bootstrap.htmleditor');
24101 /**
24102  * @class Roo.bootstrap.HtmlEditorToolbar1
24103  * Basic Toolbar
24104  * 
24105  * @example
24106  * Usage:
24107  *
24108  new Roo.bootstrap.HtmlEditor({
24109     ....
24110     toolbars : [
24111         new Roo.bootstrap.HtmlEditorToolbar1({
24112             disable : { fonts: 1 , format: 1, ..., ... , ...],
24113             btns : [ .... ]
24114         })
24115     }
24116      
24117  * 
24118  * @cfg {Object} disable List of elements to disable..
24119  * @cfg {Array} btns List of additional buttons.
24120  * 
24121  * 
24122  * NEEDS Extra CSS? 
24123  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24124  */
24125  
24126 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24127 {
24128     
24129     Roo.apply(this, config);
24130     
24131     // default disabled, based on 'good practice'..
24132     this.disable = this.disable || {};
24133     Roo.applyIf(this.disable, {
24134         fontSize : true,
24135         colors : true,
24136         specialElements : true
24137     });
24138     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24139     
24140     this.editor = config.editor;
24141     this.editorcore = config.editor.editorcore;
24142     
24143     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24144     
24145     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24146     // dont call parent... till later.
24147 }
24148 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24149      
24150     bar : true,
24151     
24152     editor : false,
24153     editorcore : false,
24154     
24155     
24156     formats : [
24157         "p" ,  
24158         "h1","h2","h3","h4","h5","h6", 
24159         "pre", "code", 
24160         "abbr", "acronym", "address", "cite", "samp", "var",
24161         'div','span'
24162     ],
24163     
24164     onRender : function(ct, position)
24165     {
24166        // Roo.log("Call onRender: " + this.xtype);
24167         
24168        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24169        Roo.log(this.el);
24170        this.el.dom.style.marginBottom = '0';
24171        var _this = this;
24172        var editorcore = this.editorcore;
24173        var editor= this.editor;
24174        
24175        var children = [];
24176        var btn = function(id,cmd , toggle, handler, html){
24177        
24178             var  event = toggle ? 'toggle' : 'click';
24179        
24180             var a = {
24181                 size : 'sm',
24182                 xtype: 'Button',
24183                 xns: Roo.bootstrap,
24184                 //glyphicon : id,
24185                 fa: id,
24186                 cmd : id || cmd,
24187                 enableToggle:toggle !== false,
24188                 html : html || '',
24189                 pressed : toggle ? false : null,
24190                 listeners : {}
24191             };
24192             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24193                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24194             };
24195             children.push(a);
24196             return a;
24197        }
24198        
24199     //    var cb_box = function...
24200         
24201         var style = {
24202                 xtype: 'Button',
24203                 size : 'sm',
24204                 xns: Roo.bootstrap,
24205                 fa : 'font',
24206                 //html : 'submit'
24207                 menu : {
24208                     xtype: 'Menu',
24209                     xns: Roo.bootstrap,
24210                     items:  []
24211                 }
24212         };
24213         Roo.each(this.formats, function(f) {
24214             style.menu.items.push({
24215                 xtype :'MenuItem',
24216                 xns: Roo.bootstrap,
24217                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24218                 tagname : f,
24219                 listeners : {
24220                     click : function()
24221                     {
24222                         editorcore.insertTag(this.tagname);
24223                         editor.focus();
24224                     }
24225                 }
24226                 
24227             });
24228         });
24229         children.push(style);   
24230         
24231         btn('bold',false,true);
24232         btn('italic',false,true);
24233         btn('align-left', 'justifyleft',true);
24234         btn('align-center', 'justifycenter',true);
24235         btn('align-right' , 'justifyright',true);
24236         btn('link', false, false, function(btn) {
24237             //Roo.log("create link?");
24238             var url = prompt(this.createLinkText, this.defaultLinkValue);
24239             if(url && url != 'http:/'+'/'){
24240                 this.editorcore.relayCmd('createlink', url);
24241             }
24242         }),
24243         btn('list','insertunorderedlist',true);
24244         btn('pencil', false,true, function(btn){
24245                 Roo.log(this);
24246                 this.toggleSourceEdit(btn.pressed);
24247         });
24248         
24249         if (this.editor.btns.length > 0) {
24250             for (var i = 0; i<this.editor.btns.length; i++) {
24251                 children.push(this.editor.btns[i]);
24252             }
24253         }
24254         
24255         /*
24256         var cog = {
24257                 xtype: 'Button',
24258                 size : 'sm',
24259                 xns: Roo.bootstrap,
24260                 glyphicon : 'cog',
24261                 //html : 'submit'
24262                 menu : {
24263                     xtype: 'Menu',
24264                     xns: Roo.bootstrap,
24265                     items:  []
24266                 }
24267         };
24268         
24269         cog.menu.items.push({
24270             xtype :'MenuItem',
24271             xns: Roo.bootstrap,
24272             html : Clean styles,
24273             tagname : f,
24274             listeners : {
24275                 click : function()
24276                 {
24277                     editorcore.insertTag(this.tagname);
24278                     editor.focus();
24279                 }
24280             }
24281             
24282         });
24283        */
24284         
24285          
24286        this.xtype = 'NavSimplebar';
24287         
24288         for(var i=0;i< children.length;i++) {
24289             
24290             this.buttons.add(this.addxtypeChild(children[i]));
24291             
24292         }
24293         
24294         editor.on('editorevent', this.updateToolbar, this);
24295     },
24296     onBtnClick : function(id)
24297     {
24298        this.editorcore.relayCmd(id);
24299        this.editorcore.focus();
24300     },
24301     
24302     /**
24303      * Protected method that will not generally be called directly. It triggers
24304      * a toolbar update by reading the markup state of the current selection in the editor.
24305      */
24306     updateToolbar: function(){
24307
24308         if(!this.editorcore.activated){
24309             this.editor.onFirstFocus(); // is this neeed?
24310             return;
24311         }
24312
24313         var btns = this.buttons; 
24314         var doc = this.editorcore.doc;
24315         btns.get('bold').setActive(doc.queryCommandState('bold'));
24316         btns.get('italic').setActive(doc.queryCommandState('italic'));
24317         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24318         
24319         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24320         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24321         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24322         
24323         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24324         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24325          /*
24326         
24327         var ans = this.editorcore.getAllAncestors();
24328         if (this.formatCombo) {
24329             
24330             
24331             var store = this.formatCombo.store;
24332             this.formatCombo.setValue("");
24333             for (var i =0; i < ans.length;i++) {
24334                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24335                     // select it..
24336                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24337                     break;
24338                 }
24339             }
24340         }
24341         
24342         
24343         
24344         // hides menus... - so this cant be on a menu...
24345         Roo.bootstrap.MenuMgr.hideAll();
24346         */
24347         Roo.bootstrap.MenuMgr.hideAll();
24348         //this.editorsyncValue();
24349     },
24350     onFirstFocus: function() {
24351         this.buttons.each(function(item){
24352            item.enable();
24353         });
24354     },
24355     toggleSourceEdit : function(sourceEditMode){
24356         
24357           
24358         if(sourceEditMode){
24359             Roo.log("disabling buttons");
24360            this.buttons.each( function(item){
24361                 if(item.cmd != 'pencil'){
24362                     item.disable();
24363                 }
24364             });
24365           
24366         }else{
24367             Roo.log("enabling buttons");
24368             if(this.editorcore.initialized){
24369                 this.buttons.each( function(item){
24370                     item.enable();
24371                 });
24372             }
24373             
24374         }
24375         Roo.log("calling toggole on editor");
24376         // tell the editor that it's been pressed..
24377         this.editor.toggleSourceEdit(sourceEditMode);
24378        
24379     }
24380 });
24381
24382
24383
24384
24385
24386 /**
24387  * @class Roo.bootstrap.Table.AbstractSelectionModel
24388  * @extends Roo.util.Observable
24389  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24390  * implemented by descendant classes.  This class should not be directly instantiated.
24391  * @constructor
24392  */
24393 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24394     this.locked = false;
24395     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24396 };
24397
24398
24399 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24400     /** @ignore Called by the grid automatically. Do not call directly. */
24401     init : function(grid){
24402         this.grid = grid;
24403         this.initEvents();
24404     },
24405
24406     /**
24407      * Locks the selections.
24408      */
24409     lock : function(){
24410         this.locked = true;
24411     },
24412
24413     /**
24414      * Unlocks the selections.
24415      */
24416     unlock : function(){
24417         this.locked = false;
24418     },
24419
24420     /**
24421      * Returns true if the selections are locked.
24422      * @return {Boolean}
24423      */
24424     isLocked : function(){
24425         return this.locked;
24426     }
24427 });
24428 /**
24429  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24430  * @class Roo.bootstrap.Table.RowSelectionModel
24431  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24432  * It supports multiple selections and keyboard selection/navigation. 
24433  * @constructor
24434  * @param {Object} config
24435  */
24436
24437 Roo.bootstrap.Table.RowSelectionModel = function(config){
24438     Roo.apply(this, config);
24439     this.selections = new Roo.util.MixedCollection(false, function(o){
24440         return o.id;
24441     });
24442
24443     this.last = false;
24444     this.lastActive = false;
24445
24446     this.addEvents({
24447         /**
24448              * @event selectionchange
24449              * Fires when the selection changes
24450              * @param {SelectionModel} this
24451              */
24452             "selectionchange" : true,
24453         /**
24454              * @event afterselectionchange
24455              * Fires after the selection changes (eg. by key press or clicking)
24456              * @param {SelectionModel} this
24457              */
24458             "afterselectionchange" : true,
24459         /**
24460              * @event beforerowselect
24461              * Fires when a row is selected being selected, return false to cancel.
24462              * @param {SelectionModel} this
24463              * @param {Number} rowIndex The selected index
24464              * @param {Boolean} keepExisting False if other selections will be cleared
24465              */
24466             "beforerowselect" : true,
24467         /**
24468              * @event rowselect
24469              * Fires when a row is selected.
24470              * @param {SelectionModel} this
24471              * @param {Number} rowIndex The selected index
24472              * @param {Roo.data.Record} r The record
24473              */
24474             "rowselect" : true,
24475         /**
24476              * @event rowdeselect
24477              * Fires when a row is deselected.
24478              * @param {SelectionModel} this
24479              * @param {Number} rowIndex The selected index
24480              */
24481         "rowdeselect" : true
24482     });
24483     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24484     this.locked = false;
24485  };
24486
24487 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24488     /**
24489      * @cfg {Boolean} singleSelect
24490      * True to allow selection of only one row at a time (defaults to false)
24491      */
24492     singleSelect : false,
24493
24494     // private
24495     initEvents : function()
24496     {
24497
24498         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24499         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24500         //}else{ // allow click to work like normal
24501          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24502         //}
24503         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24504         this.grid.on("rowclick", this.handleMouseDown, this);
24505         
24506         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24507             "up" : function(e){
24508                 if(!e.shiftKey){
24509                     this.selectPrevious(e.shiftKey);
24510                 }else if(this.last !== false && this.lastActive !== false){
24511                     var last = this.last;
24512                     this.selectRange(this.last,  this.lastActive-1);
24513                     this.grid.getView().focusRow(this.lastActive);
24514                     if(last !== false){
24515                         this.last = last;
24516                     }
24517                 }else{
24518                     this.selectFirstRow();
24519                 }
24520                 this.fireEvent("afterselectionchange", this);
24521             },
24522             "down" : function(e){
24523                 if(!e.shiftKey){
24524                     this.selectNext(e.shiftKey);
24525                 }else if(this.last !== false && this.lastActive !== false){
24526                     var last = this.last;
24527                     this.selectRange(this.last,  this.lastActive+1);
24528                     this.grid.getView().focusRow(this.lastActive);
24529                     if(last !== false){
24530                         this.last = last;
24531                     }
24532                 }else{
24533                     this.selectFirstRow();
24534                 }
24535                 this.fireEvent("afterselectionchange", this);
24536             },
24537             scope: this
24538         });
24539         this.grid.store.on('load', function(){
24540             this.selections.clear();
24541         },this);
24542         /*
24543         var view = this.grid.view;
24544         view.on("refresh", this.onRefresh, this);
24545         view.on("rowupdated", this.onRowUpdated, this);
24546         view.on("rowremoved", this.onRemove, this);
24547         */
24548     },
24549
24550     // private
24551     onRefresh : function()
24552     {
24553         var ds = this.grid.store, i, v = this.grid.view;
24554         var s = this.selections;
24555         s.each(function(r){
24556             if((i = ds.indexOfId(r.id)) != -1){
24557                 v.onRowSelect(i);
24558             }else{
24559                 s.remove(r);
24560             }
24561         });
24562     },
24563
24564     // private
24565     onRemove : function(v, index, r){
24566         this.selections.remove(r);
24567     },
24568
24569     // private
24570     onRowUpdated : function(v, index, r){
24571         if(this.isSelected(r)){
24572             v.onRowSelect(index);
24573         }
24574     },
24575
24576     /**
24577      * Select records.
24578      * @param {Array} records The records to select
24579      * @param {Boolean} keepExisting (optional) True to keep existing selections
24580      */
24581     selectRecords : function(records, keepExisting)
24582     {
24583         if(!keepExisting){
24584             this.clearSelections();
24585         }
24586             var ds = this.grid.store;
24587         for(var i = 0, len = records.length; i < len; i++){
24588             this.selectRow(ds.indexOf(records[i]), true);
24589         }
24590     },
24591
24592     /**
24593      * Gets the number of selected rows.
24594      * @return {Number}
24595      */
24596     getCount : function(){
24597         return this.selections.length;
24598     },
24599
24600     /**
24601      * Selects the first row in the grid.
24602      */
24603     selectFirstRow : function(){
24604         this.selectRow(0);
24605     },
24606
24607     /**
24608      * Select the last row.
24609      * @param {Boolean} keepExisting (optional) True to keep existing selections
24610      */
24611     selectLastRow : function(keepExisting){
24612         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24613         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24614     },
24615
24616     /**
24617      * Selects the row immediately following the last selected row.
24618      * @param {Boolean} keepExisting (optional) True to keep existing selections
24619      */
24620     selectNext : function(keepExisting)
24621     {
24622             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24623             this.selectRow(this.last+1, keepExisting);
24624             this.grid.getView().focusRow(this.last);
24625         }
24626     },
24627
24628     /**
24629      * Selects the row that precedes the last selected row.
24630      * @param {Boolean} keepExisting (optional) True to keep existing selections
24631      */
24632     selectPrevious : function(keepExisting){
24633         if(this.last){
24634             this.selectRow(this.last-1, keepExisting);
24635             this.grid.getView().focusRow(this.last);
24636         }
24637     },
24638
24639     /**
24640      * Returns the selected records
24641      * @return {Array} Array of selected records
24642      */
24643     getSelections : function(){
24644         return [].concat(this.selections.items);
24645     },
24646
24647     /**
24648      * Returns the first selected record.
24649      * @return {Record}
24650      */
24651     getSelected : function(){
24652         return this.selections.itemAt(0);
24653     },
24654
24655
24656     /**
24657      * Clears all selections.
24658      */
24659     clearSelections : function(fast)
24660     {
24661         if(this.locked) {
24662             return;
24663         }
24664         if(fast !== true){
24665                 var ds = this.grid.store;
24666             var s = this.selections;
24667             s.each(function(r){
24668                 this.deselectRow(ds.indexOfId(r.id));
24669             }, this);
24670             s.clear();
24671         }else{
24672             this.selections.clear();
24673         }
24674         this.last = false;
24675     },
24676
24677
24678     /**
24679      * Selects all rows.
24680      */
24681     selectAll : function(){
24682         if(this.locked) {
24683             return;
24684         }
24685         this.selections.clear();
24686         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24687             this.selectRow(i, true);
24688         }
24689     },
24690
24691     /**
24692      * Returns True if there is a selection.
24693      * @return {Boolean}
24694      */
24695     hasSelection : function(){
24696         return this.selections.length > 0;
24697     },
24698
24699     /**
24700      * Returns True if the specified row is selected.
24701      * @param {Number/Record} record The record or index of the record to check
24702      * @return {Boolean}
24703      */
24704     isSelected : function(index){
24705             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24706         return (r && this.selections.key(r.id) ? true : false);
24707     },
24708
24709     /**
24710      * Returns True if the specified record id is selected.
24711      * @param {String} id The id of record to check
24712      * @return {Boolean}
24713      */
24714     isIdSelected : function(id){
24715         return (this.selections.key(id) ? true : false);
24716     },
24717
24718
24719     // private
24720     handleMouseDBClick : function(e, t){
24721         
24722     },
24723     // private
24724     handleMouseDown : function(e, t)
24725     {
24726             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24727         if(this.isLocked() || rowIndex < 0 ){
24728             return;
24729         };
24730         if(e.shiftKey && this.last !== false){
24731             var last = this.last;
24732             this.selectRange(last, rowIndex, e.ctrlKey);
24733             this.last = last; // reset the last
24734             t.focus();
24735     
24736         }else{
24737             var isSelected = this.isSelected(rowIndex);
24738             //Roo.log("select row:" + rowIndex);
24739             if(isSelected){
24740                 this.deselectRow(rowIndex);
24741             } else {
24742                         this.selectRow(rowIndex, true);
24743             }
24744     
24745             /*
24746                 if(e.button !== 0 && isSelected){
24747                 alert('rowIndex 2: ' + rowIndex);
24748                     view.focusRow(rowIndex);
24749                 }else if(e.ctrlKey && isSelected){
24750                     this.deselectRow(rowIndex);
24751                 }else if(!isSelected){
24752                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24753                     view.focusRow(rowIndex);
24754                 }
24755             */
24756         }
24757         this.fireEvent("afterselectionchange", this);
24758     },
24759     // private
24760     handleDragableRowClick :  function(grid, rowIndex, e) 
24761     {
24762         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24763             this.selectRow(rowIndex, false);
24764             grid.view.focusRow(rowIndex);
24765              this.fireEvent("afterselectionchange", this);
24766         }
24767     },
24768     
24769     /**
24770      * Selects multiple rows.
24771      * @param {Array} rows Array of the indexes of the row to select
24772      * @param {Boolean} keepExisting (optional) True to keep existing selections
24773      */
24774     selectRows : function(rows, keepExisting){
24775         if(!keepExisting){
24776             this.clearSelections();
24777         }
24778         for(var i = 0, len = rows.length; i < len; i++){
24779             this.selectRow(rows[i], true);
24780         }
24781     },
24782
24783     /**
24784      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24785      * @param {Number} startRow The index of the first row in the range
24786      * @param {Number} endRow The index of the last row in the range
24787      * @param {Boolean} keepExisting (optional) True to retain existing selections
24788      */
24789     selectRange : function(startRow, endRow, keepExisting){
24790         if(this.locked) {
24791             return;
24792         }
24793         if(!keepExisting){
24794             this.clearSelections();
24795         }
24796         if(startRow <= endRow){
24797             for(var i = startRow; i <= endRow; i++){
24798                 this.selectRow(i, true);
24799             }
24800         }else{
24801             for(var i = startRow; i >= endRow; i--){
24802                 this.selectRow(i, true);
24803             }
24804         }
24805     },
24806
24807     /**
24808      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24809      * @param {Number} startRow The index of the first row in the range
24810      * @param {Number} endRow The index of the last row in the range
24811      */
24812     deselectRange : function(startRow, endRow, preventViewNotify){
24813         if(this.locked) {
24814             return;
24815         }
24816         for(var i = startRow; i <= endRow; i++){
24817             this.deselectRow(i, preventViewNotify);
24818         }
24819     },
24820
24821     /**
24822      * Selects a row.
24823      * @param {Number} row The index of the row to select
24824      * @param {Boolean} keepExisting (optional) True to keep existing selections
24825      */
24826     selectRow : function(index, keepExisting, preventViewNotify)
24827     {
24828             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24829             return;
24830         }
24831         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24832             if(!keepExisting || this.singleSelect){
24833                 this.clearSelections();
24834             }
24835             
24836             var r = this.grid.store.getAt(index);
24837             //console.log('selectRow - record id :' + r.id);
24838             
24839             this.selections.add(r);
24840             this.last = this.lastActive = index;
24841             if(!preventViewNotify){
24842                 var proxy = new Roo.Element(
24843                                 this.grid.getRowDom(index)
24844                 );
24845                 proxy.addClass('bg-info info');
24846             }
24847             this.fireEvent("rowselect", this, index, r);
24848             this.fireEvent("selectionchange", this);
24849         }
24850     },
24851
24852     /**
24853      * Deselects a row.
24854      * @param {Number} row The index of the row to deselect
24855      */
24856     deselectRow : function(index, preventViewNotify)
24857     {
24858         if(this.locked) {
24859             return;
24860         }
24861         if(this.last == index){
24862             this.last = false;
24863         }
24864         if(this.lastActive == index){
24865             this.lastActive = false;
24866         }
24867         
24868         var r = this.grid.store.getAt(index);
24869         if (!r) {
24870             return;
24871         }
24872         
24873         this.selections.remove(r);
24874         //.console.log('deselectRow - record id :' + r.id);
24875         if(!preventViewNotify){
24876         
24877             var proxy = new Roo.Element(
24878                 this.grid.getRowDom(index)
24879             );
24880             proxy.removeClass('bg-info info');
24881         }
24882         this.fireEvent("rowdeselect", this, index);
24883         this.fireEvent("selectionchange", this);
24884     },
24885
24886     // private
24887     restoreLast : function(){
24888         if(this._last){
24889             this.last = this._last;
24890         }
24891     },
24892
24893     // private
24894     acceptsNav : function(row, col, cm){
24895         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24896     },
24897
24898     // private
24899     onEditorKey : function(field, e){
24900         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24901         if(k == e.TAB){
24902             e.stopEvent();
24903             ed.completeEdit();
24904             if(e.shiftKey){
24905                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24906             }else{
24907                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24908             }
24909         }else if(k == e.ENTER && !e.ctrlKey){
24910             e.stopEvent();
24911             ed.completeEdit();
24912             if(e.shiftKey){
24913                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24914             }else{
24915                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24916             }
24917         }else if(k == e.ESC){
24918             ed.cancelEdit();
24919         }
24920         if(newCell){
24921             g.startEditing(newCell[0], newCell[1]);
24922         }
24923     }
24924 });
24925 /*
24926  * Based on:
24927  * Ext JS Library 1.1.1
24928  * Copyright(c) 2006-2007, Ext JS, LLC.
24929  *
24930  * Originally Released Under LGPL - original licence link has changed is not relivant.
24931  *
24932  * Fork - LGPL
24933  * <script type="text/javascript">
24934  */
24935  
24936 /**
24937  * @class Roo.bootstrap.PagingToolbar
24938  * @extends Roo.bootstrap.NavSimplebar
24939  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24940  * @constructor
24941  * Create a new PagingToolbar
24942  * @param {Object} config The config object
24943  * @param {Roo.data.Store} store
24944  */
24945 Roo.bootstrap.PagingToolbar = function(config)
24946 {
24947     // old args format still supported... - xtype is prefered..
24948         // created from xtype...
24949     
24950     this.ds = config.dataSource;
24951     
24952     if (config.store && !this.ds) {
24953         this.store= Roo.factory(config.store, Roo.data);
24954         this.ds = this.store;
24955         this.ds.xmodule = this.xmodule || false;
24956     }
24957     
24958     this.toolbarItems = [];
24959     if (config.items) {
24960         this.toolbarItems = config.items;
24961     }
24962     
24963     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24964     
24965     this.cursor = 0;
24966     
24967     if (this.ds) { 
24968         this.bind(this.ds);
24969     }
24970     
24971     if (Roo.bootstrap.version == 4) {
24972         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24973     } else {
24974         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24975     }
24976     
24977 };
24978
24979 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24980     /**
24981      * @cfg {Roo.data.Store} dataSource
24982      * The underlying data store providing the paged data
24983      */
24984     /**
24985      * @cfg {String/HTMLElement/Element} container
24986      * container The id or element that will contain the toolbar
24987      */
24988     /**
24989      * @cfg {Boolean} displayInfo
24990      * True to display the displayMsg (defaults to false)
24991      */
24992     /**
24993      * @cfg {Number} pageSize
24994      * The number of records to display per page (defaults to 20)
24995      */
24996     pageSize: 20,
24997     /**
24998      * @cfg {String} displayMsg
24999      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25000      */
25001     displayMsg : 'Displaying {0} - {1} of {2}',
25002     /**
25003      * @cfg {String} emptyMsg
25004      * The message to display when no records are found (defaults to "No data to display")
25005      */
25006     emptyMsg : 'No data to display',
25007     /**
25008      * Customizable piece of the default paging text (defaults to "Page")
25009      * @type String
25010      */
25011     beforePageText : "Page",
25012     /**
25013      * Customizable piece of the default paging text (defaults to "of %0")
25014      * @type String
25015      */
25016     afterPageText : "of {0}",
25017     /**
25018      * Customizable piece of the default paging text (defaults to "First Page")
25019      * @type String
25020      */
25021     firstText : "First Page",
25022     /**
25023      * Customizable piece of the default paging text (defaults to "Previous Page")
25024      * @type String
25025      */
25026     prevText : "Previous Page",
25027     /**
25028      * Customizable piece of the default paging text (defaults to "Next Page")
25029      * @type String
25030      */
25031     nextText : "Next Page",
25032     /**
25033      * Customizable piece of the default paging text (defaults to "Last Page")
25034      * @type String
25035      */
25036     lastText : "Last Page",
25037     /**
25038      * Customizable piece of the default paging text (defaults to "Refresh")
25039      * @type String
25040      */
25041     refreshText : "Refresh",
25042
25043     buttons : false,
25044     // private
25045     onRender : function(ct, position) 
25046     {
25047         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25048         this.navgroup.parentId = this.id;
25049         this.navgroup.onRender(this.el, null);
25050         // add the buttons to the navgroup
25051         
25052         if(this.displayInfo){
25053             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25054             this.displayEl = this.el.select('.x-paging-info', true).first();
25055 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25056 //            this.displayEl = navel.el.select('span',true).first();
25057         }
25058         
25059         var _this = this;
25060         
25061         if(this.buttons){
25062             Roo.each(_this.buttons, function(e){ // this might need to use render????
25063                Roo.factory(e).render(_this.el);
25064             });
25065         }
25066             
25067         Roo.each(_this.toolbarItems, function(e) {
25068             _this.navgroup.addItem(e);
25069         });
25070         
25071         
25072         this.first = this.navgroup.addItem({
25073             tooltip: this.firstText,
25074             cls: "prev btn-outline-secondary",
25075             html : ' <i class="fa fa-step-backward"></i>',
25076             disabled: true,
25077             preventDefault: true,
25078             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25079         });
25080         
25081         this.prev =  this.navgroup.addItem({
25082             tooltip: this.prevText,
25083             cls: "prev btn-outline-secondary",
25084             html : ' <i class="fa fa-backward"></i>',
25085             disabled: true,
25086             preventDefault: true,
25087             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25088         });
25089     //this.addSeparator();
25090         
25091         
25092         var field = this.navgroup.addItem( {
25093             tagtype : 'span',
25094             cls : 'x-paging-position  btn-outline-secondary',
25095              disabled: true,
25096             html : this.beforePageText  +
25097                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25098                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25099          } ); //?? escaped?
25100         
25101         this.field = field.el.select('input', true).first();
25102         this.field.on("keydown", this.onPagingKeydown, this);
25103         this.field.on("focus", function(){this.dom.select();});
25104     
25105     
25106         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25107         //this.field.setHeight(18);
25108         //this.addSeparator();
25109         this.next = this.navgroup.addItem({
25110             tooltip: this.nextText,
25111             cls: "next btn-outline-secondary",
25112             html : ' <i class="fa fa-forward"></i>',
25113             disabled: true,
25114             preventDefault: true,
25115             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25116         });
25117         this.last = this.navgroup.addItem({
25118             tooltip: this.lastText,
25119             html : ' <i class="fa fa-step-forward"></i>',
25120             cls: "next btn-outline-secondary",
25121             disabled: true,
25122             preventDefault: true,
25123             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25124         });
25125     //this.addSeparator();
25126         this.loading = this.navgroup.addItem({
25127             tooltip: this.refreshText,
25128             cls: "btn-outline-secondary",
25129             html : ' <i class="fa fa-refresh"></i>',
25130             preventDefault: true,
25131             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25132         });
25133         
25134     },
25135
25136     // private
25137     updateInfo : function(){
25138         if(this.displayEl){
25139             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25140             var msg = count == 0 ?
25141                 this.emptyMsg :
25142                 String.format(
25143                     this.displayMsg,
25144                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25145                 );
25146             this.displayEl.update(msg);
25147         }
25148     },
25149
25150     // private
25151     onLoad : function(ds, r, o)
25152     {
25153         this.cursor = o.params.start ? o.params.start : 0;
25154         
25155         var d = this.getPageData(),
25156             ap = d.activePage,
25157             ps = d.pages;
25158         
25159         
25160         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25161         this.field.dom.value = ap;
25162         this.first.setDisabled(ap == 1);
25163         this.prev.setDisabled(ap == 1);
25164         this.next.setDisabled(ap == ps);
25165         this.last.setDisabled(ap == ps);
25166         this.loading.enable();
25167         this.updateInfo();
25168     },
25169
25170     // private
25171     getPageData : function(){
25172         var total = this.ds.getTotalCount();
25173         return {
25174             total : total,
25175             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25176             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25177         };
25178     },
25179
25180     // private
25181     onLoadError : function(){
25182         this.loading.enable();
25183     },
25184
25185     // private
25186     onPagingKeydown : function(e){
25187         var k = e.getKey();
25188         var d = this.getPageData();
25189         if(k == e.RETURN){
25190             var v = this.field.dom.value, pageNum;
25191             if(!v || isNaN(pageNum = parseInt(v, 10))){
25192                 this.field.dom.value = d.activePage;
25193                 return;
25194             }
25195             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25196             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25197             e.stopEvent();
25198         }
25199         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))
25200         {
25201           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25202           this.field.dom.value = pageNum;
25203           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25204           e.stopEvent();
25205         }
25206         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25207         {
25208           var v = this.field.dom.value, pageNum; 
25209           var increment = (e.shiftKey) ? 10 : 1;
25210           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25211                 increment *= -1;
25212           }
25213           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25214             this.field.dom.value = d.activePage;
25215             return;
25216           }
25217           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25218           {
25219             this.field.dom.value = parseInt(v, 10) + increment;
25220             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25221             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25222           }
25223           e.stopEvent();
25224         }
25225     },
25226
25227     // private
25228     beforeLoad : function(){
25229         if(this.loading){
25230             this.loading.disable();
25231         }
25232     },
25233
25234     // private
25235     onClick : function(which){
25236         
25237         var ds = this.ds;
25238         if (!ds) {
25239             return;
25240         }
25241         
25242         switch(which){
25243             case "first":
25244                 ds.load({params:{start: 0, limit: this.pageSize}});
25245             break;
25246             case "prev":
25247                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25248             break;
25249             case "next":
25250                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25251             break;
25252             case "last":
25253                 var total = ds.getTotalCount();
25254                 var extra = total % this.pageSize;
25255                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25256                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25257             break;
25258             case "refresh":
25259                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25260             break;
25261         }
25262     },
25263
25264     /**
25265      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25266      * @param {Roo.data.Store} store The data store to unbind
25267      */
25268     unbind : function(ds){
25269         ds.un("beforeload", this.beforeLoad, this);
25270         ds.un("load", this.onLoad, this);
25271         ds.un("loadexception", this.onLoadError, this);
25272         ds.un("remove", this.updateInfo, this);
25273         ds.un("add", this.updateInfo, this);
25274         this.ds = undefined;
25275     },
25276
25277     /**
25278      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25279      * @param {Roo.data.Store} store The data store to bind
25280      */
25281     bind : function(ds){
25282         ds.on("beforeload", this.beforeLoad, this);
25283         ds.on("load", this.onLoad, this);
25284         ds.on("loadexception", this.onLoadError, this);
25285         ds.on("remove", this.updateInfo, this);
25286         ds.on("add", this.updateInfo, this);
25287         this.ds = ds;
25288     }
25289 });/*
25290  * - LGPL
25291  *
25292  * element
25293  * 
25294  */
25295
25296 /**
25297  * @class Roo.bootstrap.MessageBar
25298  * @extends Roo.bootstrap.Component
25299  * Bootstrap MessageBar class
25300  * @cfg {String} html contents of the MessageBar
25301  * @cfg {String} weight (info | success | warning | danger) default info
25302  * @cfg {String} beforeClass insert the bar before the given class
25303  * @cfg {Boolean} closable (true | false) default false
25304  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25305  * 
25306  * @constructor
25307  * Create a new Element
25308  * @param {Object} config The config object
25309  */
25310
25311 Roo.bootstrap.MessageBar = function(config){
25312     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25313 };
25314
25315 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25316     
25317     html: '',
25318     weight: 'info',
25319     closable: false,
25320     fixed: false,
25321     beforeClass: 'bootstrap-sticky-wrap',
25322     
25323     getAutoCreate : function(){
25324         
25325         var cfg = {
25326             tag: 'div',
25327             cls: 'alert alert-dismissable alert-' + this.weight,
25328             cn: [
25329                 {
25330                     tag: 'span',
25331                     cls: 'message',
25332                     html: this.html || ''
25333                 }
25334             ]
25335         };
25336         
25337         if(this.fixed){
25338             cfg.cls += ' alert-messages-fixed';
25339         }
25340         
25341         if(this.closable){
25342             cfg.cn.push({
25343                 tag: 'button',
25344                 cls: 'close',
25345                 html: 'x'
25346             });
25347         }
25348         
25349         return cfg;
25350     },
25351     
25352     onRender : function(ct, position)
25353     {
25354         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25355         
25356         if(!this.el){
25357             var cfg = Roo.apply({},  this.getAutoCreate());
25358             cfg.id = Roo.id();
25359             
25360             if (this.cls) {
25361                 cfg.cls += ' ' + this.cls;
25362             }
25363             if (this.style) {
25364                 cfg.style = this.style;
25365             }
25366             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25367             
25368             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25369         }
25370         
25371         this.el.select('>button.close').on('click', this.hide, this);
25372         
25373     },
25374     
25375     show : function()
25376     {
25377         if (!this.rendered) {
25378             this.render();
25379         }
25380         
25381         this.el.show();
25382         
25383         this.fireEvent('show', this);
25384         
25385     },
25386     
25387     hide : function()
25388     {
25389         if (!this.rendered) {
25390             this.render();
25391         }
25392         
25393         this.el.hide();
25394         
25395         this.fireEvent('hide', this);
25396     },
25397     
25398     update : function()
25399     {
25400 //        var e = this.el.dom.firstChild;
25401 //        
25402 //        if(this.closable){
25403 //            e = e.nextSibling;
25404 //        }
25405 //        
25406 //        e.data = this.html || '';
25407
25408         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25409     }
25410    
25411 });
25412
25413  
25414
25415      /*
25416  * - LGPL
25417  *
25418  * Graph
25419  * 
25420  */
25421
25422
25423 /**
25424  * @class Roo.bootstrap.Graph
25425  * @extends Roo.bootstrap.Component
25426  * Bootstrap Graph class
25427 > Prameters
25428  -sm {number} sm 4
25429  -md {number} md 5
25430  @cfg {String} graphtype  bar | vbar | pie
25431  @cfg {number} g_x coodinator | centre x (pie)
25432  @cfg {number} g_y coodinator | centre y (pie)
25433  @cfg {number} g_r radius (pie)
25434  @cfg {number} g_height height of the chart (respected by all elements in the set)
25435  @cfg {number} g_width width of the chart (respected by all elements in the set)
25436  @cfg {Object} title The title of the chart
25437     
25438  -{Array}  values
25439  -opts (object) options for the chart 
25440      o {
25441      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25442      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25443      o vgutter (number)
25444      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.
25445      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25446      o to
25447      o stretch (boolean)
25448      o }
25449  -opts (object) options for the pie
25450      o{
25451      o cut
25452      o startAngle (number)
25453      o endAngle (number)
25454      } 
25455  *
25456  * @constructor
25457  * Create a new Input
25458  * @param {Object} config The config object
25459  */
25460
25461 Roo.bootstrap.Graph = function(config){
25462     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25463     
25464     this.addEvents({
25465         // img events
25466         /**
25467          * @event click
25468          * The img click event for the img.
25469          * @param {Roo.EventObject} e
25470          */
25471         "click" : true
25472     });
25473 };
25474
25475 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25476     
25477     sm: 4,
25478     md: 5,
25479     graphtype: 'bar',
25480     g_height: 250,
25481     g_width: 400,
25482     g_x: 50,
25483     g_y: 50,
25484     g_r: 30,
25485     opts:{
25486         //g_colors: this.colors,
25487         g_type: 'soft',
25488         g_gutter: '20%'
25489
25490     },
25491     title : false,
25492
25493     getAutoCreate : function(){
25494         
25495         var cfg = {
25496             tag: 'div',
25497             html : null
25498         };
25499         
25500         
25501         return  cfg;
25502     },
25503
25504     onRender : function(ct,position){
25505         
25506         
25507         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25508         
25509         if (typeof(Raphael) == 'undefined') {
25510             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25511             return;
25512         }
25513         
25514         this.raphael = Raphael(this.el.dom);
25515         
25516                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25517                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25518                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25519                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25520                 /*
25521                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25522                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25523                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25524                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25525                 
25526                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25527                 r.barchart(330, 10, 300, 220, data1);
25528                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25529                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25530                 */
25531                 
25532                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25533                 // r.barchart(30, 30, 560, 250,  xdata, {
25534                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25535                 //     axis : "0 0 1 1",
25536                 //     axisxlabels :  xdata
25537                 //     //yvalues : cols,
25538                    
25539                 // });
25540 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25541 //        
25542 //        this.load(null,xdata,{
25543 //                axis : "0 0 1 1",
25544 //                axisxlabels :  xdata
25545 //                });
25546
25547     },
25548
25549     load : function(graphtype,xdata,opts)
25550     {
25551         this.raphael.clear();
25552         if(!graphtype) {
25553             graphtype = this.graphtype;
25554         }
25555         if(!opts){
25556             opts = this.opts;
25557         }
25558         var r = this.raphael,
25559             fin = function () {
25560                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25561             },
25562             fout = function () {
25563                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25564             },
25565             pfin = function() {
25566                 this.sector.stop();
25567                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25568
25569                 if (this.label) {
25570                     this.label[0].stop();
25571                     this.label[0].attr({ r: 7.5 });
25572                     this.label[1].attr({ "font-weight": 800 });
25573                 }
25574             },
25575             pfout = function() {
25576                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25577
25578                 if (this.label) {
25579                     this.label[0].animate({ r: 5 }, 500, "bounce");
25580                     this.label[1].attr({ "font-weight": 400 });
25581                 }
25582             };
25583
25584         switch(graphtype){
25585             case 'bar':
25586                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25587                 break;
25588             case 'hbar':
25589                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25590                 break;
25591             case 'pie':
25592 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25593 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25594 //            
25595                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25596                 
25597                 break;
25598
25599         }
25600         
25601         if(this.title){
25602             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25603         }
25604         
25605     },
25606     
25607     setTitle: function(o)
25608     {
25609         this.title = o;
25610     },
25611     
25612     initEvents: function() {
25613         
25614         if(!this.href){
25615             this.el.on('click', this.onClick, this);
25616         }
25617     },
25618     
25619     onClick : function(e)
25620     {
25621         Roo.log('img onclick');
25622         this.fireEvent('click', this, e);
25623     }
25624    
25625 });
25626
25627  
25628 /*
25629  * - LGPL
25630  *
25631  * numberBox
25632  * 
25633  */
25634 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25635
25636 /**
25637  * @class Roo.bootstrap.dash.NumberBox
25638  * @extends Roo.bootstrap.Component
25639  * Bootstrap NumberBox class
25640  * @cfg {String} headline Box headline
25641  * @cfg {String} content Box content
25642  * @cfg {String} icon Box icon
25643  * @cfg {String} footer Footer text
25644  * @cfg {String} fhref Footer href
25645  * 
25646  * @constructor
25647  * Create a new NumberBox
25648  * @param {Object} config The config object
25649  */
25650
25651
25652 Roo.bootstrap.dash.NumberBox = function(config){
25653     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25654     
25655 };
25656
25657 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25658     
25659     headline : '',
25660     content : '',
25661     icon : '',
25662     footer : '',
25663     fhref : '',
25664     ficon : '',
25665     
25666     getAutoCreate : function(){
25667         
25668         var cfg = {
25669             tag : 'div',
25670             cls : 'small-box ',
25671             cn : [
25672                 {
25673                     tag : 'div',
25674                     cls : 'inner',
25675                     cn :[
25676                         {
25677                             tag : 'h3',
25678                             cls : 'roo-headline',
25679                             html : this.headline
25680                         },
25681                         {
25682                             tag : 'p',
25683                             cls : 'roo-content',
25684                             html : this.content
25685                         }
25686                     ]
25687                 }
25688             ]
25689         };
25690         
25691         if(this.icon){
25692             cfg.cn.push({
25693                 tag : 'div',
25694                 cls : 'icon',
25695                 cn :[
25696                     {
25697                         tag : 'i',
25698                         cls : 'ion ' + this.icon
25699                     }
25700                 ]
25701             });
25702         }
25703         
25704         if(this.footer){
25705             var footer = {
25706                 tag : 'a',
25707                 cls : 'small-box-footer',
25708                 href : this.fhref || '#',
25709                 html : this.footer
25710             };
25711             
25712             cfg.cn.push(footer);
25713             
25714         }
25715         
25716         return  cfg;
25717     },
25718
25719     onRender : function(ct,position){
25720         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25721
25722
25723        
25724                 
25725     },
25726
25727     setHeadline: function (value)
25728     {
25729         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25730     },
25731     
25732     setFooter: function (value, href)
25733     {
25734         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25735         
25736         if(href){
25737             this.el.select('a.small-box-footer',true).first().attr('href', href);
25738         }
25739         
25740     },
25741
25742     setContent: function (value)
25743     {
25744         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25745     },
25746
25747     initEvents: function() 
25748     {   
25749         
25750     }
25751     
25752 });
25753
25754  
25755 /*
25756  * - LGPL
25757  *
25758  * TabBox
25759  * 
25760  */
25761 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25762
25763 /**
25764  * @class Roo.bootstrap.dash.TabBox
25765  * @extends Roo.bootstrap.Component
25766  * Bootstrap TabBox class
25767  * @cfg {String} title Title of the TabBox
25768  * @cfg {String} icon Icon of the TabBox
25769  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25770  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25771  * 
25772  * @constructor
25773  * Create a new TabBox
25774  * @param {Object} config The config object
25775  */
25776
25777
25778 Roo.bootstrap.dash.TabBox = function(config){
25779     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25780     this.addEvents({
25781         // raw events
25782         /**
25783          * @event addpane
25784          * When a pane is added
25785          * @param {Roo.bootstrap.dash.TabPane} pane
25786          */
25787         "addpane" : true,
25788         /**
25789          * @event activatepane
25790          * When a pane is activated
25791          * @param {Roo.bootstrap.dash.TabPane} pane
25792          */
25793         "activatepane" : true
25794         
25795          
25796     });
25797     
25798     this.panes = [];
25799 };
25800
25801 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25802
25803     title : '',
25804     icon : false,
25805     showtabs : true,
25806     tabScrollable : false,
25807     
25808     getChildContainer : function()
25809     {
25810         return this.el.select('.tab-content', true).first();
25811     },
25812     
25813     getAutoCreate : function(){
25814         
25815         var header = {
25816             tag: 'li',
25817             cls: 'pull-left header',
25818             html: this.title,
25819             cn : []
25820         };
25821         
25822         if(this.icon){
25823             header.cn.push({
25824                 tag: 'i',
25825                 cls: 'fa ' + this.icon
25826             });
25827         }
25828         
25829         var h = {
25830             tag: 'ul',
25831             cls: 'nav nav-tabs pull-right',
25832             cn: [
25833                 header
25834             ]
25835         };
25836         
25837         if(this.tabScrollable){
25838             h = {
25839                 tag: 'div',
25840                 cls: 'tab-header',
25841                 cn: [
25842                     {
25843                         tag: 'ul',
25844                         cls: 'nav nav-tabs pull-right',
25845                         cn: [
25846                             header
25847                         ]
25848                     }
25849                 ]
25850             };
25851         }
25852         
25853         var cfg = {
25854             tag: 'div',
25855             cls: 'nav-tabs-custom',
25856             cn: [
25857                 h,
25858                 {
25859                     tag: 'div',
25860                     cls: 'tab-content no-padding',
25861                     cn: []
25862                 }
25863             ]
25864         };
25865
25866         return  cfg;
25867     },
25868     initEvents : function()
25869     {
25870         //Roo.log('add add pane handler');
25871         this.on('addpane', this.onAddPane, this);
25872     },
25873      /**
25874      * Updates the box title
25875      * @param {String} html to set the title to.
25876      */
25877     setTitle : function(value)
25878     {
25879         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25880     },
25881     onAddPane : function(pane)
25882     {
25883         this.panes.push(pane);
25884         //Roo.log('addpane');
25885         //Roo.log(pane);
25886         // tabs are rendere left to right..
25887         if(!this.showtabs){
25888             return;
25889         }
25890         
25891         var ctr = this.el.select('.nav-tabs', true).first();
25892          
25893          
25894         var existing = ctr.select('.nav-tab',true);
25895         var qty = existing.getCount();;
25896         
25897         
25898         var tab = ctr.createChild({
25899             tag : 'li',
25900             cls : 'nav-tab' + (qty ? '' : ' active'),
25901             cn : [
25902                 {
25903                     tag : 'a',
25904                     href:'#',
25905                     html : pane.title
25906                 }
25907             ]
25908         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25909         pane.tab = tab;
25910         
25911         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25912         if (!qty) {
25913             pane.el.addClass('active');
25914         }
25915         
25916                 
25917     },
25918     onTabClick : function(ev,un,ob,pane)
25919     {
25920         //Roo.log('tab - prev default');
25921         ev.preventDefault();
25922         
25923         
25924         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25925         pane.tab.addClass('active');
25926         //Roo.log(pane.title);
25927         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25928         // technically we should have a deactivate event.. but maybe add later.
25929         // and it should not de-activate the selected tab...
25930         this.fireEvent('activatepane', pane);
25931         pane.el.addClass('active');
25932         pane.fireEvent('activate');
25933         
25934         
25935     },
25936     
25937     getActivePane : function()
25938     {
25939         var r = false;
25940         Roo.each(this.panes, function(p) {
25941             if(p.el.hasClass('active')){
25942                 r = p;
25943                 return false;
25944             }
25945             
25946             return;
25947         });
25948         
25949         return r;
25950     }
25951     
25952     
25953 });
25954
25955  
25956 /*
25957  * - LGPL
25958  *
25959  * Tab pane
25960  * 
25961  */
25962 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25963 /**
25964  * @class Roo.bootstrap.TabPane
25965  * @extends Roo.bootstrap.Component
25966  * Bootstrap TabPane class
25967  * @cfg {Boolean} active (false | true) Default false
25968  * @cfg {String} title title of panel
25969
25970  * 
25971  * @constructor
25972  * Create a new TabPane
25973  * @param {Object} config The config object
25974  */
25975
25976 Roo.bootstrap.dash.TabPane = function(config){
25977     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25978     
25979     this.addEvents({
25980         // raw events
25981         /**
25982          * @event activate
25983          * When a pane is activated
25984          * @param {Roo.bootstrap.dash.TabPane} pane
25985          */
25986         "activate" : true
25987          
25988     });
25989 };
25990
25991 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25992     
25993     active : false,
25994     title : '',
25995     
25996     // the tabBox that this is attached to.
25997     tab : false,
25998      
25999     getAutoCreate : function() 
26000     {
26001         var cfg = {
26002             tag: 'div',
26003             cls: 'tab-pane'
26004         };
26005         
26006         if(this.active){
26007             cfg.cls += ' active';
26008         }
26009         
26010         return cfg;
26011     },
26012     initEvents  : function()
26013     {
26014         //Roo.log('trigger add pane handler');
26015         this.parent().fireEvent('addpane', this)
26016     },
26017     
26018      /**
26019      * Updates the tab title 
26020      * @param {String} html to set the title to.
26021      */
26022     setTitle: function(str)
26023     {
26024         if (!this.tab) {
26025             return;
26026         }
26027         this.title = str;
26028         this.tab.select('a', true).first().dom.innerHTML = str;
26029         
26030     }
26031     
26032     
26033     
26034 });
26035
26036  
26037
26038
26039  /*
26040  * - LGPL
26041  *
26042  * menu
26043  * 
26044  */
26045 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26046
26047 /**
26048  * @class Roo.bootstrap.menu.Menu
26049  * @extends Roo.bootstrap.Component
26050  * Bootstrap Menu class - container for Menu
26051  * @cfg {String} html Text of the menu
26052  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26053  * @cfg {String} icon Font awesome icon
26054  * @cfg {String} pos Menu align to (top | bottom) default bottom
26055  * 
26056  * 
26057  * @constructor
26058  * Create a new Menu
26059  * @param {Object} config The config object
26060  */
26061
26062
26063 Roo.bootstrap.menu.Menu = function(config){
26064     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26065     
26066     this.addEvents({
26067         /**
26068          * @event beforeshow
26069          * Fires before this menu is displayed
26070          * @param {Roo.bootstrap.menu.Menu} this
26071          */
26072         beforeshow : true,
26073         /**
26074          * @event beforehide
26075          * Fires before this menu is hidden
26076          * @param {Roo.bootstrap.menu.Menu} this
26077          */
26078         beforehide : true,
26079         /**
26080          * @event show
26081          * Fires after this menu is displayed
26082          * @param {Roo.bootstrap.menu.Menu} this
26083          */
26084         show : true,
26085         /**
26086          * @event hide
26087          * Fires after this menu is hidden
26088          * @param {Roo.bootstrap.menu.Menu} this
26089          */
26090         hide : true,
26091         /**
26092          * @event click
26093          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26094          * @param {Roo.bootstrap.menu.Menu} this
26095          * @param {Roo.EventObject} e
26096          */
26097         click : true
26098     });
26099     
26100 };
26101
26102 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26103     
26104     submenu : false,
26105     html : '',
26106     weight : 'default',
26107     icon : false,
26108     pos : 'bottom',
26109     
26110     
26111     getChildContainer : function() {
26112         if(this.isSubMenu){
26113             return this.el;
26114         }
26115         
26116         return this.el.select('ul.dropdown-menu', true).first();  
26117     },
26118     
26119     getAutoCreate : function()
26120     {
26121         var text = [
26122             {
26123                 tag : 'span',
26124                 cls : 'roo-menu-text',
26125                 html : this.html
26126             }
26127         ];
26128         
26129         if(this.icon){
26130             text.unshift({
26131                 tag : 'i',
26132                 cls : 'fa ' + this.icon
26133             })
26134         }
26135         
26136         
26137         var cfg = {
26138             tag : 'div',
26139             cls : 'btn-group',
26140             cn : [
26141                 {
26142                     tag : 'button',
26143                     cls : 'dropdown-button btn btn-' + this.weight,
26144                     cn : text
26145                 },
26146                 {
26147                     tag : 'button',
26148                     cls : 'dropdown-toggle btn btn-' + this.weight,
26149                     cn : [
26150                         {
26151                             tag : 'span',
26152                             cls : 'caret'
26153                         }
26154                     ]
26155                 },
26156                 {
26157                     tag : 'ul',
26158                     cls : 'dropdown-menu'
26159                 }
26160             ]
26161             
26162         };
26163         
26164         if(this.pos == 'top'){
26165             cfg.cls += ' dropup';
26166         }
26167         
26168         if(this.isSubMenu){
26169             cfg = {
26170                 tag : 'ul',
26171                 cls : 'dropdown-menu'
26172             }
26173         }
26174         
26175         return cfg;
26176     },
26177     
26178     onRender : function(ct, position)
26179     {
26180         this.isSubMenu = ct.hasClass('dropdown-submenu');
26181         
26182         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26183     },
26184     
26185     initEvents : function() 
26186     {
26187         if(this.isSubMenu){
26188             return;
26189         }
26190         
26191         this.hidden = true;
26192         
26193         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26194         this.triggerEl.on('click', this.onTriggerPress, this);
26195         
26196         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26197         this.buttonEl.on('click', this.onClick, this);
26198         
26199     },
26200     
26201     list : function()
26202     {
26203         if(this.isSubMenu){
26204             return this.el;
26205         }
26206         
26207         return this.el.select('ul.dropdown-menu', true).first();
26208     },
26209     
26210     onClick : function(e)
26211     {
26212         this.fireEvent("click", this, e);
26213     },
26214     
26215     onTriggerPress  : function(e)
26216     {   
26217         if (this.isVisible()) {
26218             this.hide();
26219         } else {
26220             this.show();
26221         }
26222     },
26223     
26224     isVisible : function(){
26225         return !this.hidden;
26226     },
26227     
26228     show : function()
26229     {
26230         this.fireEvent("beforeshow", this);
26231         
26232         this.hidden = false;
26233         this.el.addClass('open');
26234         
26235         Roo.get(document).on("mouseup", this.onMouseUp, this);
26236         
26237         this.fireEvent("show", this);
26238         
26239         
26240     },
26241     
26242     hide : function()
26243     {
26244         this.fireEvent("beforehide", this);
26245         
26246         this.hidden = true;
26247         this.el.removeClass('open');
26248         
26249         Roo.get(document).un("mouseup", this.onMouseUp);
26250         
26251         this.fireEvent("hide", this);
26252     },
26253     
26254     onMouseUp : function()
26255     {
26256         this.hide();
26257     }
26258     
26259 });
26260
26261  
26262  /*
26263  * - LGPL
26264  *
26265  * menu item
26266  * 
26267  */
26268 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26269
26270 /**
26271  * @class Roo.bootstrap.menu.Item
26272  * @extends Roo.bootstrap.Component
26273  * Bootstrap MenuItem class
26274  * @cfg {Boolean} submenu (true | false) default false
26275  * @cfg {String} html text of the item
26276  * @cfg {String} href the link
26277  * @cfg {Boolean} disable (true | false) default false
26278  * @cfg {Boolean} preventDefault (true | false) default true
26279  * @cfg {String} icon Font awesome icon
26280  * @cfg {String} pos Submenu align to (left | right) default right 
26281  * 
26282  * 
26283  * @constructor
26284  * Create a new Item
26285  * @param {Object} config The config object
26286  */
26287
26288
26289 Roo.bootstrap.menu.Item = function(config){
26290     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26291     this.addEvents({
26292         /**
26293          * @event mouseover
26294          * Fires when the mouse is hovering over this menu
26295          * @param {Roo.bootstrap.menu.Item} this
26296          * @param {Roo.EventObject} e
26297          */
26298         mouseover : true,
26299         /**
26300          * @event mouseout
26301          * Fires when the mouse exits this menu
26302          * @param {Roo.bootstrap.menu.Item} this
26303          * @param {Roo.EventObject} e
26304          */
26305         mouseout : true,
26306         // raw events
26307         /**
26308          * @event click
26309          * The raw click event for the entire grid.
26310          * @param {Roo.EventObject} e
26311          */
26312         click : true
26313     });
26314 };
26315
26316 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26317     
26318     submenu : false,
26319     href : '',
26320     html : '',
26321     preventDefault: true,
26322     disable : false,
26323     icon : false,
26324     pos : 'right',
26325     
26326     getAutoCreate : function()
26327     {
26328         var text = [
26329             {
26330                 tag : 'span',
26331                 cls : 'roo-menu-item-text',
26332                 html : this.html
26333             }
26334         ];
26335         
26336         if(this.icon){
26337             text.unshift({
26338                 tag : 'i',
26339                 cls : 'fa ' + this.icon
26340             })
26341         }
26342         
26343         var cfg = {
26344             tag : 'li',
26345             cn : [
26346                 {
26347                     tag : 'a',
26348                     href : this.href || '#',
26349                     cn : text
26350                 }
26351             ]
26352         };
26353         
26354         if(this.disable){
26355             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26356         }
26357         
26358         if(this.submenu){
26359             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26360             
26361             if(this.pos == 'left'){
26362                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26363             }
26364         }
26365         
26366         return cfg;
26367     },
26368     
26369     initEvents : function() 
26370     {
26371         this.el.on('mouseover', this.onMouseOver, this);
26372         this.el.on('mouseout', this.onMouseOut, this);
26373         
26374         this.el.select('a', true).first().on('click', this.onClick, this);
26375         
26376     },
26377     
26378     onClick : function(e)
26379     {
26380         if(this.preventDefault){
26381             e.preventDefault();
26382         }
26383         
26384         this.fireEvent("click", this, e);
26385     },
26386     
26387     onMouseOver : function(e)
26388     {
26389         if(this.submenu && this.pos == 'left'){
26390             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26391         }
26392         
26393         this.fireEvent("mouseover", this, e);
26394     },
26395     
26396     onMouseOut : function(e)
26397     {
26398         this.fireEvent("mouseout", this, e);
26399     }
26400 });
26401
26402  
26403
26404  /*
26405  * - LGPL
26406  *
26407  * menu separator
26408  * 
26409  */
26410 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26411
26412 /**
26413  * @class Roo.bootstrap.menu.Separator
26414  * @extends Roo.bootstrap.Component
26415  * Bootstrap Separator class
26416  * 
26417  * @constructor
26418  * Create a new Separator
26419  * @param {Object} config The config object
26420  */
26421
26422
26423 Roo.bootstrap.menu.Separator = function(config){
26424     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26425 };
26426
26427 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26428     
26429     getAutoCreate : function(){
26430         var cfg = {
26431             tag : 'li',
26432             cls: 'divider'
26433         };
26434         
26435         return cfg;
26436     }
26437    
26438 });
26439
26440  
26441
26442  /*
26443  * - LGPL
26444  *
26445  * Tooltip
26446  * 
26447  */
26448
26449 /**
26450  * @class Roo.bootstrap.Tooltip
26451  * Bootstrap Tooltip class
26452  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26453  * to determine which dom element triggers the tooltip.
26454  * 
26455  * It needs to add support for additional attributes like tooltip-position
26456  * 
26457  * @constructor
26458  * Create a new Toolti
26459  * @param {Object} config The config object
26460  */
26461
26462 Roo.bootstrap.Tooltip = function(config){
26463     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26464     
26465     this.alignment = Roo.bootstrap.Tooltip.alignment;
26466     
26467     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26468         this.alignment = config.alignment;
26469     }
26470     
26471 };
26472
26473 Roo.apply(Roo.bootstrap.Tooltip, {
26474     /**
26475      * @function init initialize tooltip monitoring.
26476      * @static
26477      */
26478     currentEl : false,
26479     currentTip : false,
26480     currentRegion : false,
26481     
26482     //  init : delay?
26483     
26484     init : function()
26485     {
26486         Roo.get(document).on('mouseover', this.enter ,this);
26487         Roo.get(document).on('mouseout', this.leave, this);
26488          
26489         
26490         this.currentTip = new Roo.bootstrap.Tooltip();
26491     },
26492     
26493     enter : function(ev)
26494     {
26495         var dom = ev.getTarget();
26496         
26497         //Roo.log(['enter',dom]);
26498         var el = Roo.fly(dom);
26499         if (this.currentEl) {
26500             //Roo.log(dom);
26501             //Roo.log(this.currentEl);
26502             //Roo.log(this.currentEl.contains(dom));
26503             if (this.currentEl == el) {
26504                 return;
26505             }
26506             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26507                 return;
26508             }
26509
26510         }
26511         
26512         if (this.currentTip.el) {
26513             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26514         }    
26515         //Roo.log(ev);
26516         
26517         if(!el || el.dom == document){
26518             return;
26519         }
26520         
26521         var bindEl = el;
26522         
26523         // you can not look for children, as if el is the body.. then everythign is the child..
26524         if (!el.attr('tooltip')) { //
26525             if (!el.select("[tooltip]").elements.length) {
26526                 return;
26527             }
26528             // is the mouse over this child...?
26529             bindEl = el.select("[tooltip]").first();
26530             var xy = ev.getXY();
26531             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26532                 //Roo.log("not in region.");
26533                 return;
26534             }
26535             //Roo.log("child element over..");
26536             
26537         }
26538         this.currentEl = bindEl;
26539         this.currentTip.bind(bindEl);
26540         this.currentRegion = Roo.lib.Region.getRegion(dom);
26541         this.currentTip.enter();
26542         
26543     },
26544     leave : function(ev)
26545     {
26546         var dom = ev.getTarget();
26547         //Roo.log(['leave',dom]);
26548         if (!this.currentEl) {
26549             return;
26550         }
26551         
26552         
26553         if (dom != this.currentEl.dom) {
26554             return;
26555         }
26556         var xy = ev.getXY();
26557         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26558             return;
26559         }
26560         // only activate leave if mouse cursor is outside... bounding box..
26561         
26562         
26563         
26564         
26565         if (this.currentTip) {
26566             this.currentTip.leave();
26567         }
26568         //Roo.log('clear currentEl');
26569         this.currentEl = false;
26570         
26571         
26572     },
26573     alignment : {
26574         'left' : ['r-l', [-2,0], 'right'],
26575         'right' : ['l-r', [2,0], 'left'],
26576         'bottom' : ['t-b', [0,2], 'top'],
26577         'top' : [ 'b-t', [0,-2], 'bottom']
26578     }
26579     
26580 });
26581
26582
26583 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26584     
26585     
26586     bindEl : false,
26587     
26588     delay : null, // can be { show : 300 , hide: 500}
26589     
26590     timeout : null,
26591     
26592     hoverState : null, //???
26593     
26594     placement : 'bottom', 
26595     
26596     alignment : false,
26597     
26598     getAutoCreate : function(){
26599     
26600         var cfg = {
26601            cls : 'tooltip',
26602            role : 'tooltip',
26603            cn : [
26604                 {
26605                     cls : 'tooltip-arrow'
26606                 },
26607                 {
26608                     cls : 'tooltip-inner'
26609                 }
26610            ]
26611         };
26612         
26613         return cfg;
26614     },
26615     bind : function(el)
26616     {
26617         this.bindEl = el;
26618     },
26619       
26620     
26621     enter : function () {
26622        
26623         if (this.timeout != null) {
26624             clearTimeout(this.timeout);
26625         }
26626         
26627         this.hoverState = 'in';
26628          //Roo.log("enter - show");
26629         if (!this.delay || !this.delay.show) {
26630             this.show();
26631             return;
26632         }
26633         var _t = this;
26634         this.timeout = setTimeout(function () {
26635             if (_t.hoverState == 'in') {
26636                 _t.show();
26637             }
26638         }, this.delay.show);
26639     },
26640     leave : function()
26641     {
26642         clearTimeout(this.timeout);
26643     
26644         this.hoverState = 'out';
26645          if (!this.delay || !this.delay.hide) {
26646             this.hide();
26647             return;
26648         }
26649        
26650         var _t = this;
26651         this.timeout = setTimeout(function () {
26652             //Roo.log("leave - timeout");
26653             
26654             if (_t.hoverState == 'out') {
26655                 _t.hide();
26656                 Roo.bootstrap.Tooltip.currentEl = false;
26657             }
26658         }, delay);
26659     },
26660     
26661     show : function (msg)
26662     {
26663         if (!this.el) {
26664             this.render(document.body);
26665         }
26666         // set content.
26667         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26668         
26669         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26670         
26671         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26672         
26673         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26674         
26675         var placement = typeof this.placement == 'function' ?
26676             this.placement.call(this, this.el, on_el) :
26677             this.placement;
26678             
26679         var autoToken = /\s?auto?\s?/i;
26680         var autoPlace = autoToken.test(placement);
26681         if (autoPlace) {
26682             placement = placement.replace(autoToken, '') || 'top';
26683         }
26684         
26685         //this.el.detach()
26686         //this.el.setXY([0,0]);
26687         this.el.show();
26688         //this.el.dom.style.display='block';
26689         
26690         //this.el.appendTo(on_el);
26691         
26692         var p = this.getPosition();
26693         var box = this.el.getBox();
26694         
26695         if (autoPlace) {
26696             // fixme..
26697         }
26698         
26699         var align = this.alignment[placement];
26700         
26701         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26702         
26703         if(placement == 'top' || placement == 'bottom'){
26704             if(xy[0] < 0){
26705                 placement = 'right';
26706             }
26707             
26708             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26709                 placement = 'left';
26710             }
26711             
26712             var scroll = Roo.select('body', true).first().getScroll();
26713             
26714             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26715                 placement = 'top';
26716             }
26717             
26718             align = this.alignment[placement];
26719         }
26720         
26721         this.el.alignTo(this.bindEl, align[0],align[1]);
26722         //var arrow = this.el.select('.arrow',true).first();
26723         //arrow.set(align[2], 
26724         
26725         this.el.addClass(placement);
26726         
26727         this.el.addClass('in fade');
26728         
26729         this.hoverState = null;
26730         
26731         if (this.el.hasClass('fade')) {
26732             // fade it?
26733         }
26734         
26735     },
26736     hide : function()
26737     {
26738          
26739         if (!this.el) {
26740             return;
26741         }
26742         //this.el.setXY([0,0]);
26743         this.el.removeClass('in');
26744         //this.el.hide();
26745         
26746     }
26747     
26748 });
26749  
26750
26751  /*
26752  * - LGPL
26753  *
26754  * Location Picker
26755  * 
26756  */
26757
26758 /**
26759  * @class Roo.bootstrap.LocationPicker
26760  * @extends Roo.bootstrap.Component
26761  * Bootstrap LocationPicker class
26762  * @cfg {Number} latitude Position when init default 0
26763  * @cfg {Number} longitude Position when init default 0
26764  * @cfg {Number} zoom default 15
26765  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26766  * @cfg {Boolean} mapTypeControl default false
26767  * @cfg {Boolean} disableDoubleClickZoom default false
26768  * @cfg {Boolean} scrollwheel default true
26769  * @cfg {Boolean} streetViewControl default false
26770  * @cfg {Number} radius default 0
26771  * @cfg {String} locationName
26772  * @cfg {Boolean} draggable default true
26773  * @cfg {Boolean} enableAutocomplete default false
26774  * @cfg {Boolean} enableReverseGeocode default true
26775  * @cfg {String} markerTitle
26776  * 
26777  * @constructor
26778  * Create a new LocationPicker
26779  * @param {Object} config The config object
26780  */
26781
26782
26783 Roo.bootstrap.LocationPicker = function(config){
26784     
26785     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26786     
26787     this.addEvents({
26788         /**
26789          * @event initial
26790          * Fires when the picker initialized.
26791          * @param {Roo.bootstrap.LocationPicker} this
26792          * @param {Google Location} location
26793          */
26794         initial : true,
26795         /**
26796          * @event positionchanged
26797          * Fires when the picker position changed.
26798          * @param {Roo.bootstrap.LocationPicker} this
26799          * @param {Google Location} location
26800          */
26801         positionchanged : true,
26802         /**
26803          * @event resize
26804          * Fires when the map resize.
26805          * @param {Roo.bootstrap.LocationPicker} this
26806          */
26807         resize : true,
26808         /**
26809          * @event show
26810          * Fires when the map show.
26811          * @param {Roo.bootstrap.LocationPicker} this
26812          */
26813         show : true,
26814         /**
26815          * @event hide
26816          * Fires when the map hide.
26817          * @param {Roo.bootstrap.LocationPicker} this
26818          */
26819         hide : true,
26820         /**
26821          * @event mapClick
26822          * Fires when click the map.
26823          * @param {Roo.bootstrap.LocationPicker} this
26824          * @param {Map event} e
26825          */
26826         mapClick : true,
26827         /**
26828          * @event mapRightClick
26829          * Fires when right click the map.
26830          * @param {Roo.bootstrap.LocationPicker} this
26831          * @param {Map event} e
26832          */
26833         mapRightClick : true,
26834         /**
26835          * @event markerClick
26836          * Fires when click the marker.
26837          * @param {Roo.bootstrap.LocationPicker} this
26838          * @param {Map event} e
26839          */
26840         markerClick : true,
26841         /**
26842          * @event markerRightClick
26843          * Fires when right click the marker.
26844          * @param {Roo.bootstrap.LocationPicker} this
26845          * @param {Map event} e
26846          */
26847         markerRightClick : true,
26848         /**
26849          * @event OverlayViewDraw
26850          * Fires when OverlayView Draw
26851          * @param {Roo.bootstrap.LocationPicker} this
26852          */
26853         OverlayViewDraw : true,
26854         /**
26855          * @event OverlayViewOnAdd
26856          * Fires when OverlayView Draw
26857          * @param {Roo.bootstrap.LocationPicker} this
26858          */
26859         OverlayViewOnAdd : true,
26860         /**
26861          * @event OverlayViewOnRemove
26862          * Fires when OverlayView Draw
26863          * @param {Roo.bootstrap.LocationPicker} this
26864          */
26865         OverlayViewOnRemove : true,
26866         /**
26867          * @event OverlayViewShow
26868          * Fires when OverlayView Draw
26869          * @param {Roo.bootstrap.LocationPicker} this
26870          * @param {Pixel} cpx
26871          */
26872         OverlayViewShow : true,
26873         /**
26874          * @event OverlayViewHide
26875          * Fires when OverlayView Draw
26876          * @param {Roo.bootstrap.LocationPicker} this
26877          */
26878         OverlayViewHide : true,
26879         /**
26880          * @event loadexception
26881          * Fires when load google lib failed.
26882          * @param {Roo.bootstrap.LocationPicker} this
26883          */
26884         loadexception : true
26885     });
26886         
26887 };
26888
26889 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26890     
26891     gMapContext: false,
26892     
26893     latitude: 0,
26894     longitude: 0,
26895     zoom: 15,
26896     mapTypeId: false,
26897     mapTypeControl: false,
26898     disableDoubleClickZoom: false,
26899     scrollwheel: true,
26900     streetViewControl: false,
26901     radius: 0,
26902     locationName: '',
26903     draggable: true,
26904     enableAutocomplete: false,
26905     enableReverseGeocode: true,
26906     markerTitle: '',
26907     
26908     getAutoCreate: function()
26909     {
26910
26911         var cfg = {
26912             tag: 'div',
26913             cls: 'roo-location-picker'
26914         };
26915         
26916         return cfg
26917     },
26918     
26919     initEvents: function(ct, position)
26920     {       
26921         if(!this.el.getWidth() || this.isApplied()){
26922             return;
26923         }
26924         
26925         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26926         
26927         this.initial();
26928     },
26929     
26930     initial: function()
26931     {
26932         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26933             this.fireEvent('loadexception', this);
26934             return;
26935         }
26936         
26937         if(!this.mapTypeId){
26938             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26939         }
26940         
26941         this.gMapContext = this.GMapContext();
26942         
26943         this.initOverlayView();
26944         
26945         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26946         
26947         var _this = this;
26948                 
26949         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26950             _this.setPosition(_this.gMapContext.marker.position);
26951         });
26952         
26953         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26954             _this.fireEvent('mapClick', this, event);
26955             
26956         });
26957
26958         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26959             _this.fireEvent('mapRightClick', this, event);
26960             
26961         });
26962         
26963         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26964             _this.fireEvent('markerClick', this, event);
26965             
26966         });
26967
26968         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26969             _this.fireEvent('markerRightClick', this, event);
26970             
26971         });
26972         
26973         this.setPosition(this.gMapContext.location);
26974         
26975         this.fireEvent('initial', this, this.gMapContext.location);
26976     },
26977     
26978     initOverlayView: function()
26979     {
26980         var _this = this;
26981         
26982         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26983             
26984             draw: function()
26985             {
26986                 _this.fireEvent('OverlayViewDraw', _this);
26987             },
26988             
26989             onAdd: function()
26990             {
26991                 _this.fireEvent('OverlayViewOnAdd', _this);
26992             },
26993             
26994             onRemove: function()
26995             {
26996                 _this.fireEvent('OverlayViewOnRemove', _this);
26997             },
26998             
26999             show: function(cpx)
27000             {
27001                 _this.fireEvent('OverlayViewShow', _this, cpx);
27002             },
27003             
27004             hide: function()
27005             {
27006                 _this.fireEvent('OverlayViewHide', _this);
27007             }
27008             
27009         });
27010     },
27011     
27012     fromLatLngToContainerPixel: function(event)
27013     {
27014         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27015     },
27016     
27017     isApplied: function() 
27018     {
27019         return this.getGmapContext() == false ? false : true;
27020     },
27021     
27022     getGmapContext: function() 
27023     {
27024         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27025     },
27026     
27027     GMapContext: function() 
27028     {
27029         var position = new google.maps.LatLng(this.latitude, this.longitude);
27030         
27031         var _map = new google.maps.Map(this.el.dom, {
27032             center: position,
27033             zoom: this.zoom,
27034             mapTypeId: this.mapTypeId,
27035             mapTypeControl: this.mapTypeControl,
27036             disableDoubleClickZoom: this.disableDoubleClickZoom,
27037             scrollwheel: this.scrollwheel,
27038             streetViewControl: this.streetViewControl,
27039             locationName: this.locationName,
27040             draggable: this.draggable,
27041             enableAutocomplete: this.enableAutocomplete,
27042             enableReverseGeocode: this.enableReverseGeocode
27043         });
27044         
27045         var _marker = new google.maps.Marker({
27046             position: position,
27047             map: _map,
27048             title: this.markerTitle,
27049             draggable: this.draggable
27050         });
27051         
27052         return {
27053             map: _map,
27054             marker: _marker,
27055             circle: null,
27056             location: position,
27057             radius: this.radius,
27058             locationName: this.locationName,
27059             addressComponents: {
27060                 formatted_address: null,
27061                 addressLine1: null,
27062                 addressLine2: null,
27063                 streetName: null,
27064                 streetNumber: null,
27065                 city: null,
27066                 district: null,
27067                 state: null,
27068                 stateOrProvince: null
27069             },
27070             settings: this,
27071             domContainer: this.el.dom,
27072             geodecoder: new google.maps.Geocoder()
27073         };
27074     },
27075     
27076     drawCircle: function(center, radius, options) 
27077     {
27078         if (this.gMapContext.circle != null) {
27079             this.gMapContext.circle.setMap(null);
27080         }
27081         if (radius > 0) {
27082             radius *= 1;
27083             options = Roo.apply({}, options, {
27084                 strokeColor: "#0000FF",
27085                 strokeOpacity: .35,
27086                 strokeWeight: 2,
27087                 fillColor: "#0000FF",
27088                 fillOpacity: .2
27089             });
27090             
27091             options.map = this.gMapContext.map;
27092             options.radius = radius;
27093             options.center = center;
27094             this.gMapContext.circle = new google.maps.Circle(options);
27095             return this.gMapContext.circle;
27096         }
27097         
27098         return null;
27099     },
27100     
27101     setPosition: function(location) 
27102     {
27103         this.gMapContext.location = location;
27104         this.gMapContext.marker.setPosition(location);
27105         this.gMapContext.map.panTo(location);
27106         this.drawCircle(location, this.gMapContext.radius, {});
27107         
27108         var _this = this;
27109         
27110         if (this.gMapContext.settings.enableReverseGeocode) {
27111             this.gMapContext.geodecoder.geocode({
27112                 latLng: this.gMapContext.location
27113             }, function(results, status) {
27114                 
27115                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27116                     _this.gMapContext.locationName = results[0].formatted_address;
27117                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27118                     
27119                     _this.fireEvent('positionchanged', this, location);
27120                 }
27121             });
27122             
27123             return;
27124         }
27125         
27126         this.fireEvent('positionchanged', this, location);
27127     },
27128     
27129     resize: function()
27130     {
27131         google.maps.event.trigger(this.gMapContext.map, "resize");
27132         
27133         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27134         
27135         this.fireEvent('resize', this);
27136     },
27137     
27138     setPositionByLatLng: function(latitude, longitude)
27139     {
27140         this.setPosition(new google.maps.LatLng(latitude, longitude));
27141     },
27142     
27143     getCurrentPosition: function() 
27144     {
27145         return {
27146             latitude: this.gMapContext.location.lat(),
27147             longitude: this.gMapContext.location.lng()
27148         };
27149     },
27150     
27151     getAddressName: function() 
27152     {
27153         return this.gMapContext.locationName;
27154     },
27155     
27156     getAddressComponents: function() 
27157     {
27158         return this.gMapContext.addressComponents;
27159     },
27160     
27161     address_component_from_google_geocode: function(address_components) 
27162     {
27163         var result = {};
27164         
27165         for (var i = 0; i < address_components.length; i++) {
27166             var component = address_components[i];
27167             if (component.types.indexOf("postal_code") >= 0) {
27168                 result.postalCode = component.short_name;
27169             } else if (component.types.indexOf("street_number") >= 0) {
27170                 result.streetNumber = component.short_name;
27171             } else if (component.types.indexOf("route") >= 0) {
27172                 result.streetName = component.short_name;
27173             } else if (component.types.indexOf("neighborhood") >= 0) {
27174                 result.city = component.short_name;
27175             } else if (component.types.indexOf("locality") >= 0) {
27176                 result.city = component.short_name;
27177             } else if (component.types.indexOf("sublocality") >= 0) {
27178                 result.district = component.short_name;
27179             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27180                 result.stateOrProvince = component.short_name;
27181             } else if (component.types.indexOf("country") >= 0) {
27182                 result.country = component.short_name;
27183             }
27184         }
27185         
27186         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27187         result.addressLine2 = "";
27188         return result;
27189     },
27190     
27191     setZoomLevel: function(zoom)
27192     {
27193         this.gMapContext.map.setZoom(zoom);
27194     },
27195     
27196     show: function()
27197     {
27198         if(!this.el){
27199             return;
27200         }
27201         
27202         this.el.show();
27203         
27204         this.resize();
27205         
27206         this.fireEvent('show', this);
27207     },
27208     
27209     hide: function()
27210     {
27211         if(!this.el){
27212             return;
27213         }
27214         
27215         this.el.hide();
27216         
27217         this.fireEvent('hide', this);
27218     }
27219     
27220 });
27221
27222 Roo.apply(Roo.bootstrap.LocationPicker, {
27223     
27224     OverlayView : function(map, options)
27225     {
27226         options = options || {};
27227         
27228         this.setMap(map);
27229     }
27230     
27231     
27232 });/**
27233  * @class Roo.bootstrap.Alert
27234  * @extends Roo.bootstrap.Component
27235  * Bootstrap Alert class - shows an alert area box
27236  * eg
27237  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27238   Enter a valid email address
27239 </div>
27240  * @licence LGPL
27241  * @cfg {String} title The title of alert
27242  * @cfg {String} html The content of alert
27243  * @cfg {String} weight (  success | info | warning | danger )
27244  * @cfg {String} faicon font-awesomeicon
27245  * 
27246  * @constructor
27247  * Create a new alert
27248  * @param {Object} config The config object
27249  */
27250
27251
27252 Roo.bootstrap.Alert = function(config){
27253     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27254     
27255 };
27256
27257 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27258     
27259     title: '',
27260     html: '',
27261     weight: false,
27262     faicon: false,
27263     
27264     getAutoCreate : function()
27265     {
27266         
27267         var cfg = {
27268             tag : 'div',
27269             cls : 'alert',
27270             cn : [
27271                 {
27272                     tag : 'i',
27273                     cls : 'roo-alert-icon'
27274                     
27275                 },
27276                 {
27277                     tag : 'b',
27278                     cls : 'roo-alert-title',
27279                     html : this.title
27280                 },
27281                 {
27282                     tag : 'span',
27283                     cls : 'roo-alert-text',
27284                     html : this.html
27285                 }
27286             ]
27287         };
27288         
27289         if(this.faicon){
27290             cfg.cn[0].cls += ' fa ' + this.faicon;
27291         }
27292         
27293         if(this.weight){
27294             cfg.cls += ' alert-' + this.weight;
27295         }
27296         
27297         return cfg;
27298     },
27299     
27300     initEvents: function() 
27301     {
27302         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27303     },
27304     
27305     setTitle : function(str)
27306     {
27307         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27308     },
27309     
27310     setText : function(str)
27311     {
27312         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27313     },
27314     
27315     setWeight : function(weight)
27316     {
27317         if(this.weight){
27318             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27319         }
27320         
27321         this.weight = weight;
27322         
27323         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27324     },
27325     
27326     setIcon : function(icon)
27327     {
27328         if(this.faicon){
27329             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27330         }
27331         
27332         this.faicon = icon;
27333         
27334         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27335     },
27336     
27337     hide: function() 
27338     {
27339         this.el.hide();   
27340     },
27341     
27342     show: function() 
27343     {  
27344         this.el.show();   
27345     }
27346     
27347 });
27348
27349  
27350 /*
27351 * Licence: LGPL
27352 */
27353
27354 /**
27355  * @class Roo.bootstrap.UploadCropbox
27356  * @extends Roo.bootstrap.Component
27357  * Bootstrap UploadCropbox class
27358  * @cfg {String} emptyText show when image has been loaded
27359  * @cfg {String} rotateNotify show when image too small to rotate
27360  * @cfg {Number} errorTimeout default 3000
27361  * @cfg {Number} minWidth default 300
27362  * @cfg {Number} minHeight default 300
27363  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27364  * @cfg {Boolean} isDocument (true|false) default false
27365  * @cfg {String} url action url
27366  * @cfg {String} paramName default 'imageUpload'
27367  * @cfg {String} method default POST
27368  * @cfg {Boolean} loadMask (true|false) default true
27369  * @cfg {Boolean} loadingText default 'Loading...'
27370  * 
27371  * @constructor
27372  * Create a new UploadCropbox
27373  * @param {Object} config The config object
27374  */
27375
27376 Roo.bootstrap.UploadCropbox = function(config){
27377     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27378     
27379     this.addEvents({
27380         /**
27381          * @event beforeselectfile
27382          * Fire before select file
27383          * @param {Roo.bootstrap.UploadCropbox} this
27384          */
27385         "beforeselectfile" : true,
27386         /**
27387          * @event initial
27388          * Fire after initEvent
27389          * @param {Roo.bootstrap.UploadCropbox} this
27390          */
27391         "initial" : true,
27392         /**
27393          * @event crop
27394          * Fire after initEvent
27395          * @param {Roo.bootstrap.UploadCropbox} this
27396          * @param {String} data
27397          */
27398         "crop" : true,
27399         /**
27400          * @event prepare
27401          * Fire when preparing the file data
27402          * @param {Roo.bootstrap.UploadCropbox} this
27403          * @param {Object} file
27404          */
27405         "prepare" : true,
27406         /**
27407          * @event exception
27408          * Fire when get exception
27409          * @param {Roo.bootstrap.UploadCropbox} this
27410          * @param {XMLHttpRequest} xhr
27411          */
27412         "exception" : true,
27413         /**
27414          * @event beforeloadcanvas
27415          * Fire before load the canvas
27416          * @param {Roo.bootstrap.UploadCropbox} this
27417          * @param {String} src
27418          */
27419         "beforeloadcanvas" : true,
27420         /**
27421          * @event trash
27422          * Fire when trash image
27423          * @param {Roo.bootstrap.UploadCropbox} this
27424          */
27425         "trash" : true,
27426         /**
27427          * @event download
27428          * Fire when download the image
27429          * @param {Roo.bootstrap.UploadCropbox} this
27430          */
27431         "download" : true,
27432         /**
27433          * @event footerbuttonclick
27434          * Fire when footerbuttonclick
27435          * @param {Roo.bootstrap.UploadCropbox} this
27436          * @param {String} type
27437          */
27438         "footerbuttonclick" : true,
27439         /**
27440          * @event resize
27441          * Fire when resize
27442          * @param {Roo.bootstrap.UploadCropbox} this
27443          */
27444         "resize" : true,
27445         /**
27446          * @event rotate
27447          * Fire when rotate the image
27448          * @param {Roo.bootstrap.UploadCropbox} this
27449          * @param {String} pos
27450          */
27451         "rotate" : true,
27452         /**
27453          * @event inspect
27454          * Fire when inspect the file
27455          * @param {Roo.bootstrap.UploadCropbox} this
27456          * @param {Object} file
27457          */
27458         "inspect" : true,
27459         /**
27460          * @event upload
27461          * Fire when xhr upload the file
27462          * @param {Roo.bootstrap.UploadCropbox} this
27463          * @param {Object} data
27464          */
27465         "upload" : true,
27466         /**
27467          * @event arrange
27468          * Fire when arrange the file data
27469          * @param {Roo.bootstrap.UploadCropbox} this
27470          * @param {Object} formData
27471          */
27472         "arrange" : true
27473     });
27474     
27475     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27476 };
27477
27478 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27479     
27480     emptyText : 'Click to upload image',
27481     rotateNotify : 'Image is too small to rotate',
27482     errorTimeout : 3000,
27483     scale : 0,
27484     baseScale : 1,
27485     rotate : 0,
27486     dragable : false,
27487     pinching : false,
27488     mouseX : 0,
27489     mouseY : 0,
27490     cropData : false,
27491     minWidth : 300,
27492     minHeight : 300,
27493     file : false,
27494     exif : {},
27495     baseRotate : 1,
27496     cropType : 'image/jpeg',
27497     buttons : false,
27498     canvasLoaded : false,
27499     isDocument : false,
27500     method : 'POST',
27501     paramName : 'imageUpload',
27502     loadMask : true,
27503     loadingText : 'Loading...',
27504     maskEl : false,
27505     
27506     getAutoCreate : function()
27507     {
27508         var cfg = {
27509             tag : 'div',
27510             cls : 'roo-upload-cropbox',
27511             cn : [
27512                 {
27513                     tag : 'input',
27514                     cls : 'roo-upload-cropbox-selector',
27515                     type : 'file'
27516                 },
27517                 {
27518                     tag : 'div',
27519                     cls : 'roo-upload-cropbox-body',
27520                     style : 'cursor:pointer',
27521                     cn : [
27522                         {
27523                             tag : 'div',
27524                             cls : 'roo-upload-cropbox-preview'
27525                         },
27526                         {
27527                             tag : 'div',
27528                             cls : 'roo-upload-cropbox-thumb'
27529                         },
27530                         {
27531                             tag : 'div',
27532                             cls : 'roo-upload-cropbox-empty-notify',
27533                             html : this.emptyText
27534                         },
27535                         {
27536                             tag : 'div',
27537                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27538                             html : this.rotateNotify
27539                         }
27540                     ]
27541                 },
27542                 {
27543                     tag : 'div',
27544                     cls : 'roo-upload-cropbox-footer',
27545                     cn : {
27546                         tag : 'div',
27547                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27548                         cn : []
27549                     }
27550                 }
27551             ]
27552         };
27553         
27554         return cfg;
27555     },
27556     
27557     onRender : function(ct, position)
27558     {
27559         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27560         
27561         if (this.buttons.length) {
27562             
27563             Roo.each(this.buttons, function(bb) {
27564                 
27565                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27566                 
27567                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27568                 
27569             }, this);
27570         }
27571         
27572         if(this.loadMask){
27573             this.maskEl = this.el;
27574         }
27575     },
27576     
27577     initEvents : function()
27578     {
27579         this.urlAPI = (window.createObjectURL && window) || 
27580                                 (window.URL && URL.revokeObjectURL && URL) || 
27581                                 (window.webkitURL && webkitURL);
27582                         
27583         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27584         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27585         
27586         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27587         this.selectorEl.hide();
27588         
27589         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27590         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27591         
27592         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27593         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27594         this.thumbEl.hide();
27595         
27596         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27597         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27598         
27599         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27600         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27601         this.errorEl.hide();
27602         
27603         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27604         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27605         this.footerEl.hide();
27606         
27607         this.setThumbBoxSize();
27608         
27609         this.bind();
27610         
27611         this.resize();
27612         
27613         this.fireEvent('initial', this);
27614     },
27615
27616     bind : function()
27617     {
27618         var _this = this;
27619         
27620         window.addEventListener("resize", function() { _this.resize(); } );
27621         
27622         this.bodyEl.on('click', this.beforeSelectFile, this);
27623         
27624         if(Roo.isTouch){
27625             this.bodyEl.on('touchstart', this.onTouchStart, this);
27626             this.bodyEl.on('touchmove', this.onTouchMove, this);
27627             this.bodyEl.on('touchend', this.onTouchEnd, this);
27628         }
27629         
27630         if(!Roo.isTouch){
27631             this.bodyEl.on('mousedown', this.onMouseDown, this);
27632             this.bodyEl.on('mousemove', this.onMouseMove, this);
27633             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27634             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27635             Roo.get(document).on('mouseup', this.onMouseUp, this);
27636         }
27637         
27638         this.selectorEl.on('change', this.onFileSelected, this);
27639     },
27640     
27641     reset : function()
27642     {    
27643         this.scale = 0;
27644         this.baseScale = 1;
27645         this.rotate = 0;
27646         this.baseRotate = 1;
27647         this.dragable = false;
27648         this.pinching = false;
27649         this.mouseX = 0;
27650         this.mouseY = 0;
27651         this.cropData = false;
27652         this.notifyEl.dom.innerHTML = this.emptyText;
27653         
27654         this.selectorEl.dom.value = '';
27655         
27656     },
27657     
27658     resize : function()
27659     {
27660         if(this.fireEvent('resize', this) != false){
27661             this.setThumbBoxPosition();
27662             this.setCanvasPosition();
27663         }
27664     },
27665     
27666     onFooterButtonClick : function(e, el, o, type)
27667     {
27668         switch (type) {
27669             case 'rotate-left' :
27670                 this.onRotateLeft(e);
27671                 break;
27672             case 'rotate-right' :
27673                 this.onRotateRight(e);
27674                 break;
27675             case 'picture' :
27676                 this.beforeSelectFile(e);
27677                 break;
27678             case 'trash' :
27679                 this.trash(e);
27680                 break;
27681             case 'crop' :
27682                 this.crop(e);
27683                 break;
27684             case 'download' :
27685                 this.download(e);
27686                 break;
27687             default :
27688                 break;
27689         }
27690         
27691         this.fireEvent('footerbuttonclick', this, type);
27692     },
27693     
27694     beforeSelectFile : function(e)
27695     {
27696         e.preventDefault();
27697         
27698         if(this.fireEvent('beforeselectfile', this) != false){
27699             this.selectorEl.dom.click();
27700         }
27701     },
27702     
27703     onFileSelected : function(e)
27704     {
27705         e.preventDefault();
27706         
27707         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27708             return;
27709         }
27710         
27711         var file = this.selectorEl.dom.files[0];
27712         
27713         if(this.fireEvent('inspect', this, file) != false){
27714             this.prepare(file);
27715         }
27716         
27717     },
27718     
27719     trash : function(e)
27720     {
27721         this.fireEvent('trash', this);
27722     },
27723     
27724     download : function(e)
27725     {
27726         this.fireEvent('download', this);
27727     },
27728     
27729     loadCanvas : function(src)
27730     {   
27731         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27732             
27733             this.reset();
27734             
27735             this.imageEl = document.createElement('img');
27736             
27737             var _this = this;
27738             
27739             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27740             
27741             this.imageEl.src = src;
27742         }
27743     },
27744     
27745     onLoadCanvas : function()
27746     {   
27747         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27748         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27749         
27750         this.bodyEl.un('click', this.beforeSelectFile, this);
27751         
27752         this.notifyEl.hide();
27753         this.thumbEl.show();
27754         this.footerEl.show();
27755         
27756         this.baseRotateLevel();
27757         
27758         if(this.isDocument){
27759             this.setThumbBoxSize();
27760         }
27761         
27762         this.setThumbBoxPosition();
27763         
27764         this.baseScaleLevel();
27765         
27766         this.draw();
27767         
27768         this.resize();
27769         
27770         this.canvasLoaded = true;
27771         
27772         if(this.loadMask){
27773             this.maskEl.unmask();
27774         }
27775         
27776     },
27777     
27778     setCanvasPosition : function()
27779     {   
27780         if(!this.canvasEl){
27781             return;
27782         }
27783         
27784         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27785         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27786         
27787         this.previewEl.setLeft(pw);
27788         this.previewEl.setTop(ph);
27789         
27790     },
27791     
27792     onMouseDown : function(e)
27793     {   
27794         e.stopEvent();
27795         
27796         this.dragable = true;
27797         this.pinching = false;
27798         
27799         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27800             this.dragable = false;
27801             return;
27802         }
27803         
27804         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27805         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27806         
27807     },
27808     
27809     onMouseMove : function(e)
27810     {   
27811         e.stopEvent();
27812         
27813         if(!this.canvasLoaded){
27814             return;
27815         }
27816         
27817         if (!this.dragable){
27818             return;
27819         }
27820         
27821         var minX = Math.ceil(this.thumbEl.getLeft(true));
27822         var minY = Math.ceil(this.thumbEl.getTop(true));
27823         
27824         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27825         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27826         
27827         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27828         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27829         
27830         x = x - this.mouseX;
27831         y = y - this.mouseY;
27832         
27833         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27834         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27835         
27836         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27837         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27838         
27839         this.previewEl.setLeft(bgX);
27840         this.previewEl.setTop(bgY);
27841         
27842         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27843         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27844     },
27845     
27846     onMouseUp : function(e)
27847     {   
27848         e.stopEvent();
27849         
27850         this.dragable = false;
27851     },
27852     
27853     onMouseWheel : function(e)
27854     {   
27855         e.stopEvent();
27856         
27857         this.startScale = this.scale;
27858         
27859         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27860         
27861         if(!this.zoomable()){
27862             this.scale = this.startScale;
27863             return;
27864         }
27865         
27866         this.draw();
27867         
27868         return;
27869     },
27870     
27871     zoomable : function()
27872     {
27873         var minScale = this.thumbEl.getWidth() / this.minWidth;
27874         
27875         if(this.minWidth < this.minHeight){
27876             minScale = this.thumbEl.getHeight() / this.minHeight;
27877         }
27878         
27879         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27880         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27881         
27882         if(
27883                 this.isDocument &&
27884                 (this.rotate == 0 || this.rotate == 180) && 
27885                 (
27886                     width > this.imageEl.OriginWidth || 
27887                     height > this.imageEl.OriginHeight ||
27888                     (width < this.minWidth && height < this.minHeight)
27889                 )
27890         ){
27891             return false;
27892         }
27893         
27894         if(
27895                 this.isDocument &&
27896                 (this.rotate == 90 || this.rotate == 270) && 
27897                 (
27898                     width > this.imageEl.OriginWidth || 
27899                     height > this.imageEl.OriginHeight ||
27900                     (width < this.minHeight && height < this.minWidth)
27901                 )
27902         ){
27903             return false;
27904         }
27905         
27906         if(
27907                 !this.isDocument &&
27908                 (this.rotate == 0 || this.rotate == 180) && 
27909                 (
27910                     width < this.minWidth || 
27911                     width > this.imageEl.OriginWidth || 
27912                     height < this.minHeight || 
27913                     height > this.imageEl.OriginHeight
27914                 )
27915         ){
27916             return false;
27917         }
27918         
27919         if(
27920                 !this.isDocument &&
27921                 (this.rotate == 90 || this.rotate == 270) && 
27922                 (
27923                     width < this.minHeight || 
27924                     width > this.imageEl.OriginWidth || 
27925                     height < this.minWidth || 
27926                     height > this.imageEl.OriginHeight
27927                 )
27928         ){
27929             return false;
27930         }
27931         
27932         return true;
27933         
27934     },
27935     
27936     onRotateLeft : function(e)
27937     {   
27938         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27939             
27940             var minScale = this.thumbEl.getWidth() / this.minWidth;
27941             
27942             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27943             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27944             
27945             this.startScale = this.scale;
27946             
27947             while (this.getScaleLevel() < minScale){
27948             
27949                 this.scale = this.scale + 1;
27950                 
27951                 if(!this.zoomable()){
27952                     break;
27953                 }
27954                 
27955                 if(
27956                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27957                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27958                 ){
27959                     continue;
27960                 }
27961                 
27962                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27963
27964                 this.draw();
27965                 
27966                 return;
27967             }
27968             
27969             this.scale = this.startScale;
27970             
27971             this.onRotateFail();
27972             
27973             return false;
27974         }
27975         
27976         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27977
27978         if(this.isDocument){
27979             this.setThumbBoxSize();
27980             this.setThumbBoxPosition();
27981             this.setCanvasPosition();
27982         }
27983         
27984         this.draw();
27985         
27986         this.fireEvent('rotate', this, 'left');
27987         
27988     },
27989     
27990     onRotateRight : function(e)
27991     {
27992         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27993             
27994             var minScale = this.thumbEl.getWidth() / this.minWidth;
27995         
27996             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27997             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27998             
27999             this.startScale = this.scale;
28000             
28001             while (this.getScaleLevel() < minScale){
28002             
28003                 this.scale = this.scale + 1;
28004                 
28005                 if(!this.zoomable()){
28006                     break;
28007                 }
28008                 
28009                 if(
28010                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28011                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28012                 ){
28013                     continue;
28014                 }
28015                 
28016                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28017
28018                 this.draw();
28019                 
28020                 return;
28021             }
28022             
28023             this.scale = this.startScale;
28024             
28025             this.onRotateFail();
28026             
28027             return false;
28028         }
28029         
28030         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28031
28032         if(this.isDocument){
28033             this.setThumbBoxSize();
28034             this.setThumbBoxPosition();
28035             this.setCanvasPosition();
28036         }
28037         
28038         this.draw();
28039         
28040         this.fireEvent('rotate', this, 'right');
28041     },
28042     
28043     onRotateFail : function()
28044     {
28045         this.errorEl.show(true);
28046         
28047         var _this = this;
28048         
28049         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28050     },
28051     
28052     draw : function()
28053     {
28054         this.previewEl.dom.innerHTML = '';
28055         
28056         var canvasEl = document.createElement("canvas");
28057         
28058         var contextEl = canvasEl.getContext("2d");
28059         
28060         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28061         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28062         var center = this.imageEl.OriginWidth / 2;
28063         
28064         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28065             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28066             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28067             center = this.imageEl.OriginHeight / 2;
28068         }
28069         
28070         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28071         
28072         contextEl.translate(center, center);
28073         contextEl.rotate(this.rotate * Math.PI / 180);
28074
28075         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28076         
28077         this.canvasEl = document.createElement("canvas");
28078         
28079         this.contextEl = this.canvasEl.getContext("2d");
28080         
28081         switch (this.rotate) {
28082             case 0 :
28083                 
28084                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28085                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28086                 
28087                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28088                 
28089                 break;
28090             case 90 : 
28091                 
28092                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28093                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28094                 
28095                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28096                     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);
28097                     break;
28098                 }
28099                 
28100                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28101                 
28102                 break;
28103             case 180 :
28104                 
28105                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28106                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28107                 
28108                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28109                     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);
28110                     break;
28111                 }
28112                 
28113                 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);
28114                 
28115                 break;
28116             case 270 :
28117                 
28118                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28119                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28120         
28121                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28122                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28123                     break;
28124                 }
28125                 
28126                 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);
28127                 
28128                 break;
28129             default : 
28130                 break;
28131         }
28132         
28133         this.previewEl.appendChild(this.canvasEl);
28134         
28135         this.setCanvasPosition();
28136     },
28137     
28138     crop : function()
28139     {
28140         if(!this.canvasLoaded){
28141             return;
28142         }
28143         
28144         var imageCanvas = document.createElement("canvas");
28145         
28146         var imageContext = imageCanvas.getContext("2d");
28147         
28148         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28149         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28150         
28151         var center = imageCanvas.width / 2;
28152         
28153         imageContext.translate(center, center);
28154         
28155         imageContext.rotate(this.rotate * Math.PI / 180);
28156         
28157         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28158         
28159         var canvas = document.createElement("canvas");
28160         
28161         var context = canvas.getContext("2d");
28162                 
28163         canvas.width = this.minWidth;
28164         canvas.height = this.minHeight;
28165
28166         switch (this.rotate) {
28167             case 0 :
28168                 
28169                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28170                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28171                 
28172                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28173                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28174                 
28175                 var targetWidth = this.minWidth - 2 * x;
28176                 var targetHeight = this.minHeight - 2 * y;
28177                 
28178                 var scale = 1;
28179                 
28180                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28181                     scale = targetWidth / width;
28182                 }
28183                 
28184                 if(x > 0 && y == 0){
28185                     scale = targetHeight / height;
28186                 }
28187                 
28188                 if(x > 0 && y > 0){
28189                     scale = targetWidth / width;
28190                     
28191                     if(width < height){
28192                         scale = targetHeight / height;
28193                     }
28194                 }
28195                 
28196                 context.scale(scale, scale);
28197                 
28198                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28199                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28200
28201                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28202                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28203
28204                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28205                 
28206                 break;
28207             case 90 : 
28208                 
28209                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28210                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28211                 
28212                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28213                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28214                 
28215                 var targetWidth = this.minWidth - 2 * x;
28216                 var targetHeight = this.minHeight - 2 * y;
28217                 
28218                 var scale = 1;
28219                 
28220                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28221                     scale = targetWidth / width;
28222                 }
28223                 
28224                 if(x > 0 && y == 0){
28225                     scale = targetHeight / height;
28226                 }
28227                 
28228                 if(x > 0 && y > 0){
28229                     scale = targetWidth / width;
28230                     
28231                     if(width < height){
28232                         scale = targetHeight / height;
28233                     }
28234                 }
28235                 
28236                 context.scale(scale, scale);
28237                 
28238                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28239                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28240
28241                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28242                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28243                 
28244                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28245                 
28246                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28247                 
28248                 break;
28249             case 180 :
28250                 
28251                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28252                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28253                 
28254                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28255                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28256                 
28257                 var targetWidth = this.minWidth - 2 * x;
28258                 var targetHeight = this.minHeight - 2 * y;
28259                 
28260                 var scale = 1;
28261                 
28262                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28263                     scale = targetWidth / width;
28264                 }
28265                 
28266                 if(x > 0 && y == 0){
28267                     scale = targetHeight / height;
28268                 }
28269                 
28270                 if(x > 0 && y > 0){
28271                     scale = targetWidth / width;
28272                     
28273                     if(width < height){
28274                         scale = targetHeight / height;
28275                     }
28276                 }
28277                 
28278                 context.scale(scale, scale);
28279                 
28280                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28281                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28282
28283                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28284                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28285
28286                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28287                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28288                 
28289                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28290                 
28291                 break;
28292             case 270 :
28293                 
28294                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28295                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28296                 
28297                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28298                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28299                 
28300                 var targetWidth = this.minWidth - 2 * x;
28301                 var targetHeight = this.minHeight - 2 * y;
28302                 
28303                 var scale = 1;
28304                 
28305                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28306                     scale = targetWidth / width;
28307                 }
28308                 
28309                 if(x > 0 && y == 0){
28310                     scale = targetHeight / height;
28311                 }
28312                 
28313                 if(x > 0 && y > 0){
28314                     scale = targetWidth / width;
28315                     
28316                     if(width < height){
28317                         scale = targetHeight / height;
28318                     }
28319                 }
28320                 
28321                 context.scale(scale, scale);
28322                 
28323                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28324                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28325
28326                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28327                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28328                 
28329                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28330                 
28331                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28332                 
28333                 break;
28334             default : 
28335                 break;
28336         }
28337         
28338         this.cropData = canvas.toDataURL(this.cropType);
28339         
28340         if(this.fireEvent('crop', this, this.cropData) !== false){
28341             this.process(this.file, this.cropData);
28342         }
28343         
28344         return;
28345         
28346     },
28347     
28348     setThumbBoxSize : function()
28349     {
28350         var width, height;
28351         
28352         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28353             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28354             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28355             
28356             this.minWidth = width;
28357             this.minHeight = height;
28358             
28359             if(this.rotate == 90 || this.rotate == 270){
28360                 this.minWidth = height;
28361                 this.minHeight = width;
28362             }
28363         }
28364         
28365         height = 300;
28366         width = Math.ceil(this.minWidth * height / this.minHeight);
28367         
28368         if(this.minWidth > this.minHeight){
28369             width = 300;
28370             height = Math.ceil(this.minHeight * width / this.minWidth);
28371         }
28372         
28373         this.thumbEl.setStyle({
28374             width : width + 'px',
28375             height : height + 'px'
28376         });
28377
28378         return;
28379             
28380     },
28381     
28382     setThumbBoxPosition : function()
28383     {
28384         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28385         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28386         
28387         this.thumbEl.setLeft(x);
28388         this.thumbEl.setTop(y);
28389         
28390     },
28391     
28392     baseRotateLevel : function()
28393     {
28394         this.baseRotate = 1;
28395         
28396         if(
28397                 typeof(this.exif) != 'undefined' &&
28398                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28399                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28400         ){
28401             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28402         }
28403         
28404         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28405         
28406     },
28407     
28408     baseScaleLevel : function()
28409     {
28410         var width, height;
28411         
28412         if(this.isDocument){
28413             
28414             if(this.baseRotate == 6 || this.baseRotate == 8){
28415             
28416                 height = this.thumbEl.getHeight();
28417                 this.baseScale = height / this.imageEl.OriginWidth;
28418
28419                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28420                     width = this.thumbEl.getWidth();
28421                     this.baseScale = width / this.imageEl.OriginHeight;
28422                 }
28423
28424                 return;
28425             }
28426
28427             height = this.thumbEl.getHeight();
28428             this.baseScale = height / this.imageEl.OriginHeight;
28429
28430             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28431                 width = this.thumbEl.getWidth();
28432                 this.baseScale = width / this.imageEl.OriginWidth;
28433             }
28434
28435             return;
28436         }
28437         
28438         if(this.baseRotate == 6 || this.baseRotate == 8){
28439             
28440             width = this.thumbEl.getHeight();
28441             this.baseScale = width / this.imageEl.OriginHeight;
28442             
28443             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28444                 height = this.thumbEl.getWidth();
28445                 this.baseScale = height / this.imageEl.OriginHeight;
28446             }
28447             
28448             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28449                 height = this.thumbEl.getWidth();
28450                 this.baseScale = height / this.imageEl.OriginHeight;
28451                 
28452                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28453                     width = this.thumbEl.getHeight();
28454                     this.baseScale = width / this.imageEl.OriginWidth;
28455                 }
28456             }
28457             
28458             return;
28459         }
28460         
28461         width = this.thumbEl.getWidth();
28462         this.baseScale = width / this.imageEl.OriginWidth;
28463         
28464         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28465             height = this.thumbEl.getHeight();
28466             this.baseScale = height / this.imageEl.OriginHeight;
28467         }
28468         
28469         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28470             
28471             height = this.thumbEl.getHeight();
28472             this.baseScale = height / this.imageEl.OriginHeight;
28473             
28474             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28475                 width = this.thumbEl.getWidth();
28476                 this.baseScale = width / this.imageEl.OriginWidth;
28477             }
28478             
28479         }
28480         
28481         return;
28482     },
28483     
28484     getScaleLevel : function()
28485     {
28486         return this.baseScale * Math.pow(1.1, this.scale);
28487     },
28488     
28489     onTouchStart : function(e)
28490     {
28491         if(!this.canvasLoaded){
28492             this.beforeSelectFile(e);
28493             return;
28494         }
28495         
28496         var touches = e.browserEvent.touches;
28497         
28498         if(!touches){
28499             return;
28500         }
28501         
28502         if(touches.length == 1){
28503             this.onMouseDown(e);
28504             return;
28505         }
28506         
28507         if(touches.length != 2){
28508             return;
28509         }
28510         
28511         var coords = [];
28512         
28513         for(var i = 0, finger; finger = touches[i]; i++){
28514             coords.push(finger.pageX, finger.pageY);
28515         }
28516         
28517         var x = Math.pow(coords[0] - coords[2], 2);
28518         var y = Math.pow(coords[1] - coords[3], 2);
28519         
28520         this.startDistance = Math.sqrt(x + y);
28521         
28522         this.startScale = this.scale;
28523         
28524         this.pinching = true;
28525         this.dragable = false;
28526         
28527     },
28528     
28529     onTouchMove : function(e)
28530     {
28531         if(!this.pinching && !this.dragable){
28532             return;
28533         }
28534         
28535         var touches = e.browserEvent.touches;
28536         
28537         if(!touches){
28538             return;
28539         }
28540         
28541         if(this.dragable){
28542             this.onMouseMove(e);
28543             return;
28544         }
28545         
28546         var coords = [];
28547         
28548         for(var i = 0, finger; finger = touches[i]; i++){
28549             coords.push(finger.pageX, finger.pageY);
28550         }
28551         
28552         var x = Math.pow(coords[0] - coords[2], 2);
28553         var y = Math.pow(coords[1] - coords[3], 2);
28554         
28555         this.endDistance = Math.sqrt(x + y);
28556         
28557         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28558         
28559         if(!this.zoomable()){
28560             this.scale = this.startScale;
28561             return;
28562         }
28563         
28564         this.draw();
28565         
28566     },
28567     
28568     onTouchEnd : function(e)
28569     {
28570         this.pinching = false;
28571         this.dragable = false;
28572         
28573     },
28574     
28575     process : function(file, crop)
28576     {
28577         if(this.loadMask){
28578             this.maskEl.mask(this.loadingText);
28579         }
28580         
28581         this.xhr = new XMLHttpRequest();
28582         
28583         file.xhr = this.xhr;
28584
28585         this.xhr.open(this.method, this.url, true);
28586         
28587         var headers = {
28588             "Accept": "application/json",
28589             "Cache-Control": "no-cache",
28590             "X-Requested-With": "XMLHttpRequest"
28591         };
28592         
28593         for (var headerName in headers) {
28594             var headerValue = headers[headerName];
28595             if (headerValue) {
28596                 this.xhr.setRequestHeader(headerName, headerValue);
28597             }
28598         }
28599         
28600         var _this = this;
28601         
28602         this.xhr.onload = function()
28603         {
28604             _this.xhrOnLoad(_this.xhr);
28605         }
28606         
28607         this.xhr.onerror = function()
28608         {
28609             _this.xhrOnError(_this.xhr);
28610         }
28611         
28612         var formData = new FormData();
28613
28614         formData.append('returnHTML', 'NO');
28615         
28616         if(crop){
28617             formData.append('crop', crop);
28618         }
28619         
28620         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28621             formData.append(this.paramName, file, file.name);
28622         }
28623         
28624         if(typeof(file.filename) != 'undefined'){
28625             formData.append('filename', file.filename);
28626         }
28627         
28628         if(typeof(file.mimetype) != 'undefined'){
28629             formData.append('mimetype', file.mimetype);
28630         }
28631         
28632         if(this.fireEvent('arrange', this, formData) != false){
28633             this.xhr.send(formData);
28634         };
28635     },
28636     
28637     xhrOnLoad : function(xhr)
28638     {
28639         if(this.loadMask){
28640             this.maskEl.unmask();
28641         }
28642         
28643         if (xhr.readyState !== 4) {
28644             this.fireEvent('exception', this, xhr);
28645             return;
28646         }
28647
28648         var response = Roo.decode(xhr.responseText);
28649         
28650         if(!response.success){
28651             this.fireEvent('exception', this, xhr);
28652             return;
28653         }
28654         
28655         var response = Roo.decode(xhr.responseText);
28656         
28657         this.fireEvent('upload', this, response);
28658         
28659     },
28660     
28661     xhrOnError : function()
28662     {
28663         if(this.loadMask){
28664             this.maskEl.unmask();
28665         }
28666         
28667         Roo.log('xhr on error');
28668         
28669         var response = Roo.decode(xhr.responseText);
28670           
28671         Roo.log(response);
28672         
28673     },
28674     
28675     prepare : function(file)
28676     {   
28677         if(this.loadMask){
28678             this.maskEl.mask(this.loadingText);
28679         }
28680         
28681         this.file = false;
28682         this.exif = {};
28683         
28684         if(typeof(file) === 'string'){
28685             this.loadCanvas(file);
28686             return;
28687         }
28688         
28689         if(!file || !this.urlAPI){
28690             return;
28691         }
28692         
28693         this.file = file;
28694         this.cropType = file.type;
28695         
28696         var _this = this;
28697         
28698         if(this.fireEvent('prepare', this, this.file) != false){
28699             
28700             var reader = new FileReader();
28701             
28702             reader.onload = function (e) {
28703                 if (e.target.error) {
28704                     Roo.log(e.target.error);
28705                     return;
28706                 }
28707                 
28708                 var buffer = e.target.result,
28709                     dataView = new DataView(buffer),
28710                     offset = 2,
28711                     maxOffset = dataView.byteLength - 4,
28712                     markerBytes,
28713                     markerLength;
28714                 
28715                 if (dataView.getUint16(0) === 0xffd8) {
28716                     while (offset < maxOffset) {
28717                         markerBytes = dataView.getUint16(offset);
28718                         
28719                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28720                             markerLength = dataView.getUint16(offset + 2) + 2;
28721                             if (offset + markerLength > dataView.byteLength) {
28722                                 Roo.log('Invalid meta data: Invalid segment size.');
28723                                 break;
28724                             }
28725                             
28726                             if(markerBytes == 0xffe1){
28727                                 _this.parseExifData(
28728                                     dataView,
28729                                     offset,
28730                                     markerLength
28731                                 );
28732                             }
28733                             
28734                             offset += markerLength;
28735                             
28736                             continue;
28737                         }
28738                         
28739                         break;
28740                     }
28741                     
28742                 }
28743                 
28744                 var url = _this.urlAPI.createObjectURL(_this.file);
28745                 
28746                 _this.loadCanvas(url);
28747                 
28748                 return;
28749             }
28750             
28751             reader.readAsArrayBuffer(this.file);
28752             
28753         }
28754         
28755     },
28756     
28757     parseExifData : function(dataView, offset, length)
28758     {
28759         var tiffOffset = offset + 10,
28760             littleEndian,
28761             dirOffset;
28762     
28763         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28764             // No Exif data, might be XMP data instead
28765             return;
28766         }
28767         
28768         // Check for the ASCII code for "Exif" (0x45786966):
28769         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28770             // No Exif data, might be XMP data instead
28771             return;
28772         }
28773         if (tiffOffset + 8 > dataView.byteLength) {
28774             Roo.log('Invalid Exif data: Invalid segment size.');
28775             return;
28776         }
28777         // Check for the two null bytes:
28778         if (dataView.getUint16(offset + 8) !== 0x0000) {
28779             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28780             return;
28781         }
28782         // Check the byte alignment:
28783         switch (dataView.getUint16(tiffOffset)) {
28784         case 0x4949:
28785             littleEndian = true;
28786             break;
28787         case 0x4D4D:
28788             littleEndian = false;
28789             break;
28790         default:
28791             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28792             return;
28793         }
28794         // Check for the TIFF tag marker (0x002A):
28795         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28796             Roo.log('Invalid Exif data: Missing TIFF marker.');
28797             return;
28798         }
28799         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28800         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28801         
28802         this.parseExifTags(
28803             dataView,
28804             tiffOffset,
28805             tiffOffset + dirOffset,
28806             littleEndian
28807         );
28808     },
28809     
28810     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28811     {
28812         var tagsNumber,
28813             dirEndOffset,
28814             i;
28815         if (dirOffset + 6 > dataView.byteLength) {
28816             Roo.log('Invalid Exif data: Invalid directory offset.');
28817             return;
28818         }
28819         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28820         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28821         if (dirEndOffset + 4 > dataView.byteLength) {
28822             Roo.log('Invalid Exif data: Invalid directory size.');
28823             return;
28824         }
28825         for (i = 0; i < tagsNumber; i += 1) {
28826             this.parseExifTag(
28827                 dataView,
28828                 tiffOffset,
28829                 dirOffset + 2 + 12 * i, // tag offset
28830                 littleEndian
28831             );
28832         }
28833         // Return the offset to the next directory:
28834         return dataView.getUint32(dirEndOffset, littleEndian);
28835     },
28836     
28837     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28838     {
28839         var tag = dataView.getUint16(offset, littleEndian);
28840         
28841         this.exif[tag] = this.getExifValue(
28842             dataView,
28843             tiffOffset,
28844             offset,
28845             dataView.getUint16(offset + 2, littleEndian), // tag type
28846             dataView.getUint32(offset + 4, littleEndian), // tag length
28847             littleEndian
28848         );
28849     },
28850     
28851     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28852     {
28853         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28854             tagSize,
28855             dataOffset,
28856             values,
28857             i,
28858             str,
28859             c;
28860     
28861         if (!tagType) {
28862             Roo.log('Invalid Exif data: Invalid tag type.');
28863             return;
28864         }
28865         
28866         tagSize = tagType.size * length;
28867         // Determine if the value is contained in the dataOffset bytes,
28868         // or if the value at the dataOffset is a pointer to the actual data:
28869         dataOffset = tagSize > 4 ?
28870                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28871         if (dataOffset + tagSize > dataView.byteLength) {
28872             Roo.log('Invalid Exif data: Invalid data offset.');
28873             return;
28874         }
28875         if (length === 1) {
28876             return tagType.getValue(dataView, dataOffset, littleEndian);
28877         }
28878         values = [];
28879         for (i = 0; i < length; i += 1) {
28880             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28881         }
28882         
28883         if (tagType.ascii) {
28884             str = '';
28885             // Concatenate the chars:
28886             for (i = 0; i < values.length; i += 1) {
28887                 c = values[i];
28888                 // Ignore the terminating NULL byte(s):
28889                 if (c === '\u0000') {
28890                     break;
28891                 }
28892                 str += c;
28893             }
28894             return str;
28895         }
28896         return values;
28897     }
28898     
28899 });
28900
28901 Roo.apply(Roo.bootstrap.UploadCropbox, {
28902     tags : {
28903         'Orientation': 0x0112
28904     },
28905     
28906     Orientation: {
28907             1: 0, //'top-left',
28908 //            2: 'top-right',
28909             3: 180, //'bottom-right',
28910 //            4: 'bottom-left',
28911 //            5: 'left-top',
28912             6: 90, //'right-top',
28913 //            7: 'right-bottom',
28914             8: 270 //'left-bottom'
28915     },
28916     
28917     exifTagTypes : {
28918         // byte, 8-bit unsigned int:
28919         1: {
28920             getValue: function (dataView, dataOffset) {
28921                 return dataView.getUint8(dataOffset);
28922             },
28923             size: 1
28924         },
28925         // ascii, 8-bit byte:
28926         2: {
28927             getValue: function (dataView, dataOffset) {
28928                 return String.fromCharCode(dataView.getUint8(dataOffset));
28929             },
28930             size: 1,
28931             ascii: true
28932         },
28933         // short, 16 bit int:
28934         3: {
28935             getValue: function (dataView, dataOffset, littleEndian) {
28936                 return dataView.getUint16(dataOffset, littleEndian);
28937             },
28938             size: 2
28939         },
28940         // long, 32 bit int:
28941         4: {
28942             getValue: function (dataView, dataOffset, littleEndian) {
28943                 return dataView.getUint32(dataOffset, littleEndian);
28944             },
28945             size: 4
28946         },
28947         // rational = two long values, first is numerator, second is denominator:
28948         5: {
28949             getValue: function (dataView, dataOffset, littleEndian) {
28950                 return dataView.getUint32(dataOffset, littleEndian) /
28951                     dataView.getUint32(dataOffset + 4, littleEndian);
28952             },
28953             size: 8
28954         },
28955         // slong, 32 bit signed int:
28956         9: {
28957             getValue: function (dataView, dataOffset, littleEndian) {
28958                 return dataView.getInt32(dataOffset, littleEndian);
28959             },
28960             size: 4
28961         },
28962         // srational, two slongs, first is numerator, second is denominator:
28963         10: {
28964             getValue: function (dataView, dataOffset, littleEndian) {
28965                 return dataView.getInt32(dataOffset, littleEndian) /
28966                     dataView.getInt32(dataOffset + 4, littleEndian);
28967             },
28968             size: 8
28969         }
28970     },
28971     
28972     footer : {
28973         STANDARD : [
28974             {
28975                 tag : 'div',
28976                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28977                 action : 'rotate-left',
28978                 cn : [
28979                     {
28980                         tag : 'button',
28981                         cls : 'btn btn-default',
28982                         html : '<i class="fa fa-undo"></i>'
28983                     }
28984                 ]
28985             },
28986             {
28987                 tag : 'div',
28988                 cls : 'btn-group roo-upload-cropbox-picture',
28989                 action : 'picture',
28990                 cn : [
28991                     {
28992                         tag : 'button',
28993                         cls : 'btn btn-default',
28994                         html : '<i class="fa fa-picture-o"></i>'
28995                     }
28996                 ]
28997             },
28998             {
28999                 tag : 'div',
29000                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29001                 action : 'rotate-right',
29002                 cn : [
29003                     {
29004                         tag : 'button',
29005                         cls : 'btn btn-default',
29006                         html : '<i class="fa fa-repeat"></i>'
29007                     }
29008                 ]
29009             }
29010         ],
29011         DOCUMENT : [
29012             {
29013                 tag : 'div',
29014                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29015                 action : 'rotate-left',
29016                 cn : [
29017                     {
29018                         tag : 'button',
29019                         cls : 'btn btn-default',
29020                         html : '<i class="fa fa-undo"></i>'
29021                     }
29022                 ]
29023             },
29024             {
29025                 tag : 'div',
29026                 cls : 'btn-group roo-upload-cropbox-download',
29027                 action : 'download',
29028                 cn : [
29029                     {
29030                         tag : 'button',
29031                         cls : 'btn btn-default',
29032                         html : '<i class="fa fa-download"></i>'
29033                     }
29034                 ]
29035             },
29036             {
29037                 tag : 'div',
29038                 cls : 'btn-group roo-upload-cropbox-crop',
29039                 action : 'crop',
29040                 cn : [
29041                     {
29042                         tag : 'button',
29043                         cls : 'btn btn-default',
29044                         html : '<i class="fa fa-crop"></i>'
29045                     }
29046                 ]
29047             },
29048             {
29049                 tag : 'div',
29050                 cls : 'btn-group roo-upload-cropbox-trash',
29051                 action : 'trash',
29052                 cn : [
29053                     {
29054                         tag : 'button',
29055                         cls : 'btn btn-default',
29056                         html : '<i class="fa fa-trash"></i>'
29057                     }
29058                 ]
29059             },
29060             {
29061                 tag : 'div',
29062                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29063                 action : 'rotate-right',
29064                 cn : [
29065                     {
29066                         tag : 'button',
29067                         cls : 'btn btn-default',
29068                         html : '<i class="fa fa-repeat"></i>'
29069                     }
29070                 ]
29071             }
29072         ],
29073         ROTATOR : [
29074             {
29075                 tag : 'div',
29076                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29077                 action : 'rotate-left',
29078                 cn : [
29079                     {
29080                         tag : 'button',
29081                         cls : 'btn btn-default',
29082                         html : '<i class="fa fa-undo"></i>'
29083                     }
29084                 ]
29085             },
29086             {
29087                 tag : 'div',
29088                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29089                 action : 'rotate-right',
29090                 cn : [
29091                     {
29092                         tag : 'button',
29093                         cls : 'btn btn-default',
29094                         html : '<i class="fa fa-repeat"></i>'
29095                     }
29096                 ]
29097             }
29098         ]
29099     }
29100 });
29101
29102 /*
29103 * Licence: LGPL
29104 */
29105
29106 /**
29107  * @class Roo.bootstrap.DocumentManager
29108  * @extends Roo.bootstrap.Component
29109  * Bootstrap DocumentManager class
29110  * @cfg {String} paramName default 'imageUpload'
29111  * @cfg {String} toolTipName default 'filename'
29112  * @cfg {String} method default POST
29113  * @cfg {String} url action url
29114  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29115  * @cfg {Boolean} multiple multiple upload default true
29116  * @cfg {Number} thumbSize default 300
29117  * @cfg {String} fieldLabel
29118  * @cfg {Number} labelWidth default 4
29119  * @cfg {String} labelAlign (left|top) default left
29120  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29121 * @cfg {Number} labellg set the width of label (1-12)
29122  * @cfg {Number} labelmd set the width of label (1-12)
29123  * @cfg {Number} labelsm set the width of label (1-12)
29124  * @cfg {Number} labelxs set the width of label (1-12)
29125  * 
29126  * @constructor
29127  * Create a new DocumentManager
29128  * @param {Object} config The config object
29129  */
29130
29131 Roo.bootstrap.DocumentManager = function(config){
29132     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29133     
29134     this.files = [];
29135     this.delegates = [];
29136     
29137     this.addEvents({
29138         /**
29139          * @event initial
29140          * Fire when initial the DocumentManager
29141          * @param {Roo.bootstrap.DocumentManager} this
29142          */
29143         "initial" : true,
29144         /**
29145          * @event inspect
29146          * inspect selected file
29147          * @param {Roo.bootstrap.DocumentManager} this
29148          * @param {File} file
29149          */
29150         "inspect" : true,
29151         /**
29152          * @event exception
29153          * Fire when xhr load exception
29154          * @param {Roo.bootstrap.DocumentManager} this
29155          * @param {XMLHttpRequest} xhr
29156          */
29157         "exception" : true,
29158         /**
29159          * @event afterupload
29160          * Fire when xhr load exception
29161          * @param {Roo.bootstrap.DocumentManager} this
29162          * @param {XMLHttpRequest} xhr
29163          */
29164         "afterupload" : true,
29165         /**
29166          * @event prepare
29167          * prepare the form data
29168          * @param {Roo.bootstrap.DocumentManager} this
29169          * @param {Object} formData
29170          */
29171         "prepare" : true,
29172         /**
29173          * @event remove
29174          * Fire when remove the file
29175          * @param {Roo.bootstrap.DocumentManager} this
29176          * @param {Object} file
29177          */
29178         "remove" : true,
29179         /**
29180          * @event refresh
29181          * Fire after refresh the file
29182          * @param {Roo.bootstrap.DocumentManager} this
29183          */
29184         "refresh" : true,
29185         /**
29186          * @event click
29187          * Fire after click the image
29188          * @param {Roo.bootstrap.DocumentManager} this
29189          * @param {Object} file
29190          */
29191         "click" : true,
29192         /**
29193          * @event edit
29194          * Fire when upload a image and editable set to true
29195          * @param {Roo.bootstrap.DocumentManager} this
29196          * @param {Object} file
29197          */
29198         "edit" : true,
29199         /**
29200          * @event beforeselectfile
29201          * Fire before select file
29202          * @param {Roo.bootstrap.DocumentManager} this
29203          */
29204         "beforeselectfile" : true,
29205         /**
29206          * @event process
29207          * Fire before process file
29208          * @param {Roo.bootstrap.DocumentManager} this
29209          * @param {Object} file
29210          */
29211         "process" : true,
29212         /**
29213          * @event previewrendered
29214          * Fire when preview rendered
29215          * @param {Roo.bootstrap.DocumentManager} this
29216          * @param {Object} file
29217          */
29218         "previewrendered" : true,
29219         /**
29220          */
29221         "previewResize" : true
29222         
29223     });
29224 };
29225
29226 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29227     
29228     boxes : 0,
29229     inputName : '',
29230     thumbSize : 300,
29231     multiple : true,
29232     files : false,
29233     method : 'POST',
29234     url : '',
29235     paramName : 'imageUpload',
29236     toolTipName : 'filename',
29237     fieldLabel : '',
29238     labelWidth : 4,
29239     labelAlign : 'left',
29240     editable : true,
29241     delegates : false,
29242     xhr : false, 
29243     
29244     labellg : 0,
29245     labelmd : 0,
29246     labelsm : 0,
29247     labelxs : 0,
29248     
29249     getAutoCreate : function()
29250     {   
29251         var managerWidget = {
29252             tag : 'div',
29253             cls : 'roo-document-manager',
29254             cn : [
29255                 {
29256                     tag : 'input',
29257                     cls : 'roo-document-manager-selector',
29258                     type : 'file'
29259                 },
29260                 {
29261                     tag : 'div',
29262                     cls : 'roo-document-manager-uploader',
29263                     cn : [
29264                         {
29265                             tag : 'div',
29266                             cls : 'roo-document-manager-upload-btn',
29267                             html : '<i class="fa fa-plus"></i>'
29268                         }
29269                     ]
29270                     
29271                 }
29272             ]
29273         };
29274         
29275         var content = [
29276             {
29277                 tag : 'div',
29278                 cls : 'column col-md-12',
29279                 cn : managerWidget
29280             }
29281         ];
29282         
29283         if(this.fieldLabel.length){
29284             
29285             content = [
29286                 {
29287                     tag : 'div',
29288                     cls : 'column col-md-12',
29289                     html : this.fieldLabel
29290                 },
29291                 {
29292                     tag : 'div',
29293                     cls : 'column col-md-12',
29294                     cn : managerWidget
29295                 }
29296             ];
29297
29298             if(this.labelAlign == 'left'){
29299                 content = [
29300                     {
29301                         tag : 'div',
29302                         cls : 'column',
29303                         html : this.fieldLabel
29304                     },
29305                     {
29306                         tag : 'div',
29307                         cls : 'column',
29308                         cn : managerWidget
29309                     }
29310                 ];
29311                 
29312                 if(this.labelWidth > 12){
29313                     content[0].style = "width: " + this.labelWidth + 'px';
29314                 }
29315
29316                 if(this.labelWidth < 13 && this.labelmd == 0){
29317                     this.labelmd = this.labelWidth;
29318                 }
29319
29320                 if(this.labellg > 0){
29321                     content[0].cls += ' col-lg-' + this.labellg;
29322                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29323                 }
29324
29325                 if(this.labelmd > 0){
29326                     content[0].cls += ' col-md-' + this.labelmd;
29327                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29328                 }
29329
29330                 if(this.labelsm > 0){
29331                     content[0].cls += ' col-sm-' + this.labelsm;
29332                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29333                 }
29334
29335                 if(this.labelxs > 0){
29336                     content[0].cls += ' col-xs-' + this.labelxs;
29337                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29338                 }
29339                 
29340             }
29341         }
29342         
29343         var cfg = {
29344             tag : 'div',
29345             cls : 'row clearfix',
29346             cn : content
29347         };
29348         
29349         return cfg;
29350         
29351     },
29352     
29353     initEvents : function()
29354     {
29355         this.managerEl = this.el.select('.roo-document-manager', true).first();
29356         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29357         
29358         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29359         this.selectorEl.hide();
29360         
29361         if(this.multiple){
29362             this.selectorEl.attr('multiple', 'multiple');
29363         }
29364         
29365         this.selectorEl.on('change', this.onFileSelected, this);
29366         
29367         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29368         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29369         
29370         this.uploader.on('click', this.onUploaderClick, this);
29371         
29372         this.renderProgressDialog();
29373         
29374         var _this = this;
29375         
29376         window.addEventListener("resize", function() { _this.refresh(); } );
29377         
29378         this.fireEvent('initial', this);
29379     },
29380     
29381     renderProgressDialog : function()
29382     {
29383         var _this = this;
29384         
29385         this.progressDialog = new Roo.bootstrap.Modal({
29386             cls : 'roo-document-manager-progress-dialog',
29387             allow_close : false,
29388             animate : false,
29389             title : '',
29390             buttons : [
29391                 {
29392                     name  :'cancel',
29393                     weight : 'danger',
29394                     html : 'Cancel'
29395                 }
29396             ], 
29397             listeners : { 
29398                 btnclick : function() {
29399                     _this.uploadCancel();
29400                     this.hide();
29401                 }
29402             }
29403         });
29404          
29405         this.progressDialog.render(Roo.get(document.body));
29406          
29407         this.progress = new Roo.bootstrap.Progress({
29408             cls : 'roo-document-manager-progress',
29409             active : true,
29410             striped : true
29411         });
29412         
29413         this.progress.render(this.progressDialog.getChildContainer());
29414         
29415         this.progressBar = new Roo.bootstrap.ProgressBar({
29416             cls : 'roo-document-manager-progress-bar',
29417             aria_valuenow : 0,
29418             aria_valuemin : 0,
29419             aria_valuemax : 12,
29420             panel : 'success'
29421         });
29422         
29423         this.progressBar.render(this.progress.getChildContainer());
29424     },
29425     
29426     onUploaderClick : function(e)
29427     {
29428         e.preventDefault();
29429      
29430         if(this.fireEvent('beforeselectfile', this) != false){
29431             this.selectorEl.dom.click();
29432         }
29433         
29434     },
29435     
29436     onFileSelected : function(e)
29437     {
29438         e.preventDefault();
29439         
29440         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29441             return;
29442         }
29443         
29444         Roo.each(this.selectorEl.dom.files, function(file){
29445             if(this.fireEvent('inspect', this, file) != false){
29446                 this.files.push(file);
29447             }
29448         }, this);
29449         
29450         this.queue();
29451         
29452     },
29453     
29454     queue : function()
29455     {
29456         this.selectorEl.dom.value = '';
29457         
29458         if(!this.files || !this.files.length){
29459             return;
29460         }
29461         
29462         if(this.boxes > 0 && this.files.length > this.boxes){
29463             this.files = this.files.slice(0, this.boxes);
29464         }
29465         
29466         this.uploader.show();
29467         
29468         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29469             this.uploader.hide();
29470         }
29471         
29472         var _this = this;
29473         
29474         var files = [];
29475         
29476         var docs = [];
29477         
29478         Roo.each(this.files, function(file){
29479             
29480             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29481                 var f = this.renderPreview(file);
29482                 files.push(f);
29483                 return;
29484             }
29485             
29486             if(file.type.indexOf('image') != -1){
29487                 this.delegates.push(
29488                     (function(){
29489                         _this.process(file);
29490                     }).createDelegate(this)
29491                 );
29492         
29493                 return;
29494             }
29495             
29496             docs.push(
29497                 (function(){
29498                     _this.process(file);
29499                 }).createDelegate(this)
29500             );
29501             
29502         }, this);
29503         
29504         this.files = files;
29505         
29506         this.delegates = this.delegates.concat(docs);
29507         
29508         if(!this.delegates.length){
29509             this.refresh();
29510             return;
29511         }
29512         
29513         this.progressBar.aria_valuemax = this.delegates.length;
29514         
29515         this.arrange();
29516         
29517         return;
29518     },
29519     
29520     arrange : function()
29521     {
29522         if(!this.delegates.length){
29523             this.progressDialog.hide();
29524             this.refresh();
29525             return;
29526         }
29527         
29528         var delegate = this.delegates.shift();
29529         
29530         this.progressDialog.show();
29531         
29532         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29533         
29534         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29535         
29536         delegate();
29537     },
29538     
29539     refresh : function()
29540     {
29541         this.uploader.show();
29542         
29543         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29544             this.uploader.hide();
29545         }
29546         
29547         Roo.isTouch ? this.closable(false) : this.closable(true);
29548         
29549         this.fireEvent('refresh', this);
29550     },
29551     
29552     onRemove : function(e, el, o)
29553     {
29554         e.preventDefault();
29555         
29556         this.fireEvent('remove', this, o);
29557         
29558     },
29559     
29560     remove : function(o)
29561     {
29562         var files = [];
29563         
29564         Roo.each(this.files, function(file){
29565             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29566                 files.push(file);
29567                 return;
29568             }
29569
29570             o.target.remove();
29571
29572         }, this);
29573         
29574         this.files = files;
29575         
29576         this.refresh();
29577     },
29578     
29579     clear : function()
29580     {
29581         Roo.each(this.files, function(file){
29582             if(!file.target){
29583                 return;
29584             }
29585             
29586             file.target.remove();
29587
29588         }, this);
29589         
29590         this.files = [];
29591         
29592         this.refresh();
29593     },
29594     
29595     onClick : function(e, el, o)
29596     {
29597         e.preventDefault();
29598         
29599         this.fireEvent('click', this, o);
29600         
29601     },
29602     
29603     closable : function(closable)
29604     {
29605         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29606             
29607             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29608             
29609             if(closable){
29610                 el.show();
29611                 return;
29612             }
29613             
29614             el.hide();
29615             
29616         }, this);
29617     },
29618     
29619     xhrOnLoad : function(xhr)
29620     {
29621         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29622             el.remove();
29623         }, this);
29624         
29625         if (xhr.readyState !== 4) {
29626             this.arrange();
29627             this.fireEvent('exception', this, xhr);
29628             return;
29629         }
29630
29631         var response = Roo.decode(xhr.responseText);
29632         
29633         if(!response.success){
29634             this.arrange();
29635             this.fireEvent('exception', this, xhr);
29636             return;
29637         }
29638         
29639         var file = this.renderPreview(response.data);
29640         
29641         this.files.push(file);
29642         
29643         this.arrange();
29644         
29645         this.fireEvent('afterupload', this, xhr);
29646         
29647     },
29648     
29649     xhrOnError : function(xhr)
29650     {
29651         Roo.log('xhr on error');
29652         
29653         var response = Roo.decode(xhr.responseText);
29654           
29655         Roo.log(response);
29656         
29657         this.arrange();
29658     },
29659     
29660     process : function(file)
29661     {
29662         if(this.fireEvent('process', this, file) !== false){
29663             if(this.editable && file.type.indexOf('image') != -1){
29664                 this.fireEvent('edit', this, file);
29665                 return;
29666             }
29667
29668             this.uploadStart(file, false);
29669
29670             return;
29671         }
29672         
29673     },
29674     
29675     uploadStart : function(file, crop)
29676     {
29677         this.xhr = new XMLHttpRequest();
29678         
29679         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29680             this.arrange();
29681             return;
29682         }
29683         
29684         file.xhr = this.xhr;
29685             
29686         this.managerEl.createChild({
29687             tag : 'div',
29688             cls : 'roo-document-manager-loading',
29689             cn : [
29690                 {
29691                     tag : 'div',
29692                     tooltip : file.name,
29693                     cls : 'roo-document-manager-thumb',
29694                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29695                 }
29696             ]
29697
29698         });
29699
29700         this.xhr.open(this.method, this.url, true);
29701         
29702         var headers = {
29703             "Accept": "application/json",
29704             "Cache-Control": "no-cache",
29705             "X-Requested-With": "XMLHttpRequest"
29706         };
29707         
29708         for (var headerName in headers) {
29709             var headerValue = headers[headerName];
29710             if (headerValue) {
29711                 this.xhr.setRequestHeader(headerName, headerValue);
29712             }
29713         }
29714         
29715         var _this = this;
29716         
29717         this.xhr.onload = function()
29718         {
29719             _this.xhrOnLoad(_this.xhr);
29720         }
29721         
29722         this.xhr.onerror = function()
29723         {
29724             _this.xhrOnError(_this.xhr);
29725         }
29726         
29727         var formData = new FormData();
29728
29729         formData.append('returnHTML', 'NO');
29730         
29731         if(crop){
29732             formData.append('crop', crop);
29733         }
29734         
29735         formData.append(this.paramName, file, file.name);
29736         
29737         var options = {
29738             file : file, 
29739             manually : false
29740         };
29741         
29742         if(this.fireEvent('prepare', this, formData, options) != false){
29743             
29744             if(options.manually){
29745                 return;
29746             }
29747             
29748             this.xhr.send(formData);
29749             return;
29750         };
29751         
29752         this.uploadCancel();
29753     },
29754     
29755     uploadCancel : function()
29756     {
29757         if (this.xhr) {
29758             this.xhr.abort();
29759         }
29760         
29761         this.delegates = [];
29762         
29763         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29764             el.remove();
29765         }, this);
29766         
29767         this.arrange();
29768     },
29769     
29770     renderPreview : function(file)
29771     {
29772         if(typeof(file.target) != 'undefined' && file.target){
29773             return file;
29774         }
29775         
29776         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29777         
29778         var previewEl = this.managerEl.createChild({
29779             tag : 'div',
29780             cls : 'roo-document-manager-preview',
29781             cn : [
29782                 {
29783                     tag : 'div',
29784                     tooltip : file[this.toolTipName],
29785                     cls : 'roo-document-manager-thumb',
29786                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29787                 },
29788                 {
29789                     tag : 'button',
29790                     cls : 'close',
29791                     html : '<i class="fa fa-times-circle"></i>'
29792                 }
29793             ]
29794         });
29795
29796         var close = previewEl.select('button.close', true).first();
29797
29798         close.on('click', this.onRemove, this, file);
29799
29800         file.target = previewEl;
29801
29802         var image = previewEl.select('img', true).first();
29803         
29804         var _this = this;
29805         
29806         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29807         
29808         image.on('click', this.onClick, this, file);
29809         
29810         this.fireEvent('previewrendered', this, file);
29811         
29812         return file;
29813         
29814     },
29815     
29816     onPreviewLoad : function(file, image)
29817     {
29818         if(typeof(file.target) == 'undefined' || !file.target){
29819             return;
29820         }
29821         
29822         var width = image.dom.naturalWidth || image.dom.width;
29823         var height = image.dom.naturalHeight || image.dom.height;
29824         
29825         if(!this.previewResize) {
29826             return;
29827         }
29828         
29829         if(width > height){
29830             file.target.addClass('wide');
29831             return;
29832         }
29833         
29834         file.target.addClass('tall');
29835         return;
29836         
29837     },
29838     
29839     uploadFromSource : function(file, crop)
29840     {
29841         this.xhr = new XMLHttpRequest();
29842         
29843         this.managerEl.createChild({
29844             tag : 'div',
29845             cls : 'roo-document-manager-loading',
29846             cn : [
29847                 {
29848                     tag : 'div',
29849                     tooltip : file.name,
29850                     cls : 'roo-document-manager-thumb',
29851                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29852                 }
29853             ]
29854
29855         });
29856
29857         this.xhr.open(this.method, this.url, true);
29858         
29859         var headers = {
29860             "Accept": "application/json",
29861             "Cache-Control": "no-cache",
29862             "X-Requested-With": "XMLHttpRequest"
29863         };
29864         
29865         for (var headerName in headers) {
29866             var headerValue = headers[headerName];
29867             if (headerValue) {
29868                 this.xhr.setRequestHeader(headerName, headerValue);
29869             }
29870         }
29871         
29872         var _this = this;
29873         
29874         this.xhr.onload = function()
29875         {
29876             _this.xhrOnLoad(_this.xhr);
29877         }
29878         
29879         this.xhr.onerror = function()
29880         {
29881             _this.xhrOnError(_this.xhr);
29882         }
29883         
29884         var formData = new FormData();
29885
29886         formData.append('returnHTML', 'NO');
29887         
29888         formData.append('crop', crop);
29889         
29890         if(typeof(file.filename) != 'undefined'){
29891             formData.append('filename', file.filename);
29892         }
29893         
29894         if(typeof(file.mimetype) != 'undefined'){
29895             formData.append('mimetype', file.mimetype);
29896         }
29897         
29898         Roo.log(formData);
29899         
29900         if(this.fireEvent('prepare', this, formData) != false){
29901             this.xhr.send(formData);
29902         };
29903     }
29904 });
29905
29906 /*
29907 * Licence: LGPL
29908 */
29909
29910 /**
29911  * @class Roo.bootstrap.DocumentViewer
29912  * @extends Roo.bootstrap.Component
29913  * Bootstrap DocumentViewer class
29914  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29915  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29916  * 
29917  * @constructor
29918  * Create a new DocumentViewer
29919  * @param {Object} config The config object
29920  */
29921
29922 Roo.bootstrap.DocumentViewer = function(config){
29923     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29924     
29925     this.addEvents({
29926         /**
29927          * @event initial
29928          * Fire after initEvent
29929          * @param {Roo.bootstrap.DocumentViewer} this
29930          */
29931         "initial" : true,
29932         /**
29933          * @event click
29934          * Fire after click
29935          * @param {Roo.bootstrap.DocumentViewer} this
29936          */
29937         "click" : true,
29938         /**
29939          * @event download
29940          * Fire after download button
29941          * @param {Roo.bootstrap.DocumentViewer} this
29942          */
29943         "download" : true,
29944         /**
29945          * @event trash
29946          * Fire after trash button
29947          * @param {Roo.bootstrap.DocumentViewer} this
29948          */
29949         "trash" : true
29950         
29951     });
29952 };
29953
29954 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29955     
29956     showDownload : true,
29957     
29958     showTrash : true,
29959     
29960     getAutoCreate : function()
29961     {
29962         var cfg = {
29963             tag : 'div',
29964             cls : 'roo-document-viewer',
29965             cn : [
29966                 {
29967                     tag : 'div',
29968                     cls : 'roo-document-viewer-body',
29969                     cn : [
29970                         {
29971                             tag : 'div',
29972                             cls : 'roo-document-viewer-thumb',
29973                             cn : [
29974                                 {
29975                                     tag : 'img',
29976                                     cls : 'roo-document-viewer-image'
29977                                 }
29978                             ]
29979                         }
29980                     ]
29981                 },
29982                 {
29983                     tag : 'div',
29984                     cls : 'roo-document-viewer-footer',
29985                     cn : {
29986                         tag : 'div',
29987                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29988                         cn : [
29989                             {
29990                                 tag : 'div',
29991                                 cls : 'btn-group roo-document-viewer-download',
29992                                 cn : [
29993                                     {
29994                                         tag : 'button',
29995                                         cls : 'btn btn-default',
29996                                         html : '<i class="fa fa-download"></i>'
29997                                     }
29998                                 ]
29999                             },
30000                             {
30001                                 tag : 'div',
30002                                 cls : 'btn-group roo-document-viewer-trash',
30003                                 cn : [
30004                                     {
30005                                         tag : 'button',
30006                                         cls : 'btn btn-default',
30007                                         html : '<i class="fa fa-trash"></i>'
30008                                     }
30009                                 ]
30010                             }
30011                         ]
30012                     }
30013                 }
30014             ]
30015         };
30016         
30017         return cfg;
30018     },
30019     
30020     initEvents : function()
30021     {
30022         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30023         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30024         
30025         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30026         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30027         
30028         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30029         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30030         
30031         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30032         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30033         
30034         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30035         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30036         
30037         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30038         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30039         
30040         this.bodyEl.on('click', this.onClick, this);
30041         this.downloadBtn.on('click', this.onDownload, this);
30042         this.trashBtn.on('click', this.onTrash, this);
30043         
30044         this.downloadBtn.hide();
30045         this.trashBtn.hide();
30046         
30047         if(this.showDownload){
30048             this.downloadBtn.show();
30049         }
30050         
30051         if(this.showTrash){
30052             this.trashBtn.show();
30053         }
30054         
30055         if(!this.showDownload && !this.showTrash) {
30056             this.footerEl.hide();
30057         }
30058         
30059     },
30060     
30061     initial : function()
30062     {
30063         this.fireEvent('initial', this);
30064         
30065     },
30066     
30067     onClick : function(e)
30068     {
30069         e.preventDefault();
30070         
30071         this.fireEvent('click', this);
30072     },
30073     
30074     onDownload : function(e)
30075     {
30076         e.preventDefault();
30077         
30078         this.fireEvent('download', this);
30079     },
30080     
30081     onTrash : function(e)
30082     {
30083         e.preventDefault();
30084         
30085         this.fireEvent('trash', this);
30086     }
30087     
30088 });
30089 /*
30090  * - LGPL
30091  *
30092  * nav progress bar
30093  * 
30094  */
30095
30096 /**
30097  * @class Roo.bootstrap.NavProgressBar
30098  * @extends Roo.bootstrap.Component
30099  * Bootstrap NavProgressBar class
30100  * 
30101  * @constructor
30102  * Create a new nav progress bar
30103  * @param {Object} config The config object
30104  */
30105
30106 Roo.bootstrap.NavProgressBar = function(config){
30107     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30108
30109     this.bullets = this.bullets || [];
30110    
30111 //    Roo.bootstrap.NavProgressBar.register(this);
30112      this.addEvents({
30113         /**
30114              * @event changed
30115              * Fires when the active item changes
30116              * @param {Roo.bootstrap.NavProgressBar} this
30117              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30118              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30119          */
30120         'changed': true
30121      });
30122     
30123 };
30124
30125 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30126     
30127     bullets : [],
30128     barItems : [],
30129     
30130     getAutoCreate : function()
30131     {
30132         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30133         
30134         cfg = {
30135             tag : 'div',
30136             cls : 'roo-navigation-bar-group',
30137             cn : [
30138                 {
30139                     tag : 'div',
30140                     cls : 'roo-navigation-top-bar'
30141                 },
30142                 {
30143                     tag : 'div',
30144                     cls : 'roo-navigation-bullets-bar',
30145                     cn : [
30146                         {
30147                             tag : 'ul',
30148                             cls : 'roo-navigation-bar'
30149                         }
30150                     ]
30151                 },
30152                 
30153                 {
30154                     tag : 'div',
30155                     cls : 'roo-navigation-bottom-bar'
30156                 }
30157             ]
30158             
30159         };
30160         
30161         return cfg;
30162         
30163     },
30164     
30165     initEvents: function() 
30166     {
30167         
30168     },
30169     
30170     onRender : function(ct, position) 
30171     {
30172         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30173         
30174         if(this.bullets.length){
30175             Roo.each(this.bullets, function(b){
30176                this.addItem(b);
30177             }, this);
30178         }
30179         
30180         this.format();
30181         
30182     },
30183     
30184     addItem : function(cfg)
30185     {
30186         var item = new Roo.bootstrap.NavProgressItem(cfg);
30187         
30188         item.parentId = this.id;
30189         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30190         
30191         if(cfg.html){
30192             var top = new Roo.bootstrap.Element({
30193                 tag : 'div',
30194                 cls : 'roo-navigation-bar-text'
30195             });
30196             
30197             var bottom = new Roo.bootstrap.Element({
30198                 tag : 'div',
30199                 cls : 'roo-navigation-bar-text'
30200             });
30201             
30202             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30203             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30204             
30205             var topText = new Roo.bootstrap.Element({
30206                 tag : 'span',
30207                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30208             });
30209             
30210             var bottomText = new Roo.bootstrap.Element({
30211                 tag : 'span',
30212                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30213             });
30214             
30215             topText.onRender(top.el, null);
30216             bottomText.onRender(bottom.el, null);
30217             
30218             item.topEl = top;
30219             item.bottomEl = bottom;
30220         }
30221         
30222         this.barItems.push(item);
30223         
30224         return item;
30225     },
30226     
30227     getActive : function()
30228     {
30229         var active = false;
30230         
30231         Roo.each(this.barItems, function(v){
30232             
30233             if (!v.isActive()) {
30234                 return;
30235             }
30236             
30237             active = v;
30238             return false;
30239             
30240         });
30241         
30242         return active;
30243     },
30244     
30245     setActiveItem : function(item)
30246     {
30247         var prev = false;
30248         
30249         Roo.each(this.barItems, function(v){
30250             if (v.rid == item.rid) {
30251                 return ;
30252             }
30253             
30254             if (v.isActive()) {
30255                 v.setActive(false);
30256                 prev = v;
30257             }
30258         });
30259
30260         item.setActive(true);
30261         
30262         this.fireEvent('changed', this, item, prev);
30263     },
30264     
30265     getBarItem: function(rid)
30266     {
30267         var ret = false;
30268         
30269         Roo.each(this.barItems, function(e) {
30270             if (e.rid != rid) {
30271                 return;
30272             }
30273             
30274             ret =  e;
30275             return false;
30276         });
30277         
30278         return ret;
30279     },
30280     
30281     indexOfItem : function(item)
30282     {
30283         var index = false;
30284         
30285         Roo.each(this.barItems, function(v, i){
30286             
30287             if (v.rid != item.rid) {
30288                 return;
30289             }
30290             
30291             index = i;
30292             return false
30293         });
30294         
30295         return index;
30296     },
30297     
30298     setActiveNext : function()
30299     {
30300         var i = this.indexOfItem(this.getActive());
30301         
30302         if (i > this.barItems.length) {
30303             return;
30304         }
30305         
30306         this.setActiveItem(this.barItems[i+1]);
30307     },
30308     
30309     setActivePrev : function()
30310     {
30311         var i = this.indexOfItem(this.getActive());
30312         
30313         if (i  < 1) {
30314             return;
30315         }
30316         
30317         this.setActiveItem(this.barItems[i-1]);
30318     },
30319     
30320     format : function()
30321     {
30322         if(!this.barItems.length){
30323             return;
30324         }
30325      
30326         var width = 100 / this.barItems.length;
30327         
30328         Roo.each(this.barItems, function(i){
30329             i.el.setStyle('width', width + '%');
30330             i.topEl.el.setStyle('width', width + '%');
30331             i.bottomEl.el.setStyle('width', width + '%');
30332         }, this);
30333         
30334     }
30335     
30336 });
30337 /*
30338  * - LGPL
30339  *
30340  * Nav Progress Item
30341  * 
30342  */
30343
30344 /**
30345  * @class Roo.bootstrap.NavProgressItem
30346  * @extends Roo.bootstrap.Component
30347  * Bootstrap NavProgressItem class
30348  * @cfg {String} rid the reference id
30349  * @cfg {Boolean} active (true|false) Is item active default false
30350  * @cfg {Boolean} disabled (true|false) Is item active default false
30351  * @cfg {String} html
30352  * @cfg {String} position (top|bottom) text position default bottom
30353  * @cfg {String} icon show icon instead of number
30354  * 
30355  * @constructor
30356  * Create a new NavProgressItem
30357  * @param {Object} config The config object
30358  */
30359 Roo.bootstrap.NavProgressItem = function(config){
30360     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30361     this.addEvents({
30362         // raw events
30363         /**
30364          * @event click
30365          * The raw click event for the entire grid.
30366          * @param {Roo.bootstrap.NavProgressItem} this
30367          * @param {Roo.EventObject} e
30368          */
30369         "click" : true
30370     });
30371    
30372 };
30373
30374 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30375     
30376     rid : '',
30377     active : false,
30378     disabled : false,
30379     html : '',
30380     position : 'bottom',
30381     icon : false,
30382     
30383     getAutoCreate : function()
30384     {
30385         var iconCls = 'roo-navigation-bar-item-icon';
30386         
30387         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30388         
30389         var cfg = {
30390             tag: 'li',
30391             cls: 'roo-navigation-bar-item',
30392             cn : [
30393                 {
30394                     tag : 'i',
30395                     cls : iconCls
30396                 }
30397             ]
30398         };
30399         
30400         if(this.active){
30401             cfg.cls += ' active';
30402         }
30403         if(this.disabled){
30404             cfg.cls += ' disabled';
30405         }
30406         
30407         return cfg;
30408     },
30409     
30410     disable : function()
30411     {
30412         this.setDisabled(true);
30413     },
30414     
30415     enable : function()
30416     {
30417         this.setDisabled(false);
30418     },
30419     
30420     initEvents: function() 
30421     {
30422         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30423         
30424         this.iconEl.on('click', this.onClick, this);
30425     },
30426     
30427     onClick : function(e)
30428     {
30429         e.preventDefault();
30430         
30431         if(this.disabled){
30432             return;
30433         }
30434         
30435         if(this.fireEvent('click', this, e) === false){
30436             return;
30437         };
30438         
30439         this.parent().setActiveItem(this);
30440     },
30441     
30442     isActive: function () 
30443     {
30444         return this.active;
30445     },
30446     
30447     setActive : function(state)
30448     {
30449         if(this.active == state){
30450             return;
30451         }
30452         
30453         this.active = state;
30454         
30455         if (state) {
30456             this.el.addClass('active');
30457             return;
30458         }
30459         
30460         this.el.removeClass('active');
30461         
30462         return;
30463     },
30464     
30465     setDisabled : function(state)
30466     {
30467         if(this.disabled == state){
30468             return;
30469         }
30470         
30471         this.disabled = state;
30472         
30473         if (state) {
30474             this.el.addClass('disabled');
30475             return;
30476         }
30477         
30478         this.el.removeClass('disabled');
30479     },
30480     
30481     tooltipEl : function()
30482     {
30483         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30484     }
30485 });
30486  
30487
30488  /*
30489  * - LGPL
30490  *
30491  * FieldLabel
30492  * 
30493  */
30494
30495 /**
30496  * @class Roo.bootstrap.FieldLabel
30497  * @extends Roo.bootstrap.Component
30498  * Bootstrap FieldLabel class
30499  * @cfg {String} html contents of the element
30500  * @cfg {String} tag tag of the element default label
30501  * @cfg {String} cls class of the element
30502  * @cfg {String} target label target 
30503  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30504  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30505  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30506  * @cfg {String} iconTooltip default "This field is required"
30507  * @cfg {String} indicatorpos (left|right) default left
30508  * 
30509  * @constructor
30510  * Create a new FieldLabel
30511  * @param {Object} config The config object
30512  */
30513
30514 Roo.bootstrap.FieldLabel = function(config){
30515     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30516     
30517     this.addEvents({
30518             /**
30519              * @event invalid
30520              * Fires after the field has been marked as invalid.
30521              * @param {Roo.form.FieldLabel} this
30522              * @param {String} msg The validation message
30523              */
30524             invalid : true,
30525             /**
30526              * @event valid
30527              * Fires after the field has been validated with no errors.
30528              * @param {Roo.form.FieldLabel} this
30529              */
30530             valid : true
30531         });
30532 };
30533
30534 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30535     
30536     tag: 'label',
30537     cls: '',
30538     html: '',
30539     target: '',
30540     allowBlank : true,
30541     invalidClass : 'has-warning',
30542     validClass : 'has-success',
30543     iconTooltip : 'This field is required',
30544     indicatorpos : 'left',
30545     
30546     getAutoCreate : function(){
30547         
30548         var cls = "";
30549         if (!this.allowBlank) {
30550             cls  = "visible";
30551         }
30552         
30553         var cfg = {
30554             tag : this.tag,
30555             cls : 'roo-bootstrap-field-label ' + this.cls,
30556             for : this.target,
30557             cn : [
30558                 {
30559                     tag : 'i',
30560                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30561                     tooltip : this.iconTooltip
30562                 },
30563                 {
30564                     tag : 'span',
30565                     html : this.html
30566                 }
30567             ] 
30568         };
30569         
30570         if(this.indicatorpos == 'right'){
30571             var cfg = {
30572                 tag : this.tag,
30573                 cls : 'roo-bootstrap-field-label ' + this.cls,
30574                 for : this.target,
30575                 cn : [
30576                     {
30577                         tag : 'span',
30578                         html : this.html
30579                     },
30580                     {
30581                         tag : 'i',
30582                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30583                         tooltip : this.iconTooltip
30584                     }
30585                 ] 
30586             };
30587         }
30588         
30589         return cfg;
30590     },
30591     
30592     initEvents: function() 
30593     {
30594         Roo.bootstrap.Element.superclass.initEvents.call(this);
30595         
30596         this.indicator = this.indicatorEl();
30597         
30598         if(this.indicator){
30599             this.indicator.removeClass('visible');
30600             this.indicator.addClass('invisible');
30601         }
30602         
30603         Roo.bootstrap.FieldLabel.register(this);
30604     },
30605     
30606     indicatorEl : function()
30607     {
30608         var indicator = this.el.select('i.roo-required-indicator',true).first();
30609         
30610         if(!indicator){
30611             return false;
30612         }
30613         
30614         return indicator;
30615         
30616     },
30617     
30618     /**
30619      * Mark this field as valid
30620      */
30621     markValid : function()
30622     {
30623         if(this.indicator){
30624             this.indicator.removeClass('visible');
30625             this.indicator.addClass('invisible');
30626         }
30627         if (Roo.bootstrap.version == 3) {
30628             this.el.removeClass(this.invalidClass);
30629             this.el.addClass(this.validClass);
30630         } else {
30631             this.el.removeClass('is-invalid');
30632             this.el.addClass('is-valid');
30633         }
30634         
30635         
30636         this.fireEvent('valid', this);
30637     },
30638     
30639     /**
30640      * Mark this field as invalid
30641      * @param {String} msg The validation message
30642      */
30643     markInvalid : function(msg)
30644     {
30645         if(this.indicator){
30646             this.indicator.removeClass('invisible');
30647             this.indicator.addClass('visible');
30648         }
30649           if (Roo.bootstrap.version == 3) {
30650             this.el.removeClass(this.validClass);
30651             this.el.addClass(this.invalidClass);
30652         } else {
30653             this.el.removeClass('is-valid');
30654             this.el.addClass('is-invalid');
30655         }
30656         
30657         
30658         this.fireEvent('invalid', this, msg);
30659     }
30660     
30661    
30662 });
30663
30664 Roo.apply(Roo.bootstrap.FieldLabel, {
30665     
30666     groups: {},
30667     
30668      /**
30669     * register a FieldLabel Group
30670     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30671     */
30672     register : function(label)
30673     {
30674         if(this.groups.hasOwnProperty(label.target)){
30675             return;
30676         }
30677      
30678         this.groups[label.target] = label;
30679         
30680     },
30681     /**
30682     * fetch a FieldLabel Group based on the target
30683     * @param {string} target
30684     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30685     */
30686     get: function(target) {
30687         if (typeof(this.groups[target]) == 'undefined') {
30688             return false;
30689         }
30690         
30691         return this.groups[target] ;
30692     }
30693 });
30694
30695  
30696
30697  /*
30698  * - LGPL
30699  *
30700  * page DateSplitField.
30701  * 
30702  */
30703
30704
30705 /**
30706  * @class Roo.bootstrap.DateSplitField
30707  * @extends Roo.bootstrap.Component
30708  * Bootstrap DateSplitField class
30709  * @cfg {string} fieldLabel - the label associated
30710  * @cfg {Number} labelWidth set the width of label (0-12)
30711  * @cfg {String} labelAlign (top|left)
30712  * @cfg {Boolean} dayAllowBlank (true|false) default false
30713  * @cfg {Boolean} monthAllowBlank (true|false) default false
30714  * @cfg {Boolean} yearAllowBlank (true|false) default false
30715  * @cfg {string} dayPlaceholder 
30716  * @cfg {string} monthPlaceholder
30717  * @cfg {string} yearPlaceholder
30718  * @cfg {string} dayFormat default 'd'
30719  * @cfg {string} monthFormat default 'm'
30720  * @cfg {string} yearFormat default 'Y'
30721  * @cfg {Number} labellg set the width of label (1-12)
30722  * @cfg {Number} labelmd set the width of label (1-12)
30723  * @cfg {Number} labelsm set the width of label (1-12)
30724  * @cfg {Number} labelxs set the width of label (1-12)
30725
30726  *     
30727  * @constructor
30728  * Create a new DateSplitField
30729  * @param {Object} config The config object
30730  */
30731
30732 Roo.bootstrap.DateSplitField = function(config){
30733     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30734     
30735     this.addEvents({
30736         // raw events
30737          /**
30738          * @event years
30739          * getting the data of years
30740          * @param {Roo.bootstrap.DateSplitField} this
30741          * @param {Object} years
30742          */
30743         "years" : true,
30744         /**
30745          * @event days
30746          * getting the data of days
30747          * @param {Roo.bootstrap.DateSplitField} this
30748          * @param {Object} days
30749          */
30750         "days" : true,
30751         /**
30752          * @event invalid
30753          * Fires after the field has been marked as invalid.
30754          * @param {Roo.form.Field} this
30755          * @param {String} msg The validation message
30756          */
30757         invalid : true,
30758        /**
30759          * @event valid
30760          * Fires after the field has been validated with no errors.
30761          * @param {Roo.form.Field} this
30762          */
30763         valid : true
30764     });
30765 };
30766
30767 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30768     
30769     fieldLabel : '',
30770     labelAlign : 'top',
30771     labelWidth : 3,
30772     dayAllowBlank : false,
30773     monthAllowBlank : false,
30774     yearAllowBlank : false,
30775     dayPlaceholder : '',
30776     monthPlaceholder : '',
30777     yearPlaceholder : '',
30778     dayFormat : 'd',
30779     monthFormat : 'm',
30780     yearFormat : 'Y',
30781     isFormField : true,
30782     labellg : 0,
30783     labelmd : 0,
30784     labelsm : 0,
30785     labelxs : 0,
30786     
30787     getAutoCreate : function()
30788     {
30789         var cfg = {
30790             tag : 'div',
30791             cls : 'row roo-date-split-field-group',
30792             cn : [
30793                 {
30794                     tag : 'input',
30795                     type : 'hidden',
30796                     cls : 'form-hidden-field roo-date-split-field-group-value',
30797                     name : this.name
30798                 }
30799             ]
30800         };
30801         
30802         var labelCls = 'col-md-12';
30803         var contentCls = 'col-md-4';
30804         
30805         if(this.fieldLabel){
30806             
30807             var label = {
30808                 tag : 'div',
30809                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30810                 cn : [
30811                     {
30812                         tag : 'label',
30813                         html : this.fieldLabel
30814                     }
30815                 ]
30816             };
30817             
30818             if(this.labelAlign == 'left'){
30819             
30820                 if(this.labelWidth > 12){
30821                     label.style = "width: " + this.labelWidth + 'px';
30822                 }
30823
30824                 if(this.labelWidth < 13 && this.labelmd == 0){
30825                     this.labelmd = this.labelWidth;
30826                 }
30827
30828                 if(this.labellg > 0){
30829                     labelCls = ' col-lg-' + this.labellg;
30830                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30831                 }
30832
30833                 if(this.labelmd > 0){
30834                     labelCls = ' col-md-' + this.labelmd;
30835                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30836                 }
30837
30838                 if(this.labelsm > 0){
30839                     labelCls = ' col-sm-' + this.labelsm;
30840                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30841                 }
30842
30843                 if(this.labelxs > 0){
30844                     labelCls = ' col-xs-' + this.labelxs;
30845                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30846                 }
30847             }
30848             
30849             label.cls += ' ' + labelCls;
30850             
30851             cfg.cn.push(label);
30852         }
30853         
30854         Roo.each(['day', 'month', 'year'], function(t){
30855             cfg.cn.push({
30856                 tag : 'div',
30857                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30858             });
30859         }, this);
30860         
30861         return cfg;
30862     },
30863     
30864     inputEl: function ()
30865     {
30866         return this.el.select('.roo-date-split-field-group-value', true).first();
30867     },
30868     
30869     onRender : function(ct, position) 
30870     {
30871         var _this = this;
30872         
30873         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30874         
30875         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30876         
30877         this.dayField = new Roo.bootstrap.ComboBox({
30878             allowBlank : this.dayAllowBlank,
30879             alwaysQuery : true,
30880             displayField : 'value',
30881             editable : false,
30882             fieldLabel : '',
30883             forceSelection : true,
30884             mode : 'local',
30885             placeholder : this.dayPlaceholder,
30886             selectOnFocus : true,
30887             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30888             triggerAction : 'all',
30889             typeAhead : true,
30890             valueField : 'value',
30891             store : new Roo.data.SimpleStore({
30892                 data : (function() {    
30893                     var days = [];
30894                     _this.fireEvent('days', _this, days);
30895                     return days;
30896                 })(),
30897                 fields : [ 'value' ]
30898             }),
30899             listeners : {
30900                 select : function (_self, record, index)
30901                 {
30902                     _this.setValue(_this.getValue());
30903                 }
30904             }
30905         });
30906
30907         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30908         
30909         this.monthField = new Roo.bootstrap.MonthField({
30910             after : '<i class=\"fa fa-calendar\"></i>',
30911             allowBlank : this.monthAllowBlank,
30912             placeholder : this.monthPlaceholder,
30913             readOnly : true,
30914             listeners : {
30915                 render : function (_self)
30916                 {
30917                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30918                         e.preventDefault();
30919                         _self.focus();
30920                     });
30921                 },
30922                 select : function (_self, oldvalue, newvalue)
30923                 {
30924                     _this.setValue(_this.getValue());
30925                 }
30926             }
30927         });
30928         
30929         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30930         
30931         this.yearField = new Roo.bootstrap.ComboBox({
30932             allowBlank : this.yearAllowBlank,
30933             alwaysQuery : true,
30934             displayField : 'value',
30935             editable : false,
30936             fieldLabel : '',
30937             forceSelection : true,
30938             mode : 'local',
30939             placeholder : this.yearPlaceholder,
30940             selectOnFocus : true,
30941             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30942             triggerAction : 'all',
30943             typeAhead : true,
30944             valueField : 'value',
30945             store : new Roo.data.SimpleStore({
30946                 data : (function() {
30947                     var years = [];
30948                     _this.fireEvent('years', _this, years);
30949                     return years;
30950                 })(),
30951                 fields : [ 'value' ]
30952             }),
30953             listeners : {
30954                 select : function (_self, record, index)
30955                 {
30956                     _this.setValue(_this.getValue());
30957                 }
30958             }
30959         });
30960
30961         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30962     },
30963     
30964     setValue : function(v, format)
30965     {
30966         this.inputEl.dom.value = v;
30967         
30968         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30969         
30970         var d = Date.parseDate(v, f);
30971         
30972         if(!d){
30973             this.validate();
30974             return;
30975         }
30976         
30977         this.setDay(d.format(this.dayFormat));
30978         this.setMonth(d.format(this.monthFormat));
30979         this.setYear(d.format(this.yearFormat));
30980         
30981         this.validate();
30982         
30983         return;
30984     },
30985     
30986     setDay : function(v)
30987     {
30988         this.dayField.setValue(v);
30989         this.inputEl.dom.value = this.getValue();
30990         this.validate();
30991         return;
30992     },
30993     
30994     setMonth : function(v)
30995     {
30996         this.monthField.setValue(v, true);
30997         this.inputEl.dom.value = this.getValue();
30998         this.validate();
30999         return;
31000     },
31001     
31002     setYear : function(v)
31003     {
31004         this.yearField.setValue(v);
31005         this.inputEl.dom.value = this.getValue();
31006         this.validate();
31007         return;
31008     },
31009     
31010     getDay : function()
31011     {
31012         return this.dayField.getValue();
31013     },
31014     
31015     getMonth : function()
31016     {
31017         return this.monthField.getValue();
31018     },
31019     
31020     getYear : function()
31021     {
31022         return this.yearField.getValue();
31023     },
31024     
31025     getValue : function()
31026     {
31027         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31028         
31029         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31030         
31031         return date;
31032     },
31033     
31034     reset : function()
31035     {
31036         this.setDay('');
31037         this.setMonth('');
31038         this.setYear('');
31039         this.inputEl.dom.value = '';
31040         this.validate();
31041         return;
31042     },
31043     
31044     validate : function()
31045     {
31046         var d = this.dayField.validate();
31047         var m = this.monthField.validate();
31048         var y = this.yearField.validate();
31049         
31050         var valid = true;
31051         
31052         if(
31053                 (!this.dayAllowBlank && !d) ||
31054                 (!this.monthAllowBlank && !m) ||
31055                 (!this.yearAllowBlank && !y)
31056         ){
31057             valid = false;
31058         }
31059         
31060         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31061             return valid;
31062         }
31063         
31064         if(valid){
31065             this.markValid();
31066             return valid;
31067         }
31068         
31069         this.markInvalid();
31070         
31071         return valid;
31072     },
31073     
31074     markValid : function()
31075     {
31076         
31077         var label = this.el.select('label', true).first();
31078         var icon = this.el.select('i.fa-star', true).first();
31079
31080         if(label && icon){
31081             icon.remove();
31082         }
31083         
31084         this.fireEvent('valid', this);
31085     },
31086     
31087      /**
31088      * Mark this field as invalid
31089      * @param {String} msg The validation message
31090      */
31091     markInvalid : function(msg)
31092     {
31093         
31094         var label = this.el.select('label', true).first();
31095         var icon = this.el.select('i.fa-star', true).first();
31096
31097         if(label && !icon){
31098             this.el.select('.roo-date-split-field-label', true).createChild({
31099                 tag : 'i',
31100                 cls : 'text-danger fa fa-lg fa-star',
31101                 tooltip : 'This field is required',
31102                 style : 'margin-right:5px;'
31103             }, label, true);
31104         }
31105         
31106         this.fireEvent('invalid', this, msg);
31107     },
31108     
31109     clearInvalid : function()
31110     {
31111         var label = this.el.select('label', true).first();
31112         var icon = this.el.select('i.fa-star', true).first();
31113
31114         if(label && icon){
31115             icon.remove();
31116         }
31117         
31118         this.fireEvent('valid', this);
31119     },
31120     
31121     getName: function()
31122     {
31123         return this.name;
31124     }
31125     
31126 });
31127
31128  /**
31129  *
31130  * This is based on 
31131  * http://masonry.desandro.com
31132  *
31133  * The idea is to render all the bricks based on vertical width...
31134  *
31135  * The original code extends 'outlayer' - we might need to use that....
31136  * 
31137  */
31138
31139
31140 /**
31141  * @class Roo.bootstrap.LayoutMasonry
31142  * @extends Roo.bootstrap.Component
31143  * Bootstrap Layout Masonry class
31144  * 
31145  * @constructor
31146  * Create a new Element
31147  * @param {Object} config The config object
31148  */
31149
31150 Roo.bootstrap.LayoutMasonry = function(config){
31151     
31152     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31153     
31154     this.bricks = [];
31155     
31156     Roo.bootstrap.LayoutMasonry.register(this);
31157     
31158     this.addEvents({
31159         // raw events
31160         /**
31161          * @event layout
31162          * Fire after layout the items
31163          * @param {Roo.bootstrap.LayoutMasonry} this
31164          * @param {Roo.EventObject} e
31165          */
31166         "layout" : true
31167     });
31168     
31169 };
31170
31171 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31172     
31173     /**
31174      * @cfg {Boolean} isLayoutInstant = no animation?
31175      */   
31176     isLayoutInstant : false, // needed?
31177    
31178     /**
31179      * @cfg {Number} boxWidth  width of the columns
31180      */   
31181     boxWidth : 450,
31182     
31183       /**
31184      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31185      */   
31186     boxHeight : 0,
31187     
31188     /**
31189      * @cfg {Number} padWidth padding below box..
31190      */   
31191     padWidth : 10, 
31192     
31193     /**
31194      * @cfg {Number} gutter gutter width..
31195      */   
31196     gutter : 10,
31197     
31198      /**
31199      * @cfg {Number} maxCols maximum number of columns
31200      */   
31201     
31202     maxCols: 0,
31203     
31204     /**
31205      * @cfg {Boolean} isAutoInitial defalut true
31206      */   
31207     isAutoInitial : true, 
31208     
31209     containerWidth: 0,
31210     
31211     /**
31212      * @cfg {Boolean} isHorizontal defalut false
31213      */   
31214     isHorizontal : false, 
31215
31216     currentSize : null,
31217     
31218     tag: 'div',
31219     
31220     cls: '',
31221     
31222     bricks: null, //CompositeElement
31223     
31224     cols : 1,
31225     
31226     _isLayoutInited : false,
31227     
31228 //    isAlternative : false, // only use for vertical layout...
31229     
31230     /**
31231      * @cfg {Number} alternativePadWidth padding below box..
31232      */   
31233     alternativePadWidth : 50,
31234     
31235     selectedBrick : [],
31236     
31237     getAutoCreate : function(){
31238         
31239         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31240         
31241         var cfg = {
31242             tag: this.tag,
31243             cls: 'blog-masonary-wrapper ' + this.cls,
31244             cn : {
31245                 cls : 'mas-boxes masonary'
31246             }
31247         };
31248         
31249         return cfg;
31250     },
31251     
31252     getChildContainer: function( )
31253     {
31254         if (this.boxesEl) {
31255             return this.boxesEl;
31256         }
31257         
31258         this.boxesEl = this.el.select('.mas-boxes').first();
31259         
31260         return this.boxesEl;
31261     },
31262     
31263     
31264     initEvents : function()
31265     {
31266         var _this = this;
31267         
31268         if(this.isAutoInitial){
31269             Roo.log('hook children rendered');
31270             this.on('childrenrendered', function() {
31271                 Roo.log('children rendered');
31272                 _this.initial();
31273             } ,this);
31274         }
31275     },
31276     
31277     initial : function()
31278     {
31279         this.selectedBrick = [];
31280         
31281         this.currentSize = this.el.getBox(true);
31282         
31283         Roo.EventManager.onWindowResize(this.resize, this); 
31284
31285         if(!this.isAutoInitial){
31286             this.layout();
31287             return;
31288         }
31289         
31290         this.layout();
31291         
31292         return;
31293         //this.layout.defer(500,this);
31294         
31295     },
31296     
31297     resize : function()
31298     {
31299         var cs = this.el.getBox(true);
31300         
31301         if (
31302                 this.currentSize.width == cs.width && 
31303                 this.currentSize.x == cs.x && 
31304                 this.currentSize.height == cs.height && 
31305                 this.currentSize.y == cs.y 
31306         ) {
31307             Roo.log("no change in with or X or Y");
31308             return;
31309         }
31310         
31311         this.currentSize = cs;
31312         
31313         this.layout();
31314         
31315     },
31316     
31317     layout : function()
31318     {   
31319         this._resetLayout();
31320         
31321         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31322         
31323         this.layoutItems( isInstant );
31324       
31325         this._isLayoutInited = true;
31326         
31327         this.fireEvent('layout', this);
31328         
31329     },
31330     
31331     _resetLayout : function()
31332     {
31333         if(this.isHorizontal){
31334             this.horizontalMeasureColumns();
31335             return;
31336         }
31337         
31338         this.verticalMeasureColumns();
31339         
31340     },
31341     
31342     verticalMeasureColumns : function()
31343     {
31344         this.getContainerWidth();
31345         
31346 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31347 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31348 //            return;
31349 //        }
31350         
31351         var boxWidth = this.boxWidth + this.padWidth;
31352         
31353         if(this.containerWidth < this.boxWidth){
31354             boxWidth = this.containerWidth
31355         }
31356         
31357         var containerWidth = this.containerWidth;
31358         
31359         var cols = Math.floor(containerWidth / boxWidth);
31360         
31361         this.cols = Math.max( cols, 1 );
31362         
31363         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31364         
31365         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31366         
31367         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31368         
31369         this.colWidth = boxWidth + avail - this.padWidth;
31370         
31371         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31372         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31373     },
31374     
31375     horizontalMeasureColumns : function()
31376     {
31377         this.getContainerWidth();
31378         
31379         var boxWidth = this.boxWidth;
31380         
31381         if(this.containerWidth < boxWidth){
31382             boxWidth = this.containerWidth;
31383         }
31384         
31385         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31386         
31387         this.el.setHeight(boxWidth);
31388         
31389     },
31390     
31391     getContainerWidth : function()
31392     {
31393         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31394     },
31395     
31396     layoutItems : function( isInstant )
31397     {
31398         Roo.log(this.bricks);
31399         
31400         var items = Roo.apply([], this.bricks);
31401         
31402         if(this.isHorizontal){
31403             this._horizontalLayoutItems( items , isInstant );
31404             return;
31405         }
31406         
31407 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31408 //            this._verticalAlternativeLayoutItems( items , isInstant );
31409 //            return;
31410 //        }
31411         
31412         this._verticalLayoutItems( items , isInstant );
31413         
31414     },
31415     
31416     _verticalLayoutItems : function ( items , isInstant)
31417     {
31418         if ( !items || !items.length ) {
31419             return;
31420         }
31421         
31422         var standard = [
31423             ['xs', 'xs', 'xs', 'tall'],
31424             ['xs', 'xs', 'tall'],
31425             ['xs', 'xs', 'sm'],
31426             ['xs', 'xs', 'xs'],
31427             ['xs', 'tall'],
31428             ['xs', 'sm'],
31429             ['xs', 'xs'],
31430             ['xs'],
31431             
31432             ['sm', 'xs', 'xs'],
31433             ['sm', 'xs'],
31434             ['sm'],
31435             
31436             ['tall', 'xs', 'xs', 'xs'],
31437             ['tall', 'xs', 'xs'],
31438             ['tall', 'xs'],
31439             ['tall']
31440             
31441         ];
31442         
31443         var queue = [];
31444         
31445         var boxes = [];
31446         
31447         var box = [];
31448         
31449         Roo.each(items, function(item, k){
31450             
31451             switch (item.size) {
31452                 // these layouts take up a full box,
31453                 case 'md' :
31454                 case 'md-left' :
31455                 case 'md-right' :
31456                 case 'wide' :
31457                     
31458                     if(box.length){
31459                         boxes.push(box);
31460                         box = [];
31461                     }
31462                     
31463                     boxes.push([item]);
31464                     
31465                     break;
31466                     
31467                 case 'xs' :
31468                 case 'sm' :
31469                 case 'tall' :
31470                     
31471                     box.push(item);
31472                     
31473                     break;
31474                 default :
31475                     break;
31476                     
31477             }
31478             
31479         }, this);
31480         
31481         if(box.length){
31482             boxes.push(box);
31483             box = [];
31484         }
31485         
31486         var filterPattern = function(box, length)
31487         {
31488             if(!box.length){
31489                 return;
31490             }
31491             
31492             var match = false;
31493             
31494             var pattern = box.slice(0, length);
31495             
31496             var format = [];
31497             
31498             Roo.each(pattern, function(i){
31499                 format.push(i.size);
31500             }, this);
31501             
31502             Roo.each(standard, function(s){
31503                 
31504                 if(String(s) != String(format)){
31505                     return;
31506                 }
31507                 
31508                 match = true;
31509                 return false;
31510                 
31511             }, this);
31512             
31513             if(!match && length == 1){
31514                 return;
31515             }
31516             
31517             if(!match){
31518                 filterPattern(box, length - 1);
31519                 return;
31520             }
31521                 
31522             queue.push(pattern);
31523
31524             box = box.slice(length, box.length);
31525
31526             filterPattern(box, 4);
31527
31528             return;
31529             
31530         }
31531         
31532         Roo.each(boxes, function(box, k){
31533             
31534             if(!box.length){
31535                 return;
31536             }
31537             
31538             if(box.length == 1){
31539                 queue.push(box);
31540                 return;
31541             }
31542             
31543             filterPattern(box, 4);
31544             
31545         }, this);
31546         
31547         this._processVerticalLayoutQueue( queue, isInstant );
31548         
31549     },
31550     
31551 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31552 //    {
31553 //        if ( !items || !items.length ) {
31554 //            return;
31555 //        }
31556 //
31557 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31558 //        
31559 //    },
31560     
31561     _horizontalLayoutItems : function ( items , isInstant)
31562     {
31563         if ( !items || !items.length || items.length < 3) {
31564             return;
31565         }
31566         
31567         items.reverse();
31568         
31569         var eItems = items.slice(0, 3);
31570         
31571         items = items.slice(3, items.length);
31572         
31573         var standard = [
31574             ['xs', 'xs', 'xs', 'wide'],
31575             ['xs', 'xs', 'wide'],
31576             ['xs', 'xs', 'sm'],
31577             ['xs', 'xs', 'xs'],
31578             ['xs', 'wide'],
31579             ['xs', 'sm'],
31580             ['xs', 'xs'],
31581             ['xs'],
31582             
31583             ['sm', 'xs', 'xs'],
31584             ['sm', 'xs'],
31585             ['sm'],
31586             
31587             ['wide', 'xs', 'xs', 'xs'],
31588             ['wide', 'xs', 'xs'],
31589             ['wide', 'xs'],
31590             ['wide'],
31591             
31592             ['wide-thin']
31593         ];
31594         
31595         var queue = [];
31596         
31597         var boxes = [];
31598         
31599         var box = [];
31600         
31601         Roo.each(items, function(item, k){
31602             
31603             switch (item.size) {
31604                 case 'md' :
31605                 case 'md-left' :
31606                 case 'md-right' :
31607                 case 'tall' :
31608                     
31609                     if(box.length){
31610                         boxes.push(box);
31611                         box = [];
31612                     }
31613                     
31614                     boxes.push([item]);
31615                     
31616                     break;
31617                     
31618                 case 'xs' :
31619                 case 'sm' :
31620                 case 'wide' :
31621                 case 'wide-thin' :
31622                     
31623                     box.push(item);
31624                     
31625                     break;
31626                 default :
31627                     break;
31628                     
31629             }
31630             
31631         }, this);
31632         
31633         if(box.length){
31634             boxes.push(box);
31635             box = [];
31636         }
31637         
31638         var filterPattern = function(box, length)
31639         {
31640             if(!box.length){
31641                 return;
31642             }
31643             
31644             var match = false;
31645             
31646             var pattern = box.slice(0, length);
31647             
31648             var format = [];
31649             
31650             Roo.each(pattern, function(i){
31651                 format.push(i.size);
31652             }, this);
31653             
31654             Roo.each(standard, function(s){
31655                 
31656                 if(String(s) != String(format)){
31657                     return;
31658                 }
31659                 
31660                 match = true;
31661                 return false;
31662                 
31663             }, this);
31664             
31665             if(!match && length == 1){
31666                 return;
31667             }
31668             
31669             if(!match){
31670                 filterPattern(box, length - 1);
31671                 return;
31672             }
31673                 
31674             queue.push(pattern);
31675
31676             box = box.slice(length, box.length);
31677
31678             filterPattern(box, 4);
31679
31680             return;
31681             
31682         }
31683         
31684         Roo.each(boxes, function(box, k){
31685             
31686             if(!box.length){
31687                 return;
31688             }
31689             
31690             if(box.length == 1){
31691                 queue.push(box);
31692                 return;
31693             }
31694             
31695             filterPattern(box, 4);
31696             
31697         }, this);
31698         
31699         
31700         var prune = [];
31701         
31702         var pos = this.el.getBox(true);
31703         
31704         var minX = pos.x;
31705         
31706         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31707         
31708         var hit_end = false;
31709         
31710         Roo.each(queue, function(box){
31711             
31712             if(hit_end){
31713                 
31714                 Roo.each(box, function(b){
31715                 
31716                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31717                     b.el.hide();
31718
31719                 }, this);
31720
31721                 return;
31722             }
31723             
31724             var mx = 0;
31725             
31726             Roo.each(box, function(b){
31727                 
31728                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31729                 b.el.show();
31730
31731                 mx = Math.max(mx, b.x);
31732                 
31733             }, this);
31734             
31735             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31736             
31737             if(maxX < minX){
31738                 
31739                 Roo.each(box, function(b){
31740                 
31741                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31742                     b.el.hide();
31743                     
31744                 }, this);
31745                 
31746                 hit_end = true;
31747                 
31748                 return;
31749             }
31750             
31751             prune.push(box);
31752             
31753         }, this);
31754         
31755         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31756     },
31757     
31758     /** Sets position of item in DOM
31759     * @param {Element} item
31760     * @param {Number} x - horizontal position
31761     * @param {Number} y - vertical position
31762     * @param {Boolean} isInstant - disables transitions
31763     */
31764     _processVerticalLayoutQueue : function( queue, isInstant )
31765     {
31766         var pos = this.el.getBox(true);
31767         var x = pos.x;
31768         var y = pos.y;
31769         var maxY = [];
31770         
31771         for (var i = 0; i < this.cols; i++){
31772             maxY[i] = pos.y;
31773         }
31774         
31775         Roo.each(queue, function(box, k){
31776             
31777             var col = k % this.cols;
31778             
31779             Roo.each(box, function(b,kk){
31780                 
31781                 b.el.position('absolute');
31782                 
31783                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31784                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31785                 
31786                 if(b.size == 'md-left' || b.size == 'md-right'){
31787                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31788                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31789                 }
31790                 
31791                 b.el.setWidth(width);
31792                 b.el.setHeight(height);
31793                 // iframe?
31794                 b.el.select('iframe',true).setSize(width,height);
31795                 
31796             }, this);
31797             
31798             for (var i = 0; i < this.cols; i++){
31799                 
31800                 if(maxY[i] < maxY[col]){
31801                     col = i;
31802                     continue;
31803                 }
31804                 
31805                 col = Math.min(col, i);
31806                 
31807             }
31808             
31809             x = pos.x + col * (this.colWidth + this.padWidth);
31810             
31811             y = maxY[col];
31812             
31813             var positions = [];
31814             
31815             switch (box.length){
31816                 case 1 :
31817                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31818                     break;
31819                 case 2 :
31820                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31821                     break;
31822                 case 3 :
31823                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31824                     break;
31825                 case 4 :
31826                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31827                     break;
31828                 default :
31829                     break;
31830             }
31831             
31832             Roo.each(box, function(b,kk){
31833                 
31834                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31835                 
31836                 var sz = b.el.getSize();
31837                 
31838                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31839                 
31840             }, this);
31841             
31842         }, this);
31843         
31844         var mY = 0;
31845         
31846         for (var i = 0; i < this.cols; i++){
31847             mY = Math.max(mY, maxY[i]);
31848         }
31849         
31850         this.el.setHeight(mY - pos.y);
31851         
31852     },
31853     
31854 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31855 //    {
31856 //        var pos = this.el.getBox(true);
31857 //        var x = pos.x;
31858 //        var y = pos.y;
31859 //        var maxX = pos.right;
31860 //        
31861 //        var maxHeight = 0;
31862 //        
31863 //        Roo.each(items, function(item, k){
31864 //            
31865 //            var c = k % 2;
31866 //            
31867 //            item.el.position('absolute');
31868 //                
31869 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31870 //
31871 //            item.el.setWidth(width);
31872 //
31873 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31874 //
31875 //            item.el.setHeight(height);
31876 //            
31877 //            if(c == 0){
31878 //                item.el.setXY([x, y], isInstant ? false : true);
31879 //            } else {
31880 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31881 //            }
31882 //            
31883 //            y = y + height + this.alternativePadWidth;
31884 //            
31885 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31886 //            
31887 //        }, this);
31888 //        
31889 //        this.el.setHeight(maxHeight);
31890 //        
31891 //    },
31892     
31893     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31894     {
31895         var pos = this.el.getBox(true);
31896         
31897         var minX = pos.x;
31898         var minY = pos.y;
31899         
31900         var maxX = pos.right;
31901         
31902         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31903         
31904         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31905         
31906         Roo.each(queue, function(box, k){
31907             
31908             Roo.each(box, function(b, kk){
31909                 
31910                 b.el.position('absolute');
31911                 
31912                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31913                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31914                 
31915                 if(b.size == 'md-left' || b.size == 'md-right'){
31916                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31917                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31918                 }
31919                 
31920                 b.el.setWidth(width);
31921                 b.el.setHeight(height);
31922                 
31923             }, this);
31924             
31925             if(!box.length){
31926                 return;
31927             }
31928             
31929             var positions = [];
31930             
31931             switch (box.length){
31932                 case 1 :
31933                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31934                     break;
31935                 case 2 :
31936                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31937                     break;
31938                 case 3 :
31939                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31940                     break;
31941                 case 4 :
31942                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31943                     break;
31944                 default :
31945                     break;
31946             }
31947             
31948             Roo.each(box, function(b,kk){
31949                 
31950                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31951                 
31952                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31953                 
31954             }, this);
31955             
31956         }, this);
31957         
31958     },
31959     
31960     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31961     {
31962         Roo.each(eItems, function(b,k){
31963             
31964             b.size = (k == 0) ? 'sm' : 'xs';
31965             b.x = (k == 0) ? 2 : 1;
31966             b.y = (k == 0) ? 2 : 1;
31967             
31968             b.el.position('absolute');
31969             
31970             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31971                 
31972             b.el.setWidth(width);
31973             
31974             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31975             
31976             b.el.setHeight(height);
31977             
31978         }, this);
31979
31980         var positions = [];
31981         
31982         positions.push({
31983             x : maxX - this.unitWidth * 2 - this.gutter,
31984             y : minY
31985         });
31986         
31987         positions.push({
31988             x : maxX - this.unitWidth,
31989             y : minY + (this.unitWidth + this.gutter) * 2
31990         });
31991         
31992         positions.push({
31993             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31994             y : minY
31995         });
31996         
31997         Roo.each(eItems, function(b,k){
31998             
31999             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32000
32001         }, this);
32002         
32003     },
32004     
32005     getVerticalOneBoxColPositions : function(x, y, box)
32006     {
32007         var pos = [];
32008         
32009         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32010         
32011         if(box[0].size == 'md-left'){
32012             rand = 0;
32013         }
32014         
32015         if(box[0].size == 'md-right'){
32016             rand = 1;
32017         }
32018         
32019         pos.push({
32020             x : x + (this.unitWidth + this.gutter) * rand,
32021             y : y
32022         });
32023         
32024         return pos;
32025     },
32026     
32027     getVerticalTwoBoxColPositions : function(x, y, box)
32028     {
32029         var pos = [];
32030         
32031         if(box[0].size == 'xs'){
32032             
32033             pos.push({
32034                 x : x,
32035                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32036             });
32037
32038             pos.push({
32039                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32040                 y : y
32041             });
32042             
32043             return pos;
32044             
32045         }
32046         
32047         pos.push({
32048             x : x,
32049             y : y
32050         });
32051
32052         pos.push({
32053             x : x + (this.unitWidth + this.gutter) * 2,
32054             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32055         });
32056         
32057         return pos;
32058         
32059     },
32060     
32061     getVerticalThreeBoxColPositions : function(x, y, box)
32062     {
32063         var pos = [];
32064         
32065         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32066             
32067             pos.push({
32068                 x : x,
32069                 y : y
32070             });
32071
32072             pos.push({
32073                 x : x + (this.unitWidth + this.gutter) * 1,
32074                 y : y
32075             });
32076             
32077             pos.push({
32078                 x : x + (this.unitWidth + this.gutter) * 2,
32079                 y : y
32080             });
32081             
32082             return pos;
32083             
32084         }
32085         
32086         if(box[0].size == 'xs' && box[1].size == 'xs'){
32087             
32088             pos.push({
32089                 x : x,
32090                 y : y
32091             });
32092
32093             pos.push({
32094                 x : x,
32095                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32096             });
32097             
32098             pos.push({
32099                 x : x + (this.unitWidth + this.gutter) * 1,
32100                 y : y
32101             });
32102             
32103             return pos;
32104             
32105         }
32106         
32107         pos.push({
32108             x : x,
32109             y : y
32110         });
32111
32112         pos.push({
32113             x : x + (this.unitWidth + this.gutter) * 2,
32114             y : y
32115         });
32116
32117         pos.push({
32118             x : x + (this.unitWidth + this.gutter) * 2,
32119             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32120         });
32121             
32122         return pos;
32123         
32124     },
32125     
32126     getVerticalFourBoxColPositions : function(x, y, box)
32127     {
32128         var pos = [];
32129         
32130         if(box[0].size == 'xs'){
32131             
32132             pos.push({
32133                 x : x,
32134                 y : y
32135             });
32136
32137             pos.push({
32138                 x : x,
32139                 y : y + (this.unitHeight + this.gutter) * 1
32140             });
32141             
32142             pos.push({
32143                 x : x,
32144                 y : y + (this.unitHeight + this.gutter) * 2
32145             });
32146             
32147             pos.push({
32148                 x : x + (this.unitWidth + this.gutter) * 1,
32149                 y : y
32150             });
32151             
32152             return pos;
32153             
32154         }
32155         
32156         pos.push({
32157             x : x,
32158             y : y
32159         });
32160
32161         pos.push({
32162             x : x + (this.unitWidth + this.gutter) * 2,
32163             y : y
32164         });
32165
32166         pos.push({
32167             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32168             y : y + (this.unitHeight + this.gutter) * 1
32169         });
32170
32171         pos.push({
32172             x : x + (this.unitWidth + this.gutter) * 2,
32173             y : y + (this.unitWidth + this.gutter) * 2
32174         });
32175
32176         return pos;
32177         
32178     },
32179     
32180     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32181     {
32182         var pos = [];
32183         
32184         if(box[0].size == 'md-left'){
32185             pos.push({
32186                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32187                 y : minY
32188             });
32189             
32190             return pos;
32191         }
32192         
32193         if(box[0].size == 'md-right'){
32194             pos.push({
32195                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32196                 y : minY + (this.unitWidth + this.gutter) * 1
32197             });
32198             
32199             return pos;
32200         }
32201         
32202         var rand = Math.floor(Math.random() * (4 - box[0].y));
32203         
32204         pos.push({
32205             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32206             y : minY + (this.unitWidth + this.gutter) * rand
32207         });
32208         
32209         return pos;
32210         
32211     },
32212     
32213     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32214     {
32215         var pos = [];
32216         
32217         if(box[0].size == 'xs'){
32218             
32219             pos.push({
32220                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32221                 y : minY
32222             });
32223
32224             pos.push({
32225                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32226                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32227             });
32228             
32229             return pos;
32230             
32231         }
32232         
32233         pos.push({
32234             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32235             y : minY
32236         });
32237
32238         pos.push({
32239             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32240             y : minY + (this.unitWidth + this.gutter) * 2
32241         });
32242         
32243         return pos;
32244         
32245     },
32246     
32247     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32248     {
32249         var pos = [];
32250         
32251         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32252             
32253             pos.push({
32254                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32255                 y : minY
32256             });
32257
32258             pos.push({
32259                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32260                 y : minY + (this.unitWidth + this.gutter) * 1
32261             });
32262             
32263             pos.push({
32264                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32265                 y : minY + (this.unitWidth + this.gutter) * 2
32266             });
32267             
32268             return pos;
32269             
32270         }
32271         
32272         if(box[0].size == 'xs' && box[1].size == 'xs'){
32273             
32274             pos.push({
32275                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32276                 y : minY
32277             });
32278
32279             pos.push({
32280                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32281                 y : minY
32282             });
32283             
32284             pos.push({
32285                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32286                 y : minY + (this.unitWidth + this.gutter) * 1
32287             });
32288             
32289             return pos;
32290             
32291         }
32292         
32293         pos.push({
32294             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32295             y : minY
32296         });
32297
32298         pos.push({
32299             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32300             y : minY + (this.unitWidth + this.gutter) * 2
32301         });
32302
32303         pos.push({
32304             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32305             y : minY + (this.unitWidth + this.gutter) * 2
32306         });
32307             
32308         return pos;
32309         
32310     },
32311     
32312     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32313     {
32314         var pos = [];
32315         
32316         if(box[0].size == 'xs'){
32317             
32318             pos.push({
32319                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32320                 y : minY
32321             });
32322
32323             pos.push({
32324                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32325                 y : minY
32326             });
32327             
32328             pos.push({
32329                 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),
32330                 y : minY
32331             });
32332             
32333             pos.push({
32334                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32335                 y : minY + (this.unitWidth + this.gutter) * 1
32336             });
32337             
32338             return pos;
32339             
32340         }
32341         
32342         pos.push({
32343             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32344             y : minY
32345         });
32346         
32347         pos.push({
32348             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32349             y : minY + (this.unitWidth + this.gutter) * 2
32350         });
32351         
32352         pos.push({
32353             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32354             y : minY + (this.unitWidth + this.gutter) * 2
32355         });
32356         
32357         pos.push({
32358             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),
32359             y : minY + (this.unitWidth + this.gutter) * 2
32360         });
32361
32362         return pos;
32363         
32364     },
32365     
32366     /**
32367     * remove a Masonry Brick
32368     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32369     */
32370     removeBrick : function(brick_id)
32371     {
32372         if (!brick_id) {
32373             return;
32374         }
32375         
32376         for (var i = 0; i<this.bricks.length; i++) {
32377             if (this.bricks[i].id == brick_id) {
32378                 this.bricks.splice(i,1);
32379                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32380                 this.initial();
32381             }
32382         }
32383     },
32384     
32385     /**
32386     * adds a Masonry Brick
32387     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32388     */
32389     addBrick : function(cfg)
32390     {
32391         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32392         //this.register(cn);
32393         cn.parentId = this.id;
32394         cn.render(this.el);
32395         return cn;
32396     },
32397     
32398     /**
32399     * register a Masonry Brick
32400     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32401     */
32402     
32403     register : function(brick)
32404     {
32405         this.bricks.push(brick);
32406         brick.masonryId = this.id;
32407     },
32408     
32409     /**
32410     * clear all the Masonry Brick
32411     */
32412     clearAll : function()
32413     {
32414         this.bricks = [];
32415         //this.getChildContainer().dom.innerHTML = "";
32416         this.el.dom.innerHTML = '';
32417     },
32418     
32419     getSelected : function()
32420     {
32421         if (!this.selectedBrick) {
32422             return false;
32423         }
32424         
32425         return this.selectedBrick;
32426     }
32427 });
32428
32429 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32430     
32431     groups: {},
32432      /**
32433     * register a Masonry Layout
32434     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32435     */
32436     
32437     register : function(layout)
32438     {
32439         this.groups[layout.id] = layout;
32440     },
32441     /**
32442     * fetch a  Masonry Layout based on the masonry layout ID
32443     * @param {string} the masonry layout to add
32444     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32445     */
32446     
32447     get: function(layout_id) {
32448         if (typeof(this.groups[layout_id]) == 'undefined') {
32449             return false;
32450         }
32451         return this.groups[layout_id] ;
32452     }
32453     
32454     
32455     
32456 });
32457
32458  
32459
32460  /**
32461  *
32462  * This is based on 
32463  * http://masonry.desandro.com
32464  *
32465  * The idea is to render all the bricks based on vertical width...
32466  *
32467  * The original code extends 'outlayer' - we might need to use that....
32468  * 
32469  */
32470
32471
32472 /**
32473  * @class Roo.bootstrap.LayoutMasonryAuto
32474  * @extends Roo.bootstrap.Component
32475  * Bootstrap Layout Masonry class
32476  * 
32477  * @constructor
32478  * Create a new Element
32479  * @param {Object} config The config object
32480  */
32481
32482 Roo.bootstrap.LayoutMasonryAuto = function(config){
32483     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32484 };
32485
32486 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32487     
32488       /**
32489      * @cfg {Boolean} isFitWidth  - resize the width..
32490      */   
32491     isFitWidth : false,  // options..
32492     /**
32493      * @cfg {Boolean} isOriginLeft = left align?
32494      */   
32495     isOriginLeft : true,
32496     /**
32497      * @cfg {Boolean} isOriginTop = top align?
32498      */   
32499     isOriginTop : false,
32500     /**
32501      * @cfg {Boolean} isLayoutInstant = no animation?
32502      */   
32503     isLayoutInstant : false, // needed?
32504     /**
32505      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32506      */   
32507     isResizingContainer : true,
32508     /**
32509      * @cfg {Number} columnWidth  width of the columns 
32510      */   
32511     
32512     columnWidth : 0,
32513     
32514     /**
32515      * @cfg {Number} maxCols maximum number of columns
32516      */   
32517     
32518     maxCols: 0,
32519     /**
32520      * @cfg {Number} padHeight padding below box..
32521      */   
32522     
32523     padHeight : 10, 
32524     
32525     /**
32526      * @cfg {Boolean} isAutoInitial defalut true
32527      */   
32528     
32529     isAutoInitial : true, 
32530     
32531     // private?
32532     gutter : 0,
32533     
32534     containerWidth: 0,
32535     initialColumnWidth : 0,
32536     currentSize : null,
32537     
32538     colYs : null, // array.
32539     maxY : 0,
32540     padWidth: 10,
32541     
32542     
32543     tag: 'div',
32544     cls: '',
32545     bricks: null, //CompositeElement
32546     cols : 0, // array?
32547     // element : null, // wrapped now this.el
32548     _isLayoutInited : null, 
32549     
32550     
32551     getAutoCreate : function(){
32552         
32553         var cfg = {
32554             tag: this.tag,
32555             cls: 'blog-masonary-wrapper ' + this.cls,
32556             cn : {
32557                 cls : 'mas-boxes masonary'
32558             }
32559         };
32560         
32561         return cfg;
32562     },
32563     
32564     getChildContainer: function( )
32565     {
32566         if (this.boxesEl) {
32567             return this.boxesEl;
32568         }
32569         
32570         this.boxesEl = this.el.select('.mas-boxes').first();
32571         
32572         return this.boxesEl;
32573     },
32574     
32575     
32576     initEvents : function()
32577     {
32578         var _this = this;
32579         
32580         if(this.isAutoInitial){
32581             Roo.log('hook children rendered');
32582             this.on('childrenrendered', function() {
32583                 Roo.log('children rendered');
32584                 _this.initial();
32585             } ,this);
32586         }
32587         
32588     },
32589     
32590     initial : function()
32591     {
32592         this.reloadItems();
32593
32594         this.currentSize = this.el.getBox(true);
32595
32596         /// was window resize... - let's see if this works..
32597         Roo.EventManager.onWindowResize(this.resize, this); 
32598
32599         if(!this.isAutoInitial){
32600             this.layout();
32601             return;
32602         }
32603         
32604         this.layout.defer(500,this);
32605     },
32606     
32607     reloadItems: function()
32608     {
32609         this.bricks = this.el.select('.masonry-brick', true);
32610         
32611         this.bricks.each(function(b) {
32612             //Roo.log(b.getSize());
32613             if (!b.attr('originalwidth')) {
32614                 b.attr('originalwidth',  b.getSize().width);
32615             }
32616             
32617         });
32618         
32619         Roo.log(this.bricks.elements.length);
32620     },
32621     
32622     resize : function()
32623     {
32624         Roo.log('resize');
32625         var cs = this.el.getBox(true);
32626         
32627         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32628             Roo.log("no change in with or X");
32629             return;
32630         }
32631         this.currentSize = cs;
32632         this.layout();
32633     },
32634     
32635     layout : function()
32636     {
32637          Roo.log('layout');
32638         this._resetLayout();
32639         //this._manageStamps();
32640       
32641         // don't animate first layout
32642         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32643         this.layoutItems( isInstant );
32644       
32645         // flag for initalized
32646         this._isLayoutInited = true;
32647     },
32648     
32649     layoutItems : function( isInstant )
32650     {
32651         //var items = this._getItemsForLayout( this.items );
32652         // original code supports filtering layout items.. we just ignore it..
32653         
32654         this._layoutItems( this.bricks , isInstant );
32655       
32656         this._postLayout();
32657     },
32658     _layoutItems : function ( items , isInstant)
32659     {
32660        //this.fireEvent( 'layout', this, items );
32661     
32662
32663         if ( !items || !items.elements.length ) {
32664           // no items, emit event with empty array
32665             return;
32666         }
32667
32668         var queue = [];
32669         items.each(function(item) {
32670             Roo.log("layout item");
32671             Roo.log(item);
32672             // get x/y object from method
32673             var position = this._getItemLayoutPosition( item );
32674             // enqueue
32675             position.item = item;
32676             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32677             queue.push( position );
32678         }, this);
32679       
32680         this._processLayoutQueue( queue );
32681     },
32682     /** Sets position of item in DOM
32683     * @param {Element} item
32684     * @param {Number} x - horizontal position
32685     * @param {Number} y - vertical position
32686     * @param {Boolean} isInstant - disables transitions
32687     */
32688     _processLayoutQueue : function( queue )
32689     {
32690         for ( var i=0, len = queue.length; i < len; i++ ) {
32691             var obj = queue[i];
32692             obj.item.position('absolute');
32693             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32694         }
32695     },
32696       
32697     
32698     /**
32699     * Any logic you want to do after each layout,
32700     * i.e. size the container
32701     */
32702     _postLayout : function()
32703     {
32704         this.resizeContainer();
32705     },
32706     
32707     resizeContainer : function()
32708     {
32709         if ( !this.isResizingContainer ) {
32710             return;
32711         }
32712         var size = this._getContainerSize();
32713         if ( size ) {
32714             this.el.setSize(size.width,size.height);
32715             this.boxesEl.setSize(size.width,size.height);
32716         }
32717     },
32718     
32719     
32720     
32721     _resetLayout : function()
32722     {
32723         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32724         this.colWidth = this.el.getWidth();
32725         //this.gutter = this.el.getWidth(); 
32726         
32727         this.measureColumns();
32728
32729         // reset column Y
32730         var i = this.cols;
32731         this.colYs = [];
32732         while (i--) {
32733             this.colYs.push( 0 );
32734         }
32735     
32736         this.maxY = 0;
32737     },
32738
32739     measureColumns : function()
32740     {
32741         this.getContainerWidth();
32742       // if columnWidth is 0, default to outerWidth of first item
32743         if ( !this.columnWidth ) {
32744             var firstItem = this.bricks.first();
32745             Roo.log(firstItem);
32746             this.columnWidth  = this.containerWidth;
32747             if (firstItem && firstItem.attr('originalwidth') ) {
32748                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32749             }
32750             // columnWidth fall back to item of first element
32751             Roo.log("set column width?");
32752                         this.initialColumnWidth = this.columnWidth  ;
32753
32754             // if first elem has no width, default to size of container
32755             
32756         }
32757         
32758         
32759         if (this.initialColumnWidth) {
32760             this.columnWidth = this.initialColumnWidth;
32761         }
32762         
32763         
32764             
32765         // column width is fixed at the top - however if container width get's smaller we should
32766         // reduce it...
32767         
32768         // this bit calcs how man columns..
32769             
32770         var columnWidth = this.columnWidth += this.gutter;
32771       
32772         // calculate columns
32773         var containerWidth = this.containerWidth + this.gutter;
32774         
32775         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32776         // fix rounding errors, typically with gutters
32777         var excess = columnWidth - containerWidth % columnWidth;
32778         
32779         
32780         // if overshoot is less than a pixel, round up, otherwise floor it
32781         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32782         cols = Math[ mathMethod ]( cols );
32783         this.cols = Math.max( cols, 1 );
32784         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32785         
32786          // padding positioning..
32787         var totalColWidth = this.cols * this.columnWidth;
32788         var padavail = this.containerWidth - totalColWidth;
32789         // so for 2 columns - we need 3 'pads'
32790         
32791         var padNeeded = (1+this.cols) * this.padWidth;
32792         
32793         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32794         
32795         this.columnWidth += padExtra
32796         //this.padWidth = Math.floor(padavail /  ( this.cols));
32797         
32798         // adjust colum width so that padding is fixed??
32799         
32800         // we have 3 columns ... total = width * 3
32801         // we have X left over... that should be used by 
32802         
32803         //if (this.expandC) {
32804             
32805         //}
32806         
32807         
32808         
32809     },
32810     
32811     getContainerWidth : function()
32812     {
32813        /* // container is parent if fit width
32814         var container = this.isFitWidth ? this.element.parentNode : this.element;
32815         // check that this.size and size are there
32816         // IE8 triggers resize on body size change, so they might not be
32817         
32818         var size = getSize( container );  //FIXME
32819         this.containerWidth = size && size.innerWidth; //FIXME
32820         */
32821          
32822         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32823         
32824     },
32825     
32826     _getItemLayoutPosition : function( item )  // what is item?
32827     {
32828         // we resize the item to our columnWidth..
32829       
32830         item.setWidth(this.columnWidth);
32831         item.autoBoxAdjust  = false;
32832         
32833         var sz = item.getSize();
32834  
32835         // how many columns does this brick span
32836         var remainder = this.containerWidth % this.columnWidth;
32837         
32838         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32839         // round if off by 1 pixel, otherwise use ceil
32840         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32841         colSpan = Math.min( colSpan, this.cols );
32842         
32843         // normally this should be '1' as we dont' currently allow multi width columns..
32844         
32845         var colGroup = this._getColGroup( colSpan );
32846         // get the minimum Y value from the columns
32847         var minimumY = Math.min.apply( Math, colGroup );
32848         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32849         
32850         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32851          
32852         // position the brick
32853         var position = {
32854             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32855             y: this.currentSize.y + minimumY + this.padHeight
32856         };
32857         
32858         Roo.log(position);
32859         // apply setHeight to necessary columns
32860         var setHeight = minimumY + sz.height + this.padHeight;
32861         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32862         
32863         var setSpan = this.cols + 1 - colGroup.length;
32864         for ( var i = 0; i < setSpan; i++ ) {
32865           this.colYs[ shortColIndex + i ] = setHeight ;
32866         }
32867       
32868         return position;
32869     },
32870     
32871     /**
32872      * @param {Number} colSpan - number of columns the element spans
32873      * @returns {Array} colGroup
32874      */
32875     _getColGroup : function( colSpan )
32876     {
32877         if ( colSpan < 2 ) {
32878           // if brick spans only one column, use all the column Ys
32879           return this.colYs;
32880         }
32881       
32882         var colGroup = [];
32883         // how many different places could this brick fit horizontally
32884         var groupCount = this.cols + 1 - colSpan;
32885         // for each group potential horizontal position
32886         for ( var i = 0; i < groupCount; i++ ) {
32887           // make an array of colY values for that one group
32888           var groupColYs = this.colYs.slice( i, i + colSpan );
32889           // and get the max value of the array
32890           colGroup[i] = Math.max.apply( Math, groupColYs );
32891         }
32892         return colGroup;
32893     },
32894     /*
32895     _manageStamp : function( stamp )
32896     {
32897         var stampSize =  stamp.getSize();
32898         var offset = stamp.getBox();
32899         // get the columns that this stamp affects
32900         var firstX = this.isOriginLeft ? offset.x : offset.right;
32901         var lastX = firstX + stampSize.width;
32902         var firstCol = Math.floor( firstX / this.columnWidth );
32903         firstCol = Math.max( 0, firstCol );
32904         
32905         var lastCol = Math.floor( lastX / this.columnWidth );
32906         // lastCol should not go over if multiple of columnWidth #425
32907         lastCol -= lastX % this.columnWidth ? 0 : 1;
32908         lastCol = Math.min( this.cols - 1, lastCol );
32909         
32910         // set colYs to bottom of the stamp
32911         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32912             stampSize.height;
32913             
32914         for ( var i = firstCol; i <= lastCol; i++ ) {
32915           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32916         }
32917     },
32918     */
32919     
32920     _getContainerSize : function()
32921     {
32922         this.maxY = Math.max.apply( Math, this.colYs );
32923         var size = {
32924             height: this.maxY
32925         };
32926       
32927         if ( this.isFitWidth ) {
32928             size.width = this._getContainerFitWidth();
32929         }
32930       
32931         return size;
32932     },
32933     
32934     _getContainerFitWidth : function()
32935     {
32936         var unusedCols = 0;
32937         // count unused columns
32938         var i = this.cols;
32939         while ( --i ) {
32940           if ( this.colYs[i] !== 0 ) {
32941             break;
32942           }
32943           unusedCols++;
32944         }
32945         // fit container to columns that have been used
32946         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32947     },
32948     
32949     needsResizeLayout : function()
32950     {
32951         var previousWidth = this.containerWidth;
32952         this.getContainerWidth();
32953         return previousWidth !== this.containerWidth;
32954     }
32955  
32956 });
32957
32958  
32959
32960  /*
32961  * - LGPL
32962  *
32963  * element
32964  * 
32965  */
32966
32967 /**
32968  * @class Roo.bootstrap.MasonryBrick
32969  * @extends Roo.bootstrap.Component
32970  * Bootstrap MasonryBrick class
32971  * 
32972  * @constructor
32973  * Create a new MasonryBrick
32974  * @param {Object} config The config object
32975  */
32976
32977 Roo.bootstrap.MasonryBrick = function(config){
32978     
32979     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32980     
32981     Roo.bootstrap.MasonryBrick.register(this);
32982     
32983     this.addEvents({
32984         // raw events
32985         /**
32986          * @event click
32987          * When a MasonryBrick is clcik
32988          * @param {Roo.bootstrap.MasonryBrick} this
32989          * @param {Roo.EventObject} e
32990          */
32991         "click" : true
32992     });
32993 };
32994
32995 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32996     
32997     /**
32998      * @cfg {String} title
32999      */   
33000     title : '',
33001     /**
33002      * @cfg {String} html
33003      */   
33004     html : '',
33005     /**
33006      * @cfg {String} bgimage
33007      */   
33008     bgimage : '',
33009     /**
33010      * @cfg {String} videourl
33011      */   
33012     videourl : '',
33013     /**
33014      * @cfg {String} cls
33015      */   
33016     cls : '',
33017     /**
33018      * @cfg {String} href
33019      */   
33020     href : '',
33021     /**
33022      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33023      */   
33024     size : 'xs',
33025     
33026     /**
33027      * @cfg {String} placetitle (center|bottom)
33028      */   
33029     placetitle : '',
33030     
33031     /**
33032      * @cfg {Boolean} isFitContainer defalut true
33033      */   
33034     isFitContainer : true, 
33035     
33036     /**
33037      * @cfg {Boolean} preventDefault defalut false
33038      */   
33039     preventDefault : false, 
33040     
33041     /**
33042      * @cfg {Boolean} inverse defalut false
33043      */   
33044     maskInverse : false, 
33045     
33046     getAutoCreate : function()
33047     {
33048         if(!this.isFitContainer){
33049             return this.getSplitAutoCreate();
33050         }
33051         
33052         var cls = 'masonry-brick masonry-brick-full';
33053         
33054         if(this.href.length){
33055             cls += ' masonry-brick-link';
33056         }
33057         
33058         if(this.bgimage.length){
33059             cls += ' masonry-brick-image';
33060         }
33061         
33062         if(this.maskInverse){
33063             cls += ' mask-inverse';
33064         }
33065         
33066         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33067             cls += ' enable-mask';
33068         }
33069         
33070         if(this.size){
33071             cls += ' masonry-' + this.size + '-brick';
33072         }
33073         
33074         if(this.placetitle.length){
33075             
33076             switch (this.placetitle) {
33077                 case 'center' :
33078                     cls += ' masonry-center-title';
33079                     break;
33080                 case 'bottom' :
33081                     cls += ' masonry-bottom-title';
33082                     break;
33083                 default:
33084                     break;
33085             }
33086             
33087         } else {
33088             if(!this.html.length && !this.bgimage.length){
33089                 cls += ' masonry-center-title';
33090             }
33091
33092             if(!this.html.length && this.bgimage.length){
33093                 cls += ' masonry-bottom-title';
33094             }
33095         }
33096         
33097         if(this.cls){
33098             cls += ' ' + this.cls;
33099         }
33100         
33101         var cfg = {
33102             tag: (this.href.length) ? 'a' : 'div',
33103             cls: cls,
33104             cn: [
33105                 {
33106                     tag: 'div',
33107                     cls: 'masonry-brick-mask'
33108                 },
33109                 {
33110                     tag: 'div',
33111                     cls: 'masonry-brick-paragraph',
33112                     cn: []
33113                 }
33114             ]
33115         };
33116         
33117         if(this.href.length){
33118             cfg.href = this.href;
33119         }
33120         
33121         var cn = cfg.cn[1].cn;
33122         
33123         if(this.title.length){
33124             cn.push({
33125                 tag: 'h4',
33126                 cls: 'masonry-brick-title',
33127                 html: this.title
33128             });
33129         }
33130         
33131         if(this.html.length){
33132             cn.push({
33133                 tag: 'p',
33134                 cls: 'masonry-brick-text',
33135                 html: this.html
33136             });
33137         }
33138         
33139         if (!this.title.length && !this.html.length) {
33140             cfg.cn[1].cls += ' hide';
33141         }
33142         
33143         if(this.bgimage.length){
33144             cfg.cn.push({
33145                 tag: 'img',
33146                 cls: 'masonry-brick-image-view',
33147                 src: this.bgimage
33148             });
33149         }
33150         
33151         if(this.videourl.length){
33152             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33153             // youtube support only?
33154             cfg.cn.push({
33155                 tag: 'iframe',
33156                 cls: 'masonry-brick-image-view',
33157                 src: vurl,
33158                 frameborder : 0,
33159                 allowfullscreen : true
33160             });
33161         }
33162         
33163         return cfg;
33164         
33165     },
33166     
33167     getSplitAutoCreate : function()
33168     {
33169         var cls = 'masonry-brick masonry-brick-split';
33170         
33171         if(this.href.length){
33172             cls += ' masonry-brick-link';
33173         }
33174         
33175         if(this.bgimage.length){
33176             cls += ' masonry-brick-image';
33177         }
33178         
33179         if(this.size){
33180             cls += ' masonry-' + this.size + '-brick';
33181         }
33182         
33183         switch (this.placetitle) {
33184             case 'center' :
33185                 cls += ' masonry-center-title';
33186                 break;
33187             case 'bottom' :
33188                 cls += ' masonry-bottom-title';
33189                 break;
33190             default:
33191                 if(!this.bgimage.length){
33192                     cls += ' masonry-center-title';
33193                 }
33194
33195                 if(this.bgimage.length){
33196                     cls += ' masonry-bottom-title';
33197                 }
33198                 break;
33199         }
33200         
33201         if(this.cls){
33202             cls += ' ' + this.cls;
33203         }
33204         
33205         var cfg = {
33206             tag: (this.href.length) ? 'a' : 'div',
33207             cls: cls,
33208             cn: [
33209                 {
33210                     tag: 'div',
33211                     cls: 'masonry-brick-split-head',
33212                     cn: [
33213                         {
33214                             tag: 'div',
33215                             cls: 'masonry-brick-paragraph',
33216                             cn: []
33217                         }
33218                     ]
33219                 },
33220                 {
33221                     tag: 'div',
33222                     cls: 'masonry-brick-split-body',
33223                     cn: []
33224                 }
33225             ]
33226         };
33227         
33228         if(this.href.length){
33229             cfg.href = this.href;
33230         }
33231         
33232         if(this.title.length){
33233             cfg.cn[0].cn[0].cn.push({
33234                 tag: 'h4',
33235                 cls: 'masonry-brick-title',
33236                 html: this.title
33237             });
33238         }
33239         
33240         if(this.html.length){
33241             cfg.cn[1].cn.push({
33242                 tag: 'p',
33243                 cls: 'masonry-brick-text',
33244                 html: this.html
33245             });
33246         }
33247
33248         if(this.bgimage.length){
33249             cfg.cn[0].cn.push({
33250                 tag: 'img',
33251                 cls: 'masonry-brick-image-view',
33252                 src: this.bgimage
33253             });
33254         }
33255         
33256         if(this.videourl.length){
33257             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33258             // youtube support only?
33259             cfg.cn[0].cn.cn.push({
33260                 tag: 'iframe',
33261                 cls: 'masonry-brick-image-view',
33262                 src: vurl,
33263                 frameborder : 0,
33264                 allowfullscreen : true
33265             });
33266         }
33267         
33268         return cfg;
33269     },
33270     
33271     initEvents: function() 
33272     {
33273         switch (this.size) {
33274             case 'xs' :
33275                 this.x = 1;
33276                 this.y = 1;
33277                 break;
33278             case 'sm' :
33279                 this.x = 2;
33280                 this.y = 2;
33281                 break;
33282             case 'md' :
33283             case 'md-left' :
33284             case 'md-right' :
33285                 this.x = 3;
33286                 this.y = 3;
33287                 break;
33288             case 'tall' :
33289                 this.x = 2;
33290                 this.y = 3;
33291                 break;
33292             case 'wide' :
33293                 this.x = 3;
33294                 this.y = 2;
33295                 break;
33296             case 'wide-thin' :
33297                 this.x = 3;
33298                 this.y = 1;
33299                 break;
33300                         
33301             default :
33302                 break;
33303         }
33304         
33305         if(Roo.isTouch){
33306             this.el.on('touchstart', this.onTouchStart, this);
33307             this.el.on('touchmove', this.onTouchMove, this);
33308             this.el.on('touchend', this.onTouchEnd, this);
33309             this.el.on('contextmenu', this.onContextMenu, this);
33310         } else {
33311             this.el.on('mouseenter'  ,this.enter, this);
33312             this.el.on('mouseleave', this.leave, this);
33313             this.el.on('click', this.onClick, this);
33314         }
33315         
33316         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33317             this.parent().bricks.push(this);   
33318         }
33319         
33320     },
33321     
33322     onClick: function(e, el)
33323     {
33324         var time = this.endTimer - this.startTimer;
33325         // Roo.log(e.preventDefault());
33326         if(Roo.isTouch){
33327             if(time > 1000){
33328                 e.preventDefault();
33329                 return;
33330             }
33331         }
33332         
33333         if(!this.preventDefault){
33334             return;
33335         }
33336         
33337         e.preventDefault();
33338         
33339         if (this.activeClass != '') {
33340             this.selectBrick();
33341         }
33342         
33343         this.fireEvent('click', this, e);
33344     },
33345     
33346     enter: function(e, el)
33347     {
33348         e.preventDefault();
33349         
33350         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33351             return;
33352         }
33353         
33354         if(this.bgimage.length && this.html.length){
33355             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33356         }
33357     },
33358     
33359     leave: function(e, el)
33360     {
33361         e.preventDefault();
33362         
33363         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33364             return;
33365         }
33366         
33367         if(this.bgimage.length && this.html.length){
33368             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33369         }
33370     },
33371     
33372     onTouchStart: function(e, el)
33373     {
33374 //        e.preventDefault();
33375         
33376         this.touchmoved = false;
33377         
33378         if(!this.isFitContainer){
33379             return;
33380         }
33381         
33382         if(!this.bgimage.length || !this.html.length){
33383             return;
33384         }
33385         
33386         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33387         
33388         this.timer = new Date().getTime();
33389         
33390     },
33391     
33392     onTouchMove: function(e, el)
33393     {
33394         this.touchmoved = true;
33395     },
33396     
33397     onContextMenu : function(e,el)
33398     {
33399         e.preventDefault();
33400         e.stopPropagation();
33401         return false;
33402     },
33403     
33404     onTouchEnd: function(e, el)
33405     {
33406 //        e.preventDefault();
33407         
33408         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33409         
33410             this.leave(e,el);
33411             
33412             return;
33413         }
33414         
33415         if(!this.bgimage.length || !this.html.length){
33416             
33417             if(this.href.length){
33418                 window.location.href = this.href;
33419             }
33420             
33421             return;
33422         }
33423         
33424         if(!this.isFitContainer){
33425             return;
33426         }
33427         
33428         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33429         
33430         window.location.href = this.href;
33431     },
33432     
33433     //selection on single brick only
33434     selectBrick : function() {
33435         
33436         if (!this.parentId) {
33437             return;
33438         }
33439         
33440         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33441         var index = m.selectedBrick.indexOf(this.id);
33442         
33443         if ( index > -1) {
33444             m.selectedBrick.splice(index,1);
33445             this.el.removeClass(this.activeClass);
33446             return;
33447         }
33448         
33449         for(var i = 0; i < m.selectedBrick.length; i++) {
33450             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33451             b.el.removeClass(b.activeClass);
33452         }
33453         
33454         m.selectedBrick = [];
33455         
33456         m.selectedBrick.push(this.id);
33457         this.el.addClass(this.activeClass);
33458         return;
33459     },
33460     
33461     isSelected : function(){
33462         return this.el.hasClass(this.activeClass);
33463         
33464     }
33465 });
33466
33467 Roo.apply(Roo.bootstrap.MasonryBrick, {
33468     
33469     //groups: {},
33470     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33471      /**
33472     * register a Masonry Brick
33473     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33474     */
33475     
33476     register : function(brick)
33477     {
33478         //this.groups[brick.id] = brick;
33479         this.groups.add(brick.id, brick);
33480     },
33481     /**
33482     * fetch a  masonry brick based on the masonry brick ID
33483     * @param {string} the masonry brick to add
33484     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33485     */
33486     
33487     get: function(brick_id) 
33488     {
33489         // if (typeof(this.groups[brick_id]) == 'undefined') {
33490         //     return false;
33491         // }
33492         // return this.groups[brick_id] ;
33493         
33494         if(this.groups.key(brick_id)) {
33495             return this.groups.key(brick_id);
33496         }
33497         
33498         return false;
33499     }
33500     
33501     
33502     
33503 });
33504
33505  /*
33506  * - LGPL
33507  *
33508  * element
33509  * 
33510  */
33511
33512 /**
33513  * @class Roo.bootstrap.Brick
33514  * @extends Roo.bootstrap.Component
33515  * Bootstrap Brick class
33516  * 
33517  * @constructor
33518  * Create a new Brick
33519  * @param {Object} config The config object
33520  */
33521
33522 Roo.bootstrap.Brick = function(config){
33523     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33524     
33525     this.addEvents({
33526         // raw events
33527         /**
33528          * @event click
33529          * When a Brick is click
33530          * @param {Roo.bootstrap.Brick} this
33531          * @param {Roo.EventObject} e
33532          */
33533         "click" : true
33534     });
33535 };
33536
33537 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33538     
33539     /**
33540      * @cfg {String} title
33541      */   
33542     title : '',
33543     /**
33544      * @cfg {String} html
33545      */   
33546     html : '',
33547     /**
33548      * @cfg {String} bgimage
33549      */   
33550     bgimage : '',
33551     /**
33552      * @cfg {String} cls
33553      */   
33554     cls : '',
33555     /**
33556      * @cfg {String} href
33557      */   
33558     href : '',
33559     /**
33560      * @cfg {String} video
33561      */   
33562     video : '',
33563     /**
33564      * @cfg {Boolean} square
33565      */   
33566     square : true,
33567     
33568     getAutoCreate : function()
33569     {
33570         var cls = 'roo-brick';
33571         
33572         if(this.href.length){
33573             cls += ' roo-brick-link';
33574         }
33575         
33576         if(this.bgimage.length){
33577             cls += ' roo-brick-image';
33578         }
33579         
33580         if(!this.html.length && !this.bgimage.length){
33581             cls += ' roo-brick-center-title';
33582         }
33583         
33584         if(!this.html.length && this.bgimage.length){
33585             cls += ' roo-brick-bottom-title';
33586         }
33587         
33588         if(this.cls){
33589             cls += ' ' + this.cls;
33590         }
33591         
33592         var cfg = {
33593             tag: (this.href.length) ? 'a' : 'div',
33594             cls: cls,
33595             cn: [
33596                 {
33597                     tag: 'div',
33598                     cls: 'roo-brick-paragraph',
33599                     cn: []
33600                 }
33601             ]
33602         };
33603         
33604         if(this.href.length){
33605             cfg.href = this.href;
33606         }
33607         
33608         var cn = cfg.cn[0].cn;
33609         
33610         if(this.title.length){
33611             cn.push({
33612                 tag: 'h4',
33613                 cls: 'roo-brick-title',
33614                 html: this.title
33615             });
33616         }
33617         
33618         if(this.html.length){
33619             cn.push({
33620                 tag: 'p',
33621                 cls: 'roo-brick-text',
33622                 html: this.html
33623             });
33624         } else {
33625             cn.cls += ' hide';
33626         }
33627         
33628         if(this.bgimage.length){
33629             cfg.cn.push({
33630                 tag: 'img',
33631                 cls: 'roo-brick-image-view',
33632                 src: this.bgimage
33633             });
33634         }
33635         
33636         return cfg;
33637     },
33638     
33639     initEvents: function() 
33640     {
33641         if(this.title.length || this.html.length){
33642             this.el.on('mouseenter'  ,this.enter, this);
33643             this.el.on('mouseleave', this.leave, this);
33644         }
33645         
33646         Roo.EventManager.onWindowResize(this.resize, this); 
33647         
33648         if(this.bgimage.length){
33649             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33650             this.imageEl.on('load', this.onImageLoad, this);
33651             return;
33652         }
33653         
33654         this.resize();
33655     },
33656     
33657     onImageLoad : function()
33658     {
33659         this.resize();
33660     },
33661     
33662     resize : function()
33663     {
33664         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33665         
33666         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33667         
33668         if(this.bgimage.length){
33669             var image = this.el.select('.roo-brick-image-view', true).first();
33670             
33671             image.setWidth(paragraph.getWidth());
33672             
33673             if(this.square){
33674                 image.setHeight(paragraph.getWidth());
33675             }
33676             
33677             this.el.setHeight(image.getHeight());
33678             paragraph.setHeight(image.getHeight());
33679             
33680         }
33681         
33682     },
33683     
33684     enter: function(e, el)
33685     {
33686         e.preventDefault();
33687         
33688         if(this.bgimage.length){
33689             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33690             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33691         }
33692     },
33693     
33694     leave: function(e, el)
33695     {
33696         e.preventDefault();
33697         
33698         if(this.bgimage.length){
33699             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33700             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33701         }
33702     }
33703     
33704 });
33705
33706  
33707
33708  /*
33709  * - LGPL
33710  *
33711  * Number field 
33712  */
33713
33714 /**
33715  * @class Roo.bootstrap.NumberField
33716  * @extends Roo.bootstrap.Input
33717  * Bootstrap NumberField class
33718  * 
33719  * 
33720  * 
33721  * 
33722  * @constructor
33723  * Create a new NumberField
33724  * @param {Object} config The config object
33725  */
33726
33727 Roo.bootstrap.NumberField = function(config){
33728     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33729 };
33730
33731 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33732     
33733     /**
33734      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33735      */
33736     allowDecimals : true,
33737     /**
33738      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33739      */
33740     decimalSeparator : ".",
33741     /**
33742      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33743      */
33744     decimalPrecision : 2,
33745     /**
33746      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33747      */
33748     allowNegative : true,
33749     
33750     /**
33751      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33752      */
33753     allowZero: true,
33754     /**
33755      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33756      */
33757     minValue : Number.NEGATIVE_INFINITY,
33758     /**
33759      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33760      */
33761     maxValue : Number.MAX_VALUE,
33762     /**
33763      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33764      */
33765     minText : "The minimum value for this field is {0}",
33766     /**
33767      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33768      */
33769     maxText : "The maximum value for this field is {0}",
33770     /**
33771      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33772      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33773      */
33774     nanText : "{0} is not a valid number",
33775     /**
33776      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33777      */
33778     thousandsDelimiter : false,
33779     /**
33780      * @cfg {String} valueAlign alignment of value
33781      */
33782     valueAlign : "left",
33783
33784     getAutoCreate : function()
33785     {
33786         var hiddenInput = {
33787             tag: 'input',
33788             type: 'hidden',
33789             id: Roo.id(),
33790             cls: 'hidden-number-input'
33791         };
33792         
33793         if (this.name) {
33794             hiddenInput.name = this.name;
33795         }
33796         
33797         this.name = '';
33798         
33799         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33800         
33801         this.name = hiddenInput.name;
33802         
33803         if(cfg.cn.length > 0) {
33804             cfg.cn.push(hiddenInput);
33805         }
33806         
33807         return cfg;
33808     },
33809
33810     // private
33811     initEvents : function()
33812     {   
33813         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33814         
33815         var allowed = "0123456789";
33816         
33817         if(this.allowDecimals){
33818             allowed += this.decimalSeparator;
33819         }
33820         
33821         if(this.allowNegative){
33822             allowed += "-";
33823         }
33824         
33825         if(this.thousandsDelimiter) {
33826             allowed += ",";
33827         }
33828         
33829         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33830         
33831         var keyPress = function(e){
33832             
33833             var k = e.getKey();
33834             
33835             var c = e.getCharCode();
33836             
33837             if(
33838                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33839                     allowed.indexOf(String.fromCharCode(c)) === -1
33840             ){
33841                 e.stopEvent();
33842                 return;
33843             }
33844             
33845             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33846                 return;
33847             }
33848             
33849             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33850                 e.stopEvent();
33851             }
33852         };
33853         
33854         this.el.on("keypress", keyPress, this);
33855     },
33856     
33857     validateValue : function(value)
33858     {
33859         
33860         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33861             return false;
33862         }
33863         
33864         var num = this.parseValue(value);
33865         
33866         if(isNaN(num)){
33867             this.markInvalid(String.format(this.nanText, value));
33868             return false;
33869         }
33870         
33871         if(num < this.minValue){
33872             this.markInvalid(String.format(this.minText, this.minValue));
33873             return false;
33874         }
33875         
33876         if(num > this.maxValue){
33877             this.markInvalid(String.format(this.maxText, this.maxValue));
33878             return false;
33879         }
33880         
33881         return true;
33882     },
33883
33884     getValue : function()
33885     {
33886         var v = this.hiddenEl().getValue();
33887         
33888         return this.fixPrecision(this.parseValue(v));
33889     },
33890
33891     parseValue : function(value)
33892     {
33893         if(this.thousandsDelimiter) {
33894             value += "";
33895             r = new RegExp(",", "g");
33896             value = value.replace(r, "");
33897         }
33898         
33899         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33900         return isNaN(value) ? '' : value;
33901     },
33902
33903     fixPrecision : function(value)
33904     {
33905         if(this.thousandsDelimiter) {
33906             value += "";
33907             r = new RegExp(",", "g");
33908             value = value.replace(r, "");
33909         }
33910         
33911         var nan = isNaN(value);
33912         
33913         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33914             return nan ? '' : value;
33915         }
33916         return parseFloat(value).toFixed(this.decimalPrecision);
33917     },
33918
33919     setValue : function(v)
33920     {
33921         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33922         
33923         this.value = v;
33924         
33925         if(this.rendered){
33926             
33927             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33928             
33929             this.inputEl().dom.value = (v == '') ? '' :
33930                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33931             
33932             if(!this.allowZero && v === '0') {
33933                 this.hiddenEl().dom.value = '';
33934                 this.inputEl().dom.value = '';
33935             }
33936             
33937             this.validate();
33938         }
33939     },
33940
33941     decimalPrecisionFcn : function(v)
33942     {
33943         return Math.floor(v);
33944     },
33945
33946     beforeBlur : function()
33947     {
33948         var v = this.parseValue(this.getRawValue());
33949         
33950         if(v || v === 0 || v === ''){
33951             this.setValue(v);
33952         }
33953     },
33954     
33955     hiddenEl : function()
33956     {
33957         return this.el.select('input.hidden-number-input',true).first();
33958     }
33959     
33960 });
33961
33962  
33963
33964 /*
33965 * Licence: LGPL
33966 */
33967
33968 /**
33969  * @class Roo.bootstrap.DocumentSlider
33970  * @extends Roo.bootstrap.Component
33971  * Bootstrap DocumentSlider class
33972  * 
33973  * @constructor
33974  * Create a new DocumentViewer
33975  * @param {Object} config The config object
33976  */
33977
33978 Roo.bootstrap.DocumentSlider = function(config){
33979     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33980     
33981     this.files = [];
33982     
33983     this.addEvents({
33984         /**
33985          * @event initial
33986          * Fire after initEvent
33987          * @param {Roo.bootstrap.DocumentSlider} this
33988          */
33989         "initial" : true,
33990         /**
33991          * @event update
33992          * Fire after update
33993          * @param {Roo.bootstrap.DocumentSlider} this
33994          */
33995         "update" : true,
33996         /**
33997          * @event click
33998          * Fire after click
33999          * @param {Roo.bootstrap.DocumentSlider} this
34000          */
34001         "click" : true
34002     });
34003 };
34004
34005 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34006     
34007     files : false,
34008     
34009     indicator : 0,
34010     
34011     getAutoCreate : function()
34012     {
34013         var cfg = {
34014             tag : 'div',
34015             cls : 'roo-document-slider',
34016             cn : [
34017                 {
34018                     tag : 'div',
34019                     cls : 'roo-document-slider-header',
34020                     cn : [
34021                         {
34022                             tag : 'div',
34023                             cls : 'roo-document-slider-header-title'
34024                         }
34025                     ]
34026                 },
34027                 {
34028                     tag : 'div',
34029                     cls : 'roo-document-slider-body',
34030                     cn : [
34031                         {
34032                             tag : 'div',
34033                             cls : 'roo-document-slider-prev',
34034                             cn : [
34035                                 {
34036                                     tag : 'i',
34037                                     cls : 'fa fa-chevron-left'
34038                                 }
34039                             ]
34040                         },
34041                         {
34042                             tag : 'div',
34043                             cls : 'roo-document-slider-thumb',
34044                             cn : [
34045                                 {
34046                                     tag : 'img',
34047                                     cls : 'roo-document-slider-image'
34048                                 }
34049                             ]
34050                         },
34051                         {
34052                             tag : 'div',
34053                             cls : 'roo-document-slider-next',
34054                             cn : [
34055                                 {
34056                                     tag : 'i',
34057                                     cls : 'fa fa-chevron-right'
34058                                 }
34059                             ]
34060                         }
34061                     ]
34062                 }
34063             ]
34064         };
34065         
34066         return cfg;
34067     },
34068     
34069     initEvents : function()
34070     {
34071         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34072         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34073         
34074         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34075         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34076         
34077         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34078         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34079         
34080         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34081         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34082         
34083         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34084         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34085         
34086         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34087         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34088         
34089         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34090         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34091         
34092         this.thumbEl.on('click', this.onClick, this);
34093         
34094         this.prevIndicator.on('click', this.prev, this);
34095         
34096         this.nextIndicator.on('click', this.next, this);
34097         
34098     },
34099     
34100     initial : function()
34101     {
34102         if(this.files.length){
34103             this.indicator = 1;
34104             this.update()
34105         }
34106         
34107         this.fireEvent('initial', this);
34108     },
34109     
34110     update : function()
34111     {
34112         this.imageEl.attr('src', this.files[this.indicator - 1]);
34113         
34114         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34115         
34116         this.prevIndicator.show();
34117         
34118         if(this.indicator == 1){
34119             this.prevIndicator.hide();
34120         }
34121         
34122         this.nextIndicator.show();
34123         
34124         if(this.indicator == this.files.length){
34125             this.nextIndicator.hide();
34126         }
34127         
34128         this.thumbEl.scrollTo('top');
34129         
34130         this.fireEvent('update', this);
34131     },
34132     
34133     onClick : function(e)
34134     {
34135         e.preventDefault();
34136         
34137         this.fireEvent('click', this);
34138     },
34139     
34140     prev : function(e)
34141     {
34142         e.preventDefault();
34143         
34144         this.indicator = Math.max(1, this.indicator - 1);
34145         
34146         this.update();
34147     },
34148     
34149     next : function(e)
34150     {
34151         e.preventDefault();
34152         
34153         this.indicator = Math.min(this.files.length, this.indicator + 1);
34154         
34155         this.update();
34156     }
34157 });
34158 /*
34159  * - LGPL
34160  *
34161  * RadioSet
34162  *
34163  *
34164  */
34165
34166 /**
34167  * @class Roo.bootstrap.RadioSet
34168  * @extends Roo.bootstrap.Input
34169  * Bootstrap RadioSet class
34170  * @cfg {String} indicatorpos (left|right) default left
34171  * @cfg {Boolean} inline (true|false) inline the element (default true)
34172  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34173  * @constructor
34174  * Create a new RadioSet
34175  * @param {Object} config The config object
34176  */
34177
34178 Roo.bootstrap.RadioSet = function(config){
34179     
34180     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34181     
34182     this.radioes = [];
34183     
34184     Roo.bootstrap.RadioSet.register(this);
34185     
34186     this.addEvents({
34187         /**
34188         * @event check
34189         * Fires when the element is checked or unchecked.
34190         * @param {Roo.bootstrap.RadioSet} this This radio
34191         * @param {Roo.bootstrap.Radio} item The checked item
34192         */
34193        check : true,
34194        /**
34195         * @event click
34196         * Fires when the element is click.
34197         * @param {Roo.bootstrap.RadioSet} this This radio set
34198         * @param {Roo.bootstrap.Radio} item The checked item
34199         * @param {Roo.EventObject} e The event object
34200         */
34201        click : true
34202     });
34203     
34204 };
34205
34206 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34207
34208     radioes : false,
34209     
34210     inline : true,
34211     
34212     weight : '',
34213     
34214     indicatorpos : 'left',
34215     
34216     getAutoCreate : function()
34217     {
34218         var label = {
34219             tag : 'label',
34220             cls : 'roo-radio-set-label',
34221             cn : [
34222                 {
34223                     tag : 'span',
34224                     html : this.fieldLabel
34225                 }
34226             ]
34227         };
34228         if (Roo.bootstrap.version == 3) {
34229             
34230             
34231             if(this.indicatorpos == 'left'){
34232                 label.cn.unshift({
34233                     tag : 'i',
34234                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34235                     tooltip : 'This field is required'
34236                 });
34237             } else {
34238                 label.cn.push({
34239                     tag : 'i',
34240                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34241                     tooltip : 'This field is required'
34242                 });
34243             }
34244         }
34245         var items = {
34246             tag : 'div',
34247             cls : 'roo-radio-set-items'
34248         };
34249         
34250         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34251         
34252         if (align === 'left' && this.fieldLabel.length) {
34253             
34254             items = {
34255                 cls : "roo-radio-set-right", 
34256                 cn: [
34257                     items
34258                 ]
34259             };
34260             
34261             if(this.labelWidth > 12){
34262                 label.style = "width: " + this.labelWidth + 'px';
34263             }
34264             
34265             if(this.labelWidth < 13 && this.labelmd == 0){
34266                 this.labelmd = this.labelWidth;
34267             }
34268             
34269             if(this.labellg > 0){
34270                 label.cls += ' col-lg-' + this.labellg;
34271                 items.cls += ' col-lg-' + (12 - this.labellg);
34272             }
34273             
34274             if(this.labelmd > 0){
34275                 label.cls += ' col-md-' + this.labelmd;
34276                 items.cls += ' col-md-' + (12 - this.labelmd);
34277             }
34278             
34279             if(this.labelsm > 0){
34280                 label.cls += ' col-sm-' + this.labelsm;
34281                 items.cls += ' col-sm-' + (12 - this.labelsm);
34282             }
34283             
34284             if(this.labelxs > 0){
34285                 label.cls += ' col-xs-' + this.labelxs;
34286                 items.cls += ' col-xs-' + (12 - this.labelxs);
34287             }
34288         }
34289         
34290         var cfg = {
34291             tag : 'div',
34292             cls : 'roo-radio-set',
34293             cn : [
34294                 {
34295                     tag : 'input',
34296                     cls : 'roo-radio-set-input',
34297                     type : 'hidden',
34298                     name : this.name,
34299                     value : this.value ? this.value :  ''
34300                 },
34301                 label,
34302                 items
34303             ]
34304         };
34305         
34306         if(this.weight.length){
34307             cfg.cls += ' roo-radio-' + this.weight;
34308         }
34309         
34310         if(this.inline) {
34311             cfg.cls += ' roo-radio-set-inline';
34312         }
34313         
34314         var settings=this;
34315         ['xs','sm','md','lg'].map(function(size){
34316             if (settings[size]) {
34317                 cfg.cls += ' col-' + size + '-' + settings[size];
34318             }
34319         });
34320         
34321         return cfg;
34322         
34323     },
34324
34325     initEvents : function()
34326     {
34327         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34328         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34329         
34330         if(!this.fieldLabel.length){
34331             this.labelEl.hide();
34332         }
34333         
34334         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34335         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34336         
34337         this.indicator = this.indicatorEl();
34338         
34339         if(this.indicator){
34340             this.indicator.addClass('invisible');
34341         }
34342         
34343         this.originalValue = this.getValue();
34344         
34345     },
34346     
34347     inputEl: function ()
34348     {
34349         return this.el.select('.roo-radio-set-input', true).first();
34350     },
34351     
34352     getChildContainer : function()
34353     {
34354         return this.itemsEl;
34355     },
34356     
34357     register : function(item)
34358     {
34359         this.radioes.push(item);
34360         
34361     },
34362     
34363     validate : function()
34364     {   
34365         if(this.getVisibilityEl().hasClass('hidden')){
34366             return true;
34367         }
34368         
34369         var valid = false;
34370         
34371         Roo.each(this.radioes, function(i){
34372             if(!i.checked){
34373                 return;
34374             }
34375             
34376             valid = true;
34377             return false;
34378         });
34379         
34380         if(this.allowBlank) {
34381             return true;
34382         }
34383         
34384         if(this.disabled || valid){
34385             this.markValid();
34386             return true;
34387         }
34388         
34389         this.markInvalid();
34390         return false;
34391         
34392     },
34393     
34394     markValid : function()
34395     {
34396         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34397             this.indicatorEl().removeClass('visible');
34398             this.indicatorEl().addClass('invisible');
34399         }
34400         
34401         
34402         if (Roo.bootstrap.version == 3) {
34403             this.el.removeClass([this.invalidClass, this.validClass]);
34404             this.el.addClass(this.validClass);
34405         } else {
34406             this.el.removeClass(['is-invalid','is-valid']);
34407             this.el.addClass(['is-valid']);
34408         }
34409         this.fireEvent('valid', this);
34410     },
34411     
34412     markInvalid : function(msg)
34413     {
34414         if(this.allowBlank || this.disabled){
34415             return;
34416         }
34417         
34418         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34419             this.indicatorEl().removeClass('invisible');
34420             this.indicatorEl().addClass('visible');
34421         }
34422         if (Roo.bootstrap.version == 3) {
34423             this.el.removeClass([this.invalidClass, this.validClass]);
34424             this.el.addClass(this.invalidClass);
34425         } else {
34426             this.el.removeClass(['is-invalid','is-valid']);
34427             this.el.addClass(['is-invalid']);
34428         }
34429         
34430         this.fireEvent('invalid', this, msg);
34431         
34432     },
34433     
34434     setValue : function(v, suppressEvent)
34435     {   
34436         if(this.value === v){
34437             return;
34438         }
34439         
34440         this.value = v;
34441         
34442         if(this.rendered){
34443             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34444         }
34445         
34446         Roo.each(this.radioes, function(i){
34447             i.checked = false;
34448             i.el.removeClass('checked');
34449         });
34450         
34451         Roo.each(this.radioes, function(i){
34452             
34453             if(i.value === v || i.value.toString() === v.toString()){
34454                 i.checked = true;
34455                 i.el.addClass('checked');
34456                 
34457                 if(suppressEvent !== true){
34458                     this.fireEvent('check', this, i);
34459                 }
34460                 
34461                 return false;
34462             }
34463             
34464         }, this);
34465         
34466         this.validate();
34467     },
34468     
34469     clearInvalid : function(){
34470         
34471         if(!this.el || this.preventMark){
34472             return;
34473         }
34474         
34475         this.el.removeClass([this.invalidClass]);
34476         
34477         this.fireEvent('valid', this);
34478     }
34479     
34480 });
34481
34482 Roo.apply(Roo.bootstrap.RadioSet, {
34483     
34484     groups: {},
34485     
34486     register : function(set)
34487     {
34488         this.groups[set.name] = set;
34489     },
34490     
34491     get: function(name) 
34492     {
34493         if (typeof(this.groups[name]) == 'undefined') {
34494             return false;
34495         }
34496         
34497         return this.groups[name] ;
34498     }
34499     
34500 });
34501 /*
34502  * Based on:
34503  * Ext JS Library 1.1.1
34504  * Copyright(c) 2006-2007, Ext JS, LLC.
34505  *
34506  * Originally Released Under LGPL - original licence link has changed is not relivant.
34507  *
34508  * Fork - LGPL
34509  * <script type="text/javascript">
34510  */
34511
34512
34513 /**
34514  * @class Roo.bootstrap.SplitBar
34515  * @extends Roo.util.Observable
34516  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34517  * <br><br>
34518  * Usage:
34519  * <pre><code>
34520 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34521                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34522 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34523 split.minSize = 100;
34524 split.maxSize = 600;
34525 split.animate = true;
34526 split.on('moved', splitterMoved);
34527 </code></pre>
34528  * @constructor
34529  * Create a new SplitBar
34530  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34531  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34532  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34533  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34534                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34535                         position of the SplitBar).
34536  */
34537 Roo.bootstrap.SplitBar = function(cfg){
34538     
34539     /** @private */
34540     
34541     //{
34542     //  dragElement : elm
34543     //  resizingElement: el,
34544         // optional..
34545     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34546     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34547         // existingProxy ???
34548     //}
34549     
34550     this.el = Roo.get(cfg.dragElement, true);
34551     this.el.dom.unselectable = "on";
34552     /** @private */
34553     this.resizingEl = Roo.get(cfg.resizingElement, true);
34554
34555     /**
34556      * @private
34557      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34558      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34559      * @type Number
34560      */
34561     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34562     
34563     /**
34564      * The minimum size of the resizing element. (Defaults to 0)
34565      * @type Number
34566      */
34567     this.minSize = 0;
34568     
34569     /**
34570      * The maximum size of the resizing element. (Defaults to 2000)
34571      * @type Number
34572      */
34573     this.maxSize = 2000;
34574     
34575     /**
34576      * Whether to animate the transition to the new size
34577      * @type Boolean
34578      */
34579     this.animate = false;
34580     
34581     /**
34582      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34583      * @type Boolean
34584      */
34585     this.useShim = false;
34586     
34587     /** @private */
34588     this.shim = null;
34589     
34590     if(!cfg.existingProxy){
34591         /** @private */
34592         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34593     }else{
34594         this.proxy = Roo.get(cfg.existingProxy).dom;
34595     }
34596     /** @private */
34597     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34598     
34599     /** @private */
34600     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34601     
34602     /** @private */
34603     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34604     
34605     /** @private */
34606     this.dragSpecs = {};
34607     
34608     /**
34609      * @private The adapter to use to positon and resize elements
34610      */
34611     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34612     this.adapter.init(this);
34613     
34614     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34615         /** @private */
34616         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34617         this.el.addClass("roo-splitbar-h");
34618     }else{
34619         /** @private */
34620         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34621         this.el.addClass("roo-splitbar-v");
34622     }
34623     
34624     this.addEvents({
34625         /**
34626          * @event resize
34627          * Fires when the splitter is moved (alias for {@link #event-moved})
34628          * @param {Roo.bootstrap.SplitBar} this
34629          * @param {Number} newSize the new width or height
34630          */
34631         "resize" : true,
34632         /**
34633          * @event moved
34634          * Fires when the splitter is moved
34635          * @param {Roo.bootstrap.SplitBar} this
34636          * @param {Number} newSize the new width or height
34637          */
34638         "moved" : true,
34639         /**
34640          * @event beforeresize
34641          * Fires before the splitter is dragged
34642          * @param {Roo.bootstrap.SplitBar} this
34643          */
34644         "beforeresize" : true,
34645
34646         "beforeapply" : true
34647     });
34648
34649     Roo.util.Observable.call(this);
34650 };
34651
34652 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34653     onStartProxyDrag : function(x, y){
34654         this.fireEvent("beforeresize", this);
34655         if(!this.overlay){
34656             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34657             o.unselectable();
34658             o.enableDisplayMode("block");
34659             // all splitbars share the same overlay
34660             Roo.bootstrap.SplitBar.prototype.overlay = o;
34661         }
34662         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34663         this.overlay.show();
34664         Roo.get(this.proxy).setDisplayed("block");
34665         var size = this.adapter.getElementSize(this);
34666         this.activeMinSize = this.getMinimumSize();;
34667         this.activeMaxSize = this.getMaximumSize();;
34668         var c1 = size - this.activeMinSize;
34669         var c2 = Math.max(this.activeMaxSize - size, 0);
34670         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34671             this.dd.resetConstraints();
34672             this.dd.setXConstraint(
34673                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34674                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34675             );
34676             this.dd.setYConstraint(0, 0);
34677         }else{
34678             this.dd.resetConstraints();
34679             this.dd.setXConstraint(0, 0);
34680             this.dd.setYConstraint(
34681                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34682                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34683             );
34684          }
34685         this.dragSpecs.startSize = size;
34686         this.dragSpecs.startPoint = [x, y];
34687         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34688     },
34689     
34690     /** 
34691      * @private Called after the drag operation by the DDProxy
34692      */
34693     onEndProxyDrag : function(e){
34694         Roo.get(this.proxy).setDisplayed(false);
34695         var endPoint = Roo.lib.Event.getXY(e);
34696         if(this.overlay){
34697             this.overlay.hide();
34698         }
34699         var newSize;
34700         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34701             newSize = this.dragSpecs.startSize + 
34702                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34703                     endPoint[0] - this.dragSpecs.startPoint[0] :
34704                     this.dragSpecs.startPoint[0] - endPoint[0]
34705                 );
34706         }else{
34707             newSize = this.dragSpecs.startSize + 
34708                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34709                     endPoint[1] - this.dragSpecs.startPoint[1] :
34710                     this.dragSpecs.startPoint[1] - endPoint[1]
34711                 );
34712         }
34713         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34714         if(newSize != this.dragSpecs.startSize){
34715             if(this.fireEvent('beforeapply', this, newSize) !== false){
34716                 this.adapter.setElementSize(this, newSize);
34717                 this.fireEvent("moved", this, newSize);
34718                 this.fireEvent("resize", this, newSize);
34719             }
34720         }
34721     },
34722     
34723     /**
34724      * Get the adapter this SplitBar uses
34725      * @return The adapter object
34726      */
34727     getAdapter : function(){
34728         return this.adapter;
34729     },
34730     
34731     /**
34732      * Set the adapter this SplitBar uses
34733      * @param {Object} adapter A SplitBar adapter object
34734      */
34735     setAdapter : function(adapter){
34736         this.adapter = adapter;
34737         this.adapter.init(this);
34738     },
34739     
34740     /**
34741      * Gets the minimum size for the resizing element
34742      * @return {Number} The minimum size
34743      */
34744     getMinimumSize : function(){
34745         return this.minSize;
34746     },
34747     
34748     /**
34749      * Sets the minimum size for the resizing element
34750      * @param {Number} minSize The minimum size
34751      */
34752     setMinimumSize : function(minSize){
34753         this.minSize = minSize;
34754     },
34755     
34756     /**
34757      * Gets the maximum size for the resizing element
34758      * @return {Number} The maximum size
34759      */
34760     getMaximumSize : function(){
34761         return this.maxSize;
34762     },
34763     
34764     /**
34765      * Sets the maximum size for the resizing element
34766      * @param {Number} maxSize The maximum size
34767      */
34768     setMaximumSize : function(maxSize){
34769         this.maxSize = maxSize;
34770     },
34771     
34772     /**
34773      * Sets the initialize size for the resizing element
34774      * @param {Number} size The initial size
34775      */
34776     setCurrentSize : function(size){
34777         var oldAnimate = this.animate;
34778         this.animate = false;
34779         this.adapter.setElementSize(this, size);
34780         this.animate = oldAnimate;
34781     },
34782     
34783     /**
34784      * Destroy this splitbar. 
34785      * @param {Boolean} removeEl True to remove the element
34786      */
34787     destroy : function(removeEl){
34788         if(this.shim){
34789             this.shim.remove();
34790         }
34791         this.dd.unreg();
34792         this.proxy.parentNode.removeChild(this.proxy);
34793         if(removeEl){
34794             this.el.remove();
34795         }
34796     }
34797 });
34798
34799 /**
34800  * @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.
34801  */
34802 Roo.bootstrap.SplitBar.createProxy = function(dir){
34803     var proxy = new Roo.Element(document.createElement("div"));
34804     proxy.unselectable();
34805     var cls = 'roo-splitbar-proxy';
34806     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34807     document.body.appendChild(proxy.dom);
34808     return proxy.dom;
34809 };
34810
34811 /** 
34812  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34813  * Default Adapter. It assumes the splitter and resizing element are not positioned
34814  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34815  */
34816 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34817 };
34818
34819 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34820     // do nothing for now
34821     init : function(s){
34822     
34823     },
34824     /**
34825      * Called before drag operations to get the current size of the resizing element. 
34826      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34827      */
34828      getElementSize : function(s){
34829         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34830             return s.resizingEl.getWidth();
34831         }else{
34832             return s.resizingEl.getHeight();
34833         }
34834     },
34835     
34836     /**
34837      * Called after drag operations to set the size of the resizing element.
34838      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34839      * @param {Number} newSize The new size to set
34840      * @param {Function} onComplete A function to be invoked when resizing is complete
34841      */
34842     setElementSize : function(s, newSize, onComplete){
34843         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34844             if(!s.animate){
34845                 s.resizingEl.setWidth(newSize);
34846                 if(onComplete){
34847                     onComplete(s, newSize);
34848                 }
34849             }else{
34850                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34851             }
34852         }else{
34853             
34854             if(!s.animate){
34855                 s.resizingEl.setHeight(newSize);
34856                 if(onComplete){
34857                     onComplete(s, newSize);
34858                 }
34859             }else{
34860                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34861             }
34862         }
34863     }
34864 };
34865
34866 /** 
34867  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34868  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34869  * Adapter that  moves the splitter element to align with the resized sizing element. 
34870  * Used with an absolute positioned SplitBar.
34871  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34872  * document.body, make sure you assign an id to the body element.
34873  */
34874 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34875     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34876     this.container = Roo.get(container);
34877 };
34878
34879 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34880     init : function(s){
34881         this.basic.init(s);
34882     },
34883     
34884     getElementSize : function(s){
34885         return this.basic.getElementSize(s);
34886     },
34887     
34888     setElementSize : function(s, newSize, onComplete){
34889         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34890     },
34891     
34892     moveSplitter : function(s){
34893         var yes = Roo.bootstrap.SplitBar;
34894         switch(s.placement){
34895             case yes.LEFT:
34896                 s.el.setX(s.resizingEl.getRight());
34897                 break;
34898             case yes.RIGHT:
34899                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34900                 break;
34901             case yes.TOP:
34902                 s.el.setY(s.resizingEl.getBottom());
34903                 break;
34904             case yes.BOTTOM:
34905                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34906                 break;
34907         }
34908     }
34909 };
34910
34911 /**
34912  * Orientation constant - Create a vertical SplitBar
34913  * @static
34914  * @type Number
34915  */
34916 Roo.bootstrap.SplitBar.VERTICAL = 1;
34917
34918 /**
34919  * Orientation constant - Create a horizontal SplitBar
34920  * @static
34921  * @type Number
34922  */
34923 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34924
34925 /**
34926  * Placement constant - The resizing element is to the left of the splitter element
34927  * @static
34928  * @type Number
34929  */
34930 Roo.bootstrap.SplitBar.LEFT = 1;
34931
34932 /**
34933  * Placement constant - The resizing element is to the right of the splitter element
34934  * @static
34935  * @type Number
34936  */
34937 Roo.bootstrap.SplitBar.RIGHT = 2;
34938
34939 /**
34940  * Placement constant - The resizing element is positioned above the splitter element
34941  * @static
34942  * @type Number
34943  */
34944 Roo.bootstrap.SplitBar.TOP = 3;
34945
34946 /**
34947  * Placement constant - The resizing element is positioned under splitter element
34948  * @static
34949  * @type Number
34950  */
34951 Roo.bootstrap.SplitBar.BOTTOM = 4;
34952 Roo.namespace("Roo.bootstrap.layout");/*
34953  * Based on:
34954  * Ext JS Library 1.1.1
34955  * Copyright(c) 2006-2007, Ext JS, LLC.
34956  *
34957  * Originally Released Under LGPL - original licence link has changed is not relivant.
34958  *
34959  * Fork - LGPL
34960  * <script type="text/javascript">
34961  */
34962
34963 /**
34964  * @class Roo.bootstrap.layout.Manager
34965  * @extends Roo.bootstrap.Component
34966  * Base class for layout managers.
34967  */
34968 Roo.bootstrap.layout.Manager = function(config)
34969 {
34970     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34971
34972
34973
34974
34975
34976     /** false to disable window resize monitoring @type Boolean */
34977     this.monitorWindowResize = true;
34978     this.regions = {};
34979     this.addEvents({
34980         /**
34981          * @event layout
34982          * Fires when a layout is performed.
34983          * @param {Roo.LayoutManager} this
34984          */
34985         "layout" : true,
34986         /**
34987          * @event regionresized
34988          * Fires when the user resizes a region.
34989          * @param {Roo.LayoutRegion} region The resized region
34990          * @param {Number} newSize The new size (width for east/west, height for north/south)
34991          */
34992         "regionresized" : true,
34993         /**
34994          * @event regioncollapsed
34995          * Fires when a region is collapsed.
34996          * @param {Roo.LayoutRegion} region The collapsed region
34997          */
34998         "regioncollapsed" : true,
34999         /**
35000          * @event regionexpanded
35001          * Fires when a region is expanded.
35002          * @param {Roo.LayoutRegion} region The expanded region
35003          */
35004         "regionexpanded" : true
35005     });
35006     this.updating = false;
35007
35008     if (config.el) {
35009         this.el = Roo.get(config.el);
35010         this.initEvents();
35011     }
35012
35013 };
35014
35015 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35016
35017
35018     regions : null,
35019
35020     monitorWindowResize : true,
35021
35022
35023     updating : false,
35024
35025
35026     onRender : function(ct, position)
35027     {
35028         if(!this.el){
35029             this.el = Roo.get(ct);
35030             this.initEvents();
35031         }
35032         //this.fireEvent('render',this);
35033     },
35034
35035
35036     initEvents: function()
35037     {
35038
35039
35040         // ie scrollbar fix
35041         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35042             document.body.scroll = "no";
35043         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35044             this.el.position('relative');
35045         }
35046         this.id = this.el.id;
35047         this.el.addClass("roo-layout-container");
35048         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35049         if(this.el.dom != document.body ) {
35050             this.el.on('resize', this.layout,this);
35051             this.el.on('show', this.layout,this);
35052         }
35053
35054     },
35055
35056     /**
35057      * Returns true if this layout is currently being updated
35058      * @return {Boolean}
35059      */
35060     isUpdating : function(){
35061         return this.updating;
35062     },
35063
35064     /**
35065      * Suspend the LayoutManager from doing auto-layouts while
35066      * making multiple add or remove calls
35067      */
35068     beginUpdate : function(){
35069         this.updating = true;
35070     },
35071
35072     /**
35073      * Restore auto-layouts and optionally disable the manager from performing a layout
35074      * @param {Boolean} noLayout true to disable a layout update
35075      */
35076     endUpdate : function(noLayout){
35077         this.updating = false;
35078         if(!noLayout){
35079             this.layout();
35080         }
35081     },
35082
35083     layout: function(){
35084         // abstract...
35085     },
35086
35087     onRegionResized : function(region, newSize){
35088         this.fireEvent("regionresized", region, newSize);
35089         this.layout();
35090     },
35091
35092     onRegionCollapsed : function(region){
35093         this.fireEvent("regioncollapsed", region);
35094     },
35095
35096     onRegionExpanded : function(region){
35097         this.fireEvent("regionexpanded", region);
35098     },
35099
35100     /**
35101      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35102      * performs box-model adjustments.
35103      * @return {Object} The size as an object {width: (the width), height: (the height)}
35104      */
35105     getViewSize : function()
35106     {
35107         var size;
35108         if(this.el.dom != document.body){
35109             size = this.el.getSize();
35110         }else{
35111             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35112         }
35113         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35114         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35115         return size;
35116     },
35117
35118     /**
35119      * Returns the Element this layout is bound to.
35120      * @return {Roo.Element}
35121      */
35122     getEl : function(){
35123         return this.el;
35124     },
35125
35126     /**
35127      * Returns the specified region.
35128      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35129      * @return {Roo.LayoutRegion}
35130      */
35131     getRegion : function(target){
35132         return this.regions[target.toLowerCase()];
35133     },
35134
35135     onWindowResize : function(){
35136         if(this.monitorWindowResize){
35137             this.layout();
35138         }
35139     }
35140 });
35141 /*
35142  * Based on:
35143  * Ext JS Library 1.1.1
35144  * Copyright(c) 2006-2007, Ext JS, LLC.
35145  *
35146  * Originally Released Under LGPL - original licence link has changed is not relivant.
35147  *
35148  * Fork - LGPL
35149  * <script type="text/javascript">
35150  */
35151 /**
35152  * @class Roo.bootstrap.layout.Border
35153  * @extends Roo.bootstrap.layout.Manager
35154  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35155  * please see: examples/bootstrap/nested.html<br><br>
35156  
35157 <b>The container the layout is rendered into can be either the body element or any other element.
35158 If it is not the body element, the container needs to either be an absolute positioned element,
35159 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35160 the container size if it is not the body element.</b>
35161
35162 * @constructor
35163 * Create a new Border
35164 * @param {Object} config Configuration options
35165  */
35166 Roo.bootstrap.layout.Border = function(config){
35167     config = config || {};
35168     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35169     
35170     
35171     
35172     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35173         if(config[region]){
35174             config[region].region = region;
35175             this.addRegion(config[region]);
35176         }
35177     },this);
35178     
35179 };
35180
35181 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35182
35183 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35184     
35185     parent : false, // this might point to a 'nest' or a ???
35186     
35187     /**
35188      * Creates and adds a new region if it doesn't already exist.
35189      * @param {String} target The target region key (north, south, east, west or center).
35190      * @param {Object} config The regions config object
35191      * @return {BorderLayoutRegion} The new region
35192      */
35193     addRegion : function(config)
35194     {
35195         if(!this.regions[config.region]){
35196             var r = this.factory(config);
35197             this.bindRegion(r);
35198         }
35199         return this.regions[config.region];
35200     },
35201
35202     // private (kinda)
35203     bindRegion : function(r){
35204         this.regions[r.config.region] = r;
35205         
35206         r.on("visibilitychange",    this.layout, this);
35207         r.on("paneladded",          this.layout, this);
35208         r.on("panelremoved",        this.layout, this);
35209         r.on("invalidated",         this.layout, this);
35210         r.on("resized",             this.onRegionResized, this);
35211         r.on("collapsed",           this.onRegionCollapsed, this);
35212         r.on("expanded",            this.onRegionExpanded, this);
35213     },
35214
35215     /**
35216      * Performs a layout update.
35217      */
35218     layout : function()
35219     {
35220         if(this.updating) {
35221             return;
35222         }
35223         
35224         // render all the rebions if they have not been done alreayd?
35225         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35226             if(this.regions[region] && !this.regions[region].bodyEl){
35227                 this.regions[region].onRender(this.el)
35228             }
35229         },this);
35230         
35231         var size = this.getViewSize();
35232         var w = size.width;
35233         var h = size.height;
35234         var centerW = w;
35235         var centerH = h;
35236         var centerY = 0;
35237         var centerX = 0;
35238         //var x = 0, y = 0;
35239
35240         var rs = this.regions;
35241         var north = rs["north"];
35242         var south = rs["south"]; 
35243         var west = rs["west"];
35244         var east = rs["east"];
35245         var center = rs["center"];
35246         //if(this.hideOnLayout){ // not supported anymore
35247             //c.el.setStyle("display", "none");
35248         //}
35249         if(north && north.isVisible()){
35250             var b = north.getBox();
35251             var m = north.getMargins();
35252             b.width = w - (m.left+m.right);
35253             b.x = m.left;
35254             b.y = m.top;
35255             centerY = b.height + b.y + m.bottom;
35256             centerH -= centerY;
35257             north.updateBox(this.safeBox(b));
35258         }
35259         if(south && south.isVisible()){
35260             var b = south.getBox();
35261             var m = south.getMargins();
35262             b.width = w - (m.left+m.right);
35263             b.x = m.left;
35264             var totalHeight = (b.height + m.top + m.bottom);
35265             b.y = h - totalHeight + m.top;
35266             centerH -= totalHeight;
35267             south.updateBox(this.safeBox(b));
35268         }
35269         if(west && west.isVisible()){
35270             var b = west.getBox();
35271             var m = west.getMargins();
35272             b.height = centerH - (m.top+m.bottom);
35273             b.x = m.left;
35274             b.y = centerY + m.top;
35275             var totalWidth = (b.width + m.left + m.right);
35276             centerX += totalWidth;
35277             centerW -= totalWidth;
35278             west.updateBox(this.safeBox(b));
35279         }
35280         if(east && east.isVisible()){
35281             var b = east.getBox();
35282             var m = east.getMargins();
35283             b.height = centerH - (m.top+m.bottom);
35284             var totalWidth = (b.width + m.left + m.right);
35285             b.x = w - totalWidth + m.left;
35286             b.y = centerY + m.top;
35287             centerW -= totalWidth;
35288             east.updateBox(this.safeBox(b));
35289         }
35290         if(center){
35291             var m = center.getMargins();
35292             var centerBox = {
35293                 x: centerX + m.left,
35294                 y: centerY + m.top,
35295                 width: centerW - (m.left+m.right),
35296                 height: centerH - (m.top+m.bottom)
35297             };
35298             //if(this.hideOnLayout){
35299                 //center.el.setStyle("display", "block");
35300             //}
35301             center.updateBox(this.safeBox(centerBox));
35302         }
35303         this.el.repaint();
35304         this.fireEvent("layout", this);
35305     },
35306
35307     // private
35308     safeBox : function(box){
35309         box.width = Math.max(0, box.width);
35310         box.height = Math.max(0, box.height);
35311         return box;
35312     },
35313
35314     /**
35315      * Adds a ContentPanel (or subclass) to this layout.
35316      * @param {String} target The target region key (north, south, east, west or center).
35317      * @param {Roo.ContentPanel} panel The panel to add
35318      * @return {Roo.ContentPanel} The added panel
35319      */
35320     add : function(target, panel){
35321          
35322         target = target.toLowerCase();
35323         return this.regions[target].add(panel);
35324     },
35325
35326     /**
35327      * Remove a ContentPanel (or subclass) to this layout.
35328      * @param {String} target The target region key (north, south, east, west or center).
35329      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35330      * @return {Roo.ContentPanel} The removed panel
35331      */
35332     remove : function(target, panel){
35333         target = target.toLowerCase();
35334         return this.regions[target].remove(panel);
35335     },
35336
35337     /**
35338      * Searches all regions for a panel with the specified id
35339      * @param {String} panelId
35340      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35341      */
35342     findPanel : function(panelId){
35343         var rs = this.regions;
35344         for(var target in rs){
35345             if(typeof rs[target] != "function"){
35346                 var p = rs[target].getPanel(panelId);
35347                 if(p){
35348                     return p;
35349                 }
35350             }
35351         }
35352         return null;
35353     },
35354
35355     /**
35356      * Searches all regions for a panel with the specified id and activates (shows) it.
35357      * @param {String/ContentPanel} panelId The panels id or the panel itself
35358      * @return {Roo.ContentPanel} The shown panel or null
35359      */
35360     showPanel : function(panelId) {
35361       var rs = this.regions;
35362       for(var target in rs){
35363          var r = rs[target];
35364          if(typeof r != "function"){
35365             if(r.hasPanel(panelId)){
35366                return r.showPanel(panelId);
35367             }
35368          }
35369       }
35370       return null;
35371    },
35372
35373    /**
35374      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35375      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35376      */
35377    /*
35378     restoreState : function(provider){
35379         if(!provider){
35380             provider = Roo.state.Manager;
35381         }
35382         var sm = new Roo.LayoutStateManager();
35383         sm.init(this, provider);
35384     },
35385 */
35386  
35387  
35388     /**
35389      * Adds a xtype elements to the layout.
35390      * <pre><code>
35391
35392 layout.addxtype({
35393        xtype : 'ContentPanel',
35394        region: 'west',
35395        items: [ .... ]
35396    }
35397 );
35398
35399 layout.addxtype({
35400         xtype : 'NestedLayoutPanel',
35401         region: 'west',
35402         layout: {
35403            center: { },
35404            west: { }   
35405         },
35406         items : [ ... list of content panels or nested layout panels.. ]
35407    }
35408 );
35409 </code></pre>
35410      * @param {Object} cfg Xtype definition of item to add.
35411      */
35412     addxtype : function(cfg)
35413     {
35414         // basically accepts a pannel...
35415         // can accept a layout region..!?!?
35416         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35417         
35418         
35419         // theory?  children can only be panels??
35420         
35421         //if (!cfg.xtype.match(/Panel$/)) {
35422         //    return false;
35423         //}
35424         var ret = false;
35425         
35426         if (typeof(cfg.region) == 'undefined') {
35427             Roo.log("Failed to add Panel, region was not set");
35428             Roo.log(cfg);
35429             return false;
35430         }
35431         var region = cfg.region;
35432         delete cfg.region;
35433         
35434           
35435         var xitems = [];
35436         if (cfg.items) {
35437             xitems = cfg.items;
35438             delete cfg.items;
35439         }
35440         var nb = false;
35441         
35442         if ( region == 'center') {
35443             Roo.log("Center: " + cfg.title);
35444         }
35445         
35446         
35447         switch(cfg.xtype) 
35448         {
35449             case 'Content':  // ContentPanel (el, cfg)
35450             case 'Scroll':  // ContentPanel (el, cfg)
35451             case 'View': 
35452                 cfg.autoCreate = cfg.autoCreate || true;
35453                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35454                 //} else {
35455                 //    var el = this.el.createChild();
35456                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35457                 //}
35458                 
35459                 this.add(region, ret);
35460                 break;
35461             
35462             /*
35463             case 'TreePanel': // our new panel!
35464                 cfg.el = this.el.createChild();
35465                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35466                 this.add(region, ret);
35467                 break;
35468             */
35469             
35470             case 'Nest': 
35471                 // create a new Layout (which is  a Border Layout...
35472                 
35473                 var clayout = cfg.layout;
35474                 clayout.el  = this.el.createChild();
35475                 clayout.items   = clayout.items  || [];
35476                 
35477                 delete cfg.layout;
35478                 
35479                 // replace this exitems with the clayout ones..
35480                 xitems = clayout.items;
35481                  
35482                 // force background off if it's in center...
35483                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35484                     cfg.background = false;
35485                 }
35486                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35487                 
35488                 
35489                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35490                 //console.log('adding nested layout panel '  + cfg.toSource());
35491                 this.add(region, ret);
35492                 nb = {}; /// find first...
35493                 break;
35494             
35495             case 'Grid':
35496                 
35497                 // needs grid and region
35498                 
35499                 //var el = this.getRegion(region).el.createChild();
35500                 /*
35501                  *var el = this.el.createChild();
35502                 // create the grid first...
35503                 cfg.grid.container = el;
35504                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35505                 */
35506                 
35507                 if (region == 'center' && this.active ) {
35508                     cfg.background = false;
35509                 }
35510                 
35511                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35512                 
35513                 this.add(region, ret);
35514                 /*
35515                 if (cfg.background) {
35516                     // render grid on panel activation (if panel background)
35517                     ret.on('activate', function(gp) {
35518                         if (!gp.grid.rendered) {
35519                     //        gp.grid.render(el);
35520                         }
35521                     });
35522                 } else {
35523                   //  cfg.grid.render(el);
35524                 }
35525                 */
35526                 break;
35527            
35528            
35529             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35530                 // it was the old xcomponent building that caused this before.
35531                 // espeically if border is the top element in the tree.
35532                 ret = this;
35533                 break; 
35534                 
35535                     
35536                 
35537                 
35538                 
35539             default:
35540                 /*
35541                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35542                     
35543                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35544                     this.add(region, ret);
35545                 } else {
35546                 */
35547                     Roo.log(cfg);
35548                     throw "Can not add '" + cfg.xtype + "' to Border";
35549                     return null;
35550              
35551                                 
35552              
35553         }
35554         this.beginUpdate();
35555         // add children..
35556         var region = '';
35557         var abn = {};
35558         Roo.each(xitems, function(i)  {
35559             region = nb && i.region ? i.region : false;
35560             
35561             var add = ret.addxtype(i);
35562            
35563             if (region) {
35564                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35565                 if (!i.background) {
35566                     abn[region] = nb[region] ;
35567                 }
35568             }
35569             
35570         });
35571         this.endUpdate();
35572
35573         // make the last non-background panel active..
35574         //if (nb) { Roo.log(abn); }
35575         if (nb) {
35576             
35577             for(var r in abn) {
35578                 region = this.getRegion(r);
35579                 if (region) {
35580                     // tried using nb[r], but it does not work..
35581                      
35582                     region.showPanel(abn[r]);
35583                    
35584                 }
35585             }
35586         }
35587         return ret;
35588         
35589     },
35590     
35591     
35592 // private
35593     factory : function(cfg)
35594     {
35595         
35596         var validRegions = Roo.bootstrap.layout.Border.regions;
35597
35598         var target = cfg.region;
35599         cfg.mgr = this;
35600         
35601         var r = Roo.bootstrap.layout;
35602         Roo.log(target);
35603         switch(target){
35604             case "north":
35605                 return new r.North(cfg);
35606             case "south":
35607                 return new r.South(cfg);
35608             case "east":
35609                 return new r.East(cfg);
35610             case "west":
35611                 return new r.West(cfg);
35612             case "center":
35613                 return new r.Center(cfg);
35614         }
35615         throw 'Layout region "'+target+'" not supported.';
35616     }
35617     
35618     
35619 });
35620  /*
35621  * Based on:
35622  * Ext JS Library 1.1.1
35623  * Copyright(c) 2006-2007, Ext JS, LLC.
35624  *
35625  * Originally Released Under LGPL - original licence link has changed is not relivant.
35626  *
35627  * Fork - LGPL
35628  * <script type="text/javascript">
35629  */
35630  
35631 /**
35632  * @class Roo.bootstrap.layout.Basic
35633  * @extends Roo.util.Observable
35634  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35635  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35636  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35637  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35638  * @cfg {string}   region  the region that it inhabits..
35639  * @cfg {bool}   skipConfig skip config?
35640  * 
35641
35642  */
35643 Roo.bootstrap.layout.Basic = function(config){
35644     
35645     this.mgr = config.mgr;
35646     
35647     this.position = config.region;
35648     
35649     var skipConfig = config.skipConfig;
35650     
35651     this.events = {
35652         /**
35653          * @scope Roo.BasicLayoutRegion
35654          */
35655         
35656         /**
35657          * @event beforeremove
35658          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35659          * @param {Roo.LayoutRegion} this
35660          * @param {Roo.ContentPanel} panel The panel
35661          * @param {Object} e The cancel event object
35662          */
35663         "beforeremove" : true,
35664         /**
35665          * @event invalidated
35666          * Fires when the layout for this region is changed.
35667          * @param {Roo.LayoutRegion} this
35668          */
35669         "invalidated" : true,
35670         /**
35671          * @event visibilitychange
35672          * Fires when this region is shown or hidden 
35673          * @param {Roo.LayoutRegion} this
35674          * @param {Boolean} visibility true or false
35675          */
35676         "visibilitychange" : true,
35677         /**
35678          * @event paneladded
35679          * Fires when a panel is added. 
35680          * @param {Roo.LayoutRegion} this
35681          * @param {Roo.ContentPanel} panel The panel
35682          */
35683         "paneladded" : true,
35684         /**
35685          * @event panelremoved
35686          * Fires when a panel is removed. 
35687          * @param {Roo.LayoutRegion} this
35688          * @param {Roo.ContentPanel} panel The panel
35689          */
35690         "panelremoved" : true,
35691         /**
35692          * @event beforecollapse
35693          * Fires when this region before collapse.
35694          * @param {Roo.LayoutRegion} this
35695          */
35696         "beforecollapse" : true,
35697         /**
35698          * @event collapsed
35699          * Fires when this region is collapsed.
35700          * @param {Roo.LayoutRegion} this
35701          */
35702         "collapsed" : true,
35703         /**
35704          * @event expanded
35705          * Fires when this region is expanded.
35706          * @param {Roo.LayoutRegion} this
35707          */
35708         "expanded" : true,
35709         /**
35710          * @event slideshow
35711          * Fires when this region is slid into view.
35712          * @param {Roo.LayoutRegion} this
35713          */
35714         "slideshow" : true,
35715         /**
35716          * @event slidehide
35717          * Fires when this region slides out of view. 
35718          * @param {Roo.LayoutRegion} this
35719          */
35720         "slidehide" : true,
35721         /**
35722          * @event panelactivated
35723          * Fires when a panel is activated. 
35724          * @param {Roo.LayoutRegion} this
35725          * @param {Roo.ContentPanel} panel The activated panel
35726          */
35727         "panelactivated" : true,
35728         /**
35729          * @event resized
35730          * Fires when the user resizes this region. 
35731          * @param {Roo.LayoutRegion} this
35732          * @param {Number} newSize The new size (width for east/west, height for north/south)
35733          */
35734         "resized" : true
35735     };
35736     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35737     this.panels = new Roo.util.MixedCollection();
35738     this.panels.getKey = this.getPanelId.createDelegate(this);
35739     this.box = null;
35740     this.activePanel = null;
35741     // ensure listeners are added...
35742     
35743     if (config.listeners || config.events) {
35744         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35745             listeners : config.listeners || {},
35746             events : config.events || {}
35747         });
35748     }
35749     
35750     if(skipConfig !== true){
35751         this.applyConfig(config);
35752     }
35753 };
35754
35755 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35756 {
35757     getPanelId : function(p){
35758         return p.getId();
35759     },
35760     
35761     applyConfig : function(config){
35762         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35763         this.config = config;
35764         
35765     },
35766     
35767     /**
35768      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35769      * the width, for horizontal (north, south) the height.
35770      * @param {Number} newSize The new width or height
35771      */
35772     resizeTo : function(newSize){
35773         var el = this.el ? this.el :
35774                  (this.activePanel ? this.activePanel.getEl() : null);
35775         if(el){
35776             switch(this.position){
35777                 case "east":
35778                 case "west":
35779                     el.setWidth(newSize);
35780                     this.fireEvent("resized", this, newSize);
35781                 break;
35782                 case "north":
35783                 case "south":
35784                     el.setHeight(newSize);
35785                     this.fireEvent("resized", this, newSize);
35786                 break;                
35787             }
35788         }
35789     },
35790     
35791     getBox : function(){
35792         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35793     },
35794     
35795     getMargins : function(){
35796         return this.margins;
35797     },
35798     
35799     updateBox : function(box){
35800         this.box = box;
35801         var el = this.activePanel.getEl();
35802         el.dom.style.left = box.x + "px";
35803         el.dom.style.top = box.y + "px";
35804         this.activePanel.setSize(box.width, box.height);
35805     },
35806     
35807     /**
35808      * Returns the container element for this region.
35809      * @return {Roo.Element}
35810      */
35811     getEl : function(){
35812         return this.activePanel;
35813     },
35814     
35815     /**
35816      * Returns true if this region is currently visible.
35817      * @return {Boolean}
35818      */
35819     isVisible : function(){
35820         return this.activePanel ? true : false;
35821     },
35822     
35823     setActivePanel : function(panel){
35824         panel = this.getPanel(panel);
35825         if(this.activePanel && this.activePanel != panel){
35826             this.activePanel.setActiveState(false);
35827             this.activePanel.getEl().setLeftTop(-10000,-10000);
35828         }
35829         this.activePanel = panel;
35830         panel.setActiveState(true);
35831         if(this.box){
35832             panel.setSize(this.box.width, this.box.height);
35833         }
35834         this.fireEvent("panelactivated", this, panel);
35835         this.fireEvent("invalidated");
35836     },
35837     
35838     /**
35839      * Show the specified panel.
35840      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35841      * @return {Roo.ContentPanel} The shown panel or null
35842      */
35843     showPanel : function(panel){
35844         panel = this.getPanel(panel);
35845         if(panel){
35846             this.setActivePanel(panel);
35847         }
35848         return panel;
35849     },
35850     
35851     /**
35852      * Get the active panel for this region.
35853      * @return {Roo.ContentPanel} The active panel or null
35854      */
35855     getActivePanel : function(){
35856         return this.activePanel;
35857     },
35858     
35859     /**
35860      * Add the passed ContentPanel(s)
35861      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35862      * @return {Roo.ContentPanel} The panel added (if only one was added)
35863      */
35864     add : function(panel){
35865         if(arguments.length > 1){
35866             for(var i = 0, len = arguments.length; i < len; i++) {
35867                 this.add(arguments[i]);
35868             }
35869             return null;
35870         }
35871         if(this.hasPanel(panel)){
35872             this.showPanel(panel);
35873             return panel;
35874         }
35875         var el = panel.getEl();
35876         if(el.dom.parentNode != this.mgr.el.dom){
35877             this.mgr.el.dom.appendChild(el.dom);
35878         }
35879         if(panel.setRegion){
35880             panel.setRegion(this);
35881         }
35882         this.panels.add(panel);
35883         el.setStyle("position", "absolute");
35884         if(!panel.background){
35885             this.setActivePanel(panel);
35886             if(this.config.initialSize && this.panels.getCount()==1){
35887                 this.resizeTo(this.config.initialSize);
35888             }
35889         }
35890         this.fireEvent("paneladded", this, panel);
35891         return panel;
35892     },
35893     
35894     /**
35895      * Returns true if the panel is in this region.
35896      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35897      * @return {Boolean}
35898      */
35899     hasPanel : function(panel){
35900         if(typeof panel == "object"){ // must be panel obj
35901             panel = panel.getId();
35902         }
35903         return this.getPanel(panel) ? true : false;
35904     },
35905     
35906     /**
35907      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35908      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35909      * @param {Boolean} preservePanel Overrides the config preservePanel option
35910      * @return {Roo.ContentPanel} The panel that was removed
35911      */
35912     remove : function(panel, preservePanel){
35913         panel = this.getPanel(panel);
35914         if(!panel){
35915             return null;
35916         }
35917         var e = {};
35918         this.fireEvent("beforeremove", this, panel, e);
35919         if(e.cancel === true){
35920             return null;
35921         }
35922         var panelId = panel.getId();
35923         this.panels.removeKey(panelId);
35924         return panel;
35925     },
35926     
35927     /**
35928      * Returns the panel specified or null if it's not in this region.
35929      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35930      * @return {Roo.ContentPanel}
35931      */
35932     getPanel : function(id){
35933         if(typeof id == "object"){ // must be panel obj
35934             return id;
35935         }
35936         return this.panels.get(id);
35937     },
35938     
35939     /**
35940      * Returns this regions position (north/south/east/west/center).
35941      * @return {String} 
35942      */
35943     getPosition: function(){
35944         return this.position;    
35945     }
35946 });/*
35947  * Based on:
35948  * Ext JS Library 1.1.1
35949  * Copyright(c) 2006-2007, Ext JS, LLC.
35950  *
35951  * Originally Released Under LGPL - original licence link has changed is not relivant.
35952  *
35953  * Fork - LGPL
35954  * <script type="text/javascript">
35955  */
35956  
35957 /**
35958  * @class Roo.bootstrap.layout.Region
35959  * @extends Roo.bootstrap.layout.Basic
35960  * This class represents a region in a layout manager.
35961  
35962  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35963  * @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})
35964  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35965  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35966  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35967  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35968  * @cfg {String}    title           The title for the region (overrides panel titles)
35969  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35970  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35971  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35972  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35973  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35974  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35975  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35976  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35977  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35978  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35979
35980  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35981  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35982  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35983  * @cfg {Number}    width           For East/West panels
35984  * @cfg {Number}    height          For North/South panels
35985  * @cfg {Boolean}   split           To show the splitter
35986  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35987  * 
35988  * @cfg {string}   cls             Extra CSS classes to add to region
35989  * 
35990  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35991  * @cfg {string}   region  the region that it inhabits..
35992  *
35993
35994  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35995  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35996
35997  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35998  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35999  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36000  */
36001 Roo.bootstrap.layout.Region = function(config)
36002 {
36003     this.applyConfig(config);
36004
36005     var mgr = config.mgr;
36006     var pos = config.region;
36007     config.skipConfig = true;
36008     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36009     
36010     if (mgr.el) {
36011         this.onRender(mgr.el);   
36012     }
36013      
36014     this.visible = true;
36015     this.collapsed = false;
36016     this.unrendered_panels = [];
36017 };
36018
36019 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36020
36021     position: '', // set by wrapper (eg. north/south etc..)
36022     unrendered_panels : null,  // unrendered panels.
36023     
36024     tabPosition : false,
36025     
36026     mgr: false, // points to 'Border'
36027     
36028     
36029     createBody : function(){
36030         /** This region's body element 
36031         * @type Roo.Element */
36032         this.bodyEl = this.el.createChild({
36033                 tag: "div",
36034                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36035         });
36036     },
36037
36038     onRender: function(ctr, pos)
36039     {
36040         var dh = Roo.DomHelper;
36041         /** This region's container element 
36042         * @type Roo.Element */
36043         this.el = dh.append(ctr.dom, {
36044                 tag: "div",
36045                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36046             }, true);
36047         /** This region's title element 
36048         * @type Roo.Element */
36049     
36050         this.titleEl = dh.append(this.el.dom,  {
36051                 tag: "div",
36052                 unselectable: "on",
36053                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36054                 children:[
36055                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36056                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36057                 ]
36058             }, true);
36059         
36060         this.titleEl.enableDisplayMode();
36061         /** This region's title text element 
36062         * @type HTMLElement */
36063         this.titleTextEl = this.titleEl.dom.firstChild;
36064         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36065         /*
36066         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36067         this.closeBtn.enableDisplayMode();
36068         this.closeBtn.on("click", this.closeClicked, this);
36069         this.closeBtn.hide();
36070     */
36071         this.createBody(this.config);
36072         if(this.config.hideWhenEmpty){
36073             this.hide();
36074             this.on("paneladded", this.validateVisibility, this);
36075             this.on("panelremoved", this.validateVisibility, this);
36076         }
36077         if(this.autoScroll){
36078             this.bodyEl.setStyle("overflow", "auto");
36079         }else{
36080             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36081         }
36082         //if(c.titlebar !== false){
36083             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36084                 this.titleEl.hide();
36085             }else{
36086                 this.titleEl.show();
36087                 if(this.config.title){
36088                     this.titleTextEl.innerHTML = this.config.title;
36089                 }
36090             }
36091         //}
36092         if(this.config.collapsed){
36093             this.collapse(true);
36094         }
36095         if(this.config.hidden){
36096             this.hide();
36097         }
36098         
36099         if (this.unrendered_panels && this.unrendered_panels.length) {
36100             for (var i =0;i< this.unrendered_panels.length; i++) {
36101                 this.add(this.unrendered_panels[i]);
36102             }
36103             this.unrendered_panels = null;
36104             
36105         }
36106         
36107     },
36108     
36109     applyConfig : function(c)
36110     {
36111         /*
36112          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36113             var dh = Roo.DomHelper;
36114             if(c.titlebar !== false){
36115                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36116                 this.collapseBtn.on("click", this.collapse, this);
36117                 this.collapseBtn.enableDisplayMode();
36118                 /*
36119                 if(c.showPin === true || this.showPin){
36120                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36121                     this.stickBtn.enableDisplayMode();
36122                     this.stickBtn.on("click", this.expand, this);
36123                     this.stickBtn.hide();
36124                 }
36125                 
36126             }
36127             */
36128             /** This region's collapsed element
36129             * @type Roo.Element */
36130             /*
36131              *
36132             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36133                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36134             ]}, true);
36135             
36136             if(c.floatable !== false){
36137                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36138                this.collapsedEl.on("click", this.collapseClick, this);
36139             }
36140
36141             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36142                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36143                    id: "message", unselectable: "on", style:{"float":"left"}});
36144                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36145              }
36146             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36147             this.expandBtn.on("click", this.expand, this);
36148             
36149         }
36150         
36151         if(this.collapseBtn){
36152             this.collapseBtn.setVisible(c.collapsible == true);
36153         }
36154         
36155         this.cmargins = c.cmargins || this.cmargins ||
36156                          (this.position == "west" || this.position == "east" ?
36157                              {top: 0, left: 2, right:2, bottom: 0} :
36158                              {top: 2, left: 0, right:0, bottom: 2});
36159         */
36160         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36161         
36162         
36163         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36164         
36165         this.autoScroll = c.autoScroll || false;
36166         
36167         
36168        
36169         
36170         this.duration = c.duration || .30;
36171         this.slideDuration = c.slideDuration || .45;
36172         this.config = c;
36173        
36174     },
36175     /**
36176      * Returns true if this region is currently visible.
36177      * @return {Boolean}
36178      */
36179     isVisible : function(){
36180         return this.visible;
36181     },
36182
36183     /**
36184      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36185      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36186      */
36187     //setCollapsedTitle : function(title){
36188     //    title = title || "&#160;";
36189      //   if(this.collapsedTitleTextEl){
36190       //      this.collapsedTitleTextEl.innerHTML = title;
36191        // }
36192     //},
36193
36194     getBox : function(){
36195         var b;
36196       //  if(!this.collapsed){
36197             b = this.el.getBox(false, true);
36198        // }else{
36199           //  b = this.collapsedEl.getBox(false, true);
36200         //}
36201         return b;
36202     },
36203
36204     getMargins : function(){
36205         return this.margins;
36206         //return this.collapsed ? this.cmargins : this.margins;
36207     },
36208 /*
36209     highlight : function(){
36210         this.el.addClass("x-layout-panel-dragover");
36211     },
36212
36213     unhighlight : function(){
36214         this.el.removeClass("x-layout-panel-dragover");
36215     },
36216 */
36217     updateBox : function(box)
36218     {
36219         if (!this.bodyEl) {
36220             return; // not rendered yet..
36221         }
36222         
36223         this.box = box;
36224         if(!this.collapsed){
36225             this.el.dom.style.left = box.x + "px";
36226             this.el.dom.style.top = box.y + "px";
36227             this.updateBody(box.width, box.height);
36228         }else{
36229             this.collapsedEl.dom.style.left = box.x + "px";
36230             this.collapsedEl.dom.style.top = box.y + "px";
36231             this.collapsedEl.setSize(box.width, box.height);
36232         }
36233         if(this.tabs){
36234             this.tabs.autoSizeTabs();
36235         }
36236     },
36237
36238     updateBody : function(w, h)
36239     {
36240         if(w !== null){
36241             this.el.setWidth(w);
36242             w -= this.el.getBorderWidth("rl");
36243             if(this.config.adjustments){
36244                 w += this.config.adjustments[0];
36245             }
36246         }
36247         if(h !== null && h > 0){
36248             this.el.setHeight(h);
36249             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36250             h -= this.el.getBorderWidth("tb");
36251             if(this.config.adjustments){
36252                 h += this.config.adjustments[1];
36253             }
36254             this.bodyEl.setHeight(h);
36255             if(this.tabs){
36256                 h = this.tabs.syncHeight(h);
36257             }
36258         }
36259         if(this.panelSize){
36260             w = w !== null ? w : this.panelSize.width;
36261             h = h !== null ? h : this.panelSize.height;
36262         }
36263         if(this.activePanel){
36264             var el = this.activePanel.getEl();
36265             w = w !== null ? w : el.getWidth();
36266             h = h !== null ? h : el.getHeight();
36267             this.panelSize = {width: w, height: h};
36268             this.activePanel.setSize(w, h);
36269         }
36270         if(Roo.isIE && this.tabs){
36271             this.tabs.el.repaint();
36272         }
36273     },
36274
36275     /**
36276      * Returns the container element for this region.
36277      * @return {Roo.Element}
36278      */
36279     getEl : function(){
36280         return this.el;
36281     },
36282
36283     /**
36284      * Hides this region.
36285      */
36286     hide : function(){
36287         //if(!this.collapsed){
36288             this.el.dom.style.left = "-2000px";
36289             this.el.hide();
36290         //}else{
36291          //   this.collapsedEl.dom.style.left = "-2000px";
36292          //   this.collapsedEl.hide();
36293        // }
36294         this.visible = false;
36295         this.fireEvent("visibilitychange", this, false);
36296     },
36297
36298     /**
36299      * Shows this region if it was previously hidden.
36300      */
36301     show : function(){
36302         //if(!this.collapsed){
36303             this.el.show();
36304         //}else{
36305         //    this.collapsedEl.show();
36306        // }
36307         this.visible = true;
36308         this.fireEvent("visibilitychange", this, true);
36309     },
36310 /*
36311     closeClicked : function(){
36312         if(this.activePanel){
36313             this.remove(this.activePanel);
36314         }
36315     },
36316
36317     collapseClick : function(e){
36318         if(this.isSlid){
36319            e.stopPropagation();
36320            this.slideIn();
36321         }else{
36322            e.stopPropagation();
36323            this.slideOut();
36324         }
36325     },
36326 */
36327     /**
36328      * Collapses this region.
36329      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36330      */
36331     /*
36332     collapse : function(skipAnim, skipCheck = false){
36333         if(this.collapsed) {
36334             return;
36335         }
36336         
36337         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36338             
36339             this.collapsed = true;
36340             if(this.split){
36341                 this.split.el.hide();
36342             }
36343             if(this.config.animate && skipAnim !== true){
36344                 this.fireEvent("invalidated", this);
36345                 this.animateCollapse();
36346             }else{
36347                 this.el.setLocation(-20000,-20000);
36348                 this.el.hide();
36349                 this.collapsedEl.show();
36350                 this.fireEvent("collapsed", this);
36351                 this.fireEvent("invalidated", this);
36352             }
36353         }
36354         
36355     },
36356 */
36357     animateCollapse : function(){
36358         // overridden
36359     },
36360
36361     /**
36362      * Expands this region if it was previously collapsed.
36363      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36364      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36365      */
36366     /*
36367     expand : function(e, skipAnim){
36368         if(e) {
36369             e.stopPropagation();
36370         }
36371         if(!this.collapsed || this.el.hasActiveFx()) {
36372             return;
36373         }
36374         if(this.isSlid){
36375             this.afterSlideIn();
36376             skipAnim = true;
36377         }
36378         this.collapsed = false;
36379         if(this.config.animate && skipAnim !== true){
36380             this.animateExpand();
36381         }else{
36382             this.el.show();
36383             if(this.split){
36384                 this.split.el.show();
36385             }
36386             this.collapsedEl.setLocation(-2000,-2000);
36387             this.collapsedEl.hide();
36388             this.fireEvent("invalidated", this);
36389             this.fireEvent("expanded", this);
36390         }
36391     },
36392 */
36393     animateExpand : function(){
36394         // overridden
36395     },
36396
36397     initTabs : function()
36398     {
36399         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36400         
36401         var ts = new Roo.bootstrap.panel.Tabs({
36402             el: this.bodyEl.dom,
36403             region : this,
36404             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36405             disableTooltips: this.config.disableTabTips,
36406             toolbar : this.config.toolbar
36407         });
36408         
36409         if(this.config.hideTabs){
36410             ts.stripWrap.setDisplayed(false);
36411         }
36412         this.tabs = ts;
36413         ts.resizeTabs = this.config.resizeTabs === true;
36414         ts.minTabWidth = this.config.minTabWidth || 40;
36415         ts.maxTabWidth = this.config.maxTabWidth || 250;
36416         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36417         ts.monitorResize = false;
36418         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36419         ts.bodyEl.addClass('roo-layout-tabs-body');
36420         this.panels.each(this.initPanelAsTab, this);
36421     },
36422
36423     initPanelAsTab : function(panel){
36424         var ti = this.tabs.addTab(
36425             panel.getEl().id,
36426             panel.getTitle(),
36427             null,
36428             this.config.closeOnTab && panel.isClosable(),
36429             panel.tpl
36430         );
36431         if(panel.tabTip !== undefined){
36432             ti.setTooltip(panel.tabTip);
36433         }
36434         ti.on("activate", function(){
36435               this.setActivePanel(panel);
36436         }, this);
36437         
36438         if(this.config.closeOnTab){
36439             ti.on("beforeclose", function(t, e){
36440                 e.cancel = true;
36441                 this.remove(panel);
36442             }, this);
36443         }
36444         
36445         panel.tabItem = ti;
36446         
36447         return ti;
36448     },
36449
36450     updatePanelTitle : function(panel, title)
36451     {
36452         if(this.activePanel == panel){
36453             this.updateTitle(title);
36454         }
36455         if(this.tabs){
36456             var ti = this.tabs.getTab(panel.getEl().id);
36457             ti.setText(title);
36458             if(panel.tabTip !== undefined){
36459                 ti.setTooltip(panel.tabTip);
36460             }
36461         }
36462     },
36463
36464     updateTitle : function(title){
36465         if(this.titleTextEl && !this.config.title){
36466             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36467         }
36468     },
36469
36470     setActivePanel : function(panel)
36471     {
36472         panel = this.getPanel(panel);
36473         if(this.activePanel && this.activePanel != panel){
36474             if(this.activePanel.setActiveState(false) === false){
36475                 return;
36476             }
36477         }
36478         this.activePanel = panel;
36479         panel.setActiveState(true);
36480         if(this.panelSize){
36481             panel.setSize(this.panelSize.width, this.panelSize.height);
36482         }
36483         if(this.closeBtn){
36484             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36485         }
36486         this.updateTitle(panel.getTitle());
36487         if(this.tabs){
36488             this.fireEvent("invalidated", this);
36489         }
36490         this.fireEvent("panelactivated", this, panel);
36491     },
36492
36493     /**
36494      * Shows the specified panel.
36495      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36496      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36497      */
36498     showPanel : function(panel)
36499     {
36500         panel = this.getPanel(panel);
36501         if(panel){
36502             if(this.tabs){
36503                 var tab = this.tabs.getTab(panel.getEl().id);
36504                 if(tab.isHidden()){
36505                     this.tabs.unhideTab(tab.id);
36506                 }
36507                 tab.activate();
36508             }else{
36509                 this.setActivePanel(panel);
36510             }
36511         }
36512         return panel;
36513     },
36514
36515     /**
36516      * Get the active panel for this region.
36517      * @return {Roo.ContentPanel} The active panel or null
36518      */
36519     getActivePanel : function(){
36520         return this.activePanel;
36521     },
36522
36523     validateVisibility : function(){
36524         if(this.panels.getCount() < 1){
36525             this.updateTitle("&#160;");
36526             this.closeBtn.hide();
36527             this.hide();
36528         }else{
36529             if(!this.isVisible()){
36530                 this.show();
36531             }
36532         }
36533     },
36534
36535     /**
36536      * Adds the passed ContentPanel(s) to this region.
36537      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36538      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36539      */
36540     add : function(panel)
36541     {
36542         if(arguments.length > 1){
36543             for(var i = 0, len = arguments.length; i < len; i++) {
36544                 this.add(arguments[i]);
36545             }
36546             return null;
36547         }
36548         
36549         // if we have not been rendered yet, then we can not really do much of this..
36550         if (!this.bodyEl) {
36551             this.unrendered_panels.push(panel);
36552             return panel;
36553         }
36554         
36555         
36556         
36557         
36558         if(this.hasPanel(panel)){
36559             this.showPanel(panel);
36560             return panel;
36561         }
36562         panel.setRegion(this);
36563         this.panels.add(panel);
36564        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36565             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36566             // and hide them... ???
36567             this.bodyEl.dom.appendChild(panel.getEl().dom);
36568             if(panel.background !== true){
36569                 this.setActivePanel(panel);
36570             }
36571             this.fireEvent("paneladded", this, panel);
36572             return panel;
36573         }
36574         */
36575         if(!this.tabs){
36576             this.initTabs();
36577         }else{
36578             this.initPanelAsTab(panel);
36579         }
36580         
36581         
36582         if(panel.background !== true){
36583             this.tabs.activate(panel.getEl().id);
36584         }
36585         this.fireEvent("paneladded", this, panel);
36586         return panel;
36587     },
36588
36589     /**
36590      * Hides the tab for the specified panel.
36591      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36592      */
36593     hidePanel : function(panel){
36594         if(this.tabs && (panel = this.getPanel(panel))){
36595             this.tabs.hideTab(panel.getEl().id);
36596         }
36597     },
36598
36599     /**
36600      * Unhides the tab for a previously hidden panel.
36601      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36602      */
36603     unhidePanel : function(panel){
36604         if(this.tabs && (panel = this.getPanel(panel))){
36605             this.tabs.unhideTab(panel.getEl().id);
36606         }
36607     },
36608
36609     clearPanels : function(){
36610         while(this.panels.getCount() > 0){
36611              this.remove(this.panels.first());
36612         }
36613     },
36614
36615     /**
36616      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36617      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36618      * @param {Boolean} preservePanel Overrides the config preservePanel option
36619      * @return {Roo.ContentPanel} The panel that was removed
36620      */
36621     remove : function(panel, preservePanel)
36622     {
36623         panel = this.getPanel(panel);
36624         if(!panel){
36625             return null;
36626         }
36627         var e = {};
36628         this.fireEvent("beforeremove", this, panel, e);
36629         if(e.cancel === true){
36630             return null;
36631         }
36632         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36633         var panelId = panel.getId();
36634         this.panels.removeKey(panelId);
36635         if(preservePanel){
36636             document.body.appendChild(panel.getEl().dom);
36637         }
36638         if(this.tabs){
36639             this.tabs.removeTab(panel.getEl().id);
36640         }else if (!preservePanel){
36641             this.bodyEl.dom.removeChild(panel.getEl().dom);
36642         }
36643         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36644             var p = this.panels.first();
36645             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36646             tempEl.appendChild(p.getEl().dom);
36647             this.bodyEl.update("");
36648             this.bodyEl.dom.appendChild(p.getEl().dom);
36649             tempEl = null;
36650             this.updateTitle(p.getTitle());
36651             this.tabs = null;
36652             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36653             this.setActivePanel(p);
36654         }
36655         panel.setRegion(null);
36656         if(this.activePanel == panel){
36657             this.activePanel = null;
36658         }
36659         if(this.config.autoDestroy !== false && preservePanel !== true){
36660             try{panel.destroy();}catch(e){}
36661         }
36662         this.fireEvent("panelremoved", this, panel);
36663         return panel;
36664     },
36665
36666     /**
36667      * Returns the TabPanel component used by this region
36668      * @return {Roo.TabPanel}
36669      */
36670     getTabs : function(){
36671         return this.tabs;
36672     },
36673
36674     createTool : function(parentEl, className){
36675         var btn = Roo.DomHelper.append(parentEl, {
36676             tag: "div",
36677             cls: "x-layout-tools-button",
36678             children: [ {
36679                 tag: "div",
36680                 cls: "roo-layout-tools-button-inner " + className,
36681                 html: "&#160;"
36682             }]
36683         }, true);
36684         btn.addClassOnOver("roo-layout-tools-button-over");
36685         return btn;
36686     }
36687 });/*
36688  * Based on:
36689  * Ext JS Library 1.1.1
36690  * Copyright(c) 2006-2007, Ext JS, LLC.
36691  *
36692  * Originally Released Under LGPL - original licence link has changed is not relivant.
36693  *
36694  * Fork - LGPL
36695  * <script type="text/javascript">
36696  */
36697  
36698
36699
36700 /**
36701  * @class Roo.SplitLayoutRegion
36702  * @extends Roo.LayoutRegion
36703  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36704  */
36705 Roo.bootstrap.layout.Split = function(config){
36706     this.cursor = config.cursor;
36707     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36708 };
36709
36710 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36711 {
36712     splitTip : "Drag to resize.",
36713     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36714     useSplitTips : false,
36715
36716     applyConfig : function(config){
36717         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36718     },
36719     
36720     onRender : function(ctr,pos) {
36721         
36722         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36723         if(!this.config.split){
36724             return;
36725         }
36726         if(!this.split){
36727             
36728             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36729                             tag: "div",
36730                             id: this.el.id + "-split",
36731                             cls: "roo-layout-split roo-layout-split-"+this.position,
36732                             html: "&#160;"
36733             });
36734             /** The SplitBar for this region 
36735             * @type Roo.SplitBar */
36736             // does not exist yet...
36737             Roo.log([this.position, this.orientation]);
36738             
36739             this.split = new Roo.bootstrap.SplitBar({
36740                 dragElement : splitEl,
36741                 resizingElement: this.el,
36742                 orientation : this.orientation
36743             });
36744             
36745             this.split.on("moved", this.onSplitMove, this);
36746             this.split.useShim = this.config.useShim === true;
36747             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36748             if(this.useSplitTips){
36749                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36750             }
36751             //if(config.collapsible){
36752             //    this.split.el.on("dblclick", this.collapse,  this);
36753             //}
36754         }
36755         if(typeof this.config.minSize != "undefined"){
36756             this.split.minSize = this.config.minSize;
36757         }
36758         if(typeof this.config.maxSize != "undefined"){
36759             this.split.maxSize = this.config.maxSize;
36760         }
36761         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36762             this.hideSplitter();
36763         }
36764         
36765     },
36766
36767     getHMaxSize : function(){
36768          var cmax = this.config.maxSize || 10000;
36769          var center = this.mgr.getRegion("center");
36770          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36771     },
36772
36773     getVMaxSize : function(){
36774          var cmax = this.config.maxSize || 10000;
36775          var center = this.mgr.getRegion("center");
36776          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36777     },
36778
36779     onSplitMove : function(split, newSize){
36780         this.fireEvent("resized", this, newSize);
36781     },
36782     
36783     /** 
36784      * Returns the {@link Roo.SplitBar} for this region.
36785      * @return {Roo.SplitBar}
36786      */
36787     getSplitBar : function(){
36788         return this.split;
36789     },
36790     
36791     hide : function(){
36792         this.hideSplitter();
36793         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36794     },
36795
36796     hideSplitter : function(){
36797         if(this.split){
36798             this.split.el.setLocation(-2000,-2000);
36799             this.split.el.hide();
36800         }
36801     },
36802
36803     show : function(){
36804         if(this.split){
36805             this.split.el.show();
36806         }
36807         Roo.bootstrap.layout.Split.superclass.show.call(this);
36808     },
36809     
36810     beforeSlide: function(){
36811         if(Roo.isGecko){// firefox overflow auto bug workaround
36812             this.bodyEl.clip();
36813             if(this.tabs) {
36814                 this.tabs.bodyEl.clip();
36815             }
36816             if(this.activePanel){
36817                 this.activePanel.getEl().clip();
36818                 
36819                 if(this.activePanel.beforeSlide){
36820                     this.activePanel.beforeSlide();
36821                 }
36822             }
36823         }
36824     },
36825     
36826     afterSlide : function(){
36827         if(Roo.isGecko){// firefox overflow auto bug workaround
36828             this.bodyEl.unclip();
36829             if(this.tabs) {
36830                 this.tabs.bodyEl.unclip();
36831             }
36832             if(this.activePanel){
36833                 this.activePanel.getEl().unclip();
36834                 if(this.activePanel.afterSlide){
36835                     this.activePanel.afterSlide();
36836                 }
36837             }
36838         }
36839     },
36840
36841     initAutoHide : function(){
36842         if(this.autoHide !== false){
36843             if(!this.autoHideHd){
36844                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36845                 this.autoHideHd = {
36846                     "mouseout": function(e){
36847                         if(!e.within(this.el, true)){
36848                             st.delay(500);
36849                         }
36850                     },
36851                     "mouseover" : function(e){
36852                         st.cancel();
36853                     },
36854                     scope : this
36855                 };
36856             }
36857             this.el.on(this.autoHideHd);
36858         }
36859     },
36860
36861     clearAutoHide : function(){
36862         if(this.autoHide !== false){
36863             this.el.un("mouseout", this.autoHideHd.mouseout);
36864             this.el.un("mouseover", this.autoHideHd.mouseover);
36865         }
36866     },
36867
36868     clearMonitor : function(){
36869         Roo.get(document).un("click", this.slideInIf, this);
36870     },
36871
36872     // these names are backwards but not changed for compat
36873     slideOut : function(){
36874         if(this.isSlid || this.el.hasActiveFx()){
36875             return;
36876         }
36877         this.isSlid = true;
36878         if(this.collapseBtn){
36879             this.collapseBtn.hide();
36880         }
36881         this.closeBtnState = this.closeBtn.getStyle('display');
36882         this.closeBtn.hide();
36883         if(this.stickBtn){
36884             this.stickBtn.show();
36885         }
36886         this.el.show();
36887         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36888         this.beforeSlide();
36889         this.el.setStyle("z-index", 10001);
36890         this.el.slideIn(this.getSlideAnchor(), {
36891             callback: function(){
36892                 this.afterSlide();
36893                 this.initAutoHide();
36894                 Roo.get(document).on("click", this.slideInIf, this);
36895                 this.fireEvent("slideshow", this);
36896             },
36897             scope: this,
36898             block: true
36899         });
36900     },
36901
36902     afterSlideIn : function(){
36903         this.clearAutoHide();
36904         this.isSlid = false;
36905         this.clearMonitor();
36906         this.el.setStyle("z-index", "");
36907         if(this.collapseBtn){
36908             this.collapseBtn.show();
36909         }
36910         this.closeBtn.setStyle('display', this.closeBtnState);
36911         if(this.stickBtn){
36912             this.stickBtn.hide();
36913         }
36914         this.fireEvent("slidehide", this);
36915     },
36916
36917     slideIn : function(cb){
36918         if(!this.isSlid || this.el.hasActiveFx()){
36919             Roo.callback(cb);
36920             return;
36921         }
36922         this.isSlid = false;
36923         this.beforeSlide();
36924         this.el.slideOut(this.getSlideAnchor(), {
36925             callback: function(){
36926                 this.el.setLeftTop(-10000, -10000);
36927                 this.afterSlide();
36928                 this.afterSlideIn();
36929                 Roo.callback(cb);
36930             },
36931             scope: this,
36932             block: true
36933         });
36934     },
36935     
36936     slideInIf : function(e){
36937         if(!e.within(this.el)){
36938             this.slideIn();
36939         }
36940     },
36941
36942     animateCollapse : function(){
36943         this.beforeSlide();
36944         this.el.setStyle("z-index", 20000);
36945         var anchor = this.getSlideAnchor();
36946         this.el.slideOut(anchor, {
36947             callback : function(){
36948                 this.el.setStyle("z-index", "");
36949                 this.collapsedEl.slideIn(anchor, {duration:.3});
36950                 this.afterSlide();
36951                 this.el.setLocation(-10000,-10000);
36952                 this.el.hide();
36953                 this.fireEvent("collapsed", this);
36954             },
36955             scope: this,
36956             block: true
36957         });
36958     },
36959
36960     animateExpand : function(){
36961         this.beforeSlide();
36962         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36963         this.el.setStyle("z-index", 20000);
36964         this.collapsedEl.hide({
36965             duration:.1
36966         });
36967         this.el.slideIn(this.getSlideAnchor(), {
36968             callback : function(){
36969                 this.el.setStyle("z-index", "");
36970                 this.afterSlide();
36971                 if(this.split){
36972                     this.split.el.show();
36973                 }
36974                 this.fireEvent("invalidated", this);
36975                 this.fireEvent("expanded", this);
36976             },
36977             scope: this,
36978             block: true
36979         });
36980     },
36981
36982     anchors : {
36983         "west" : "left",
36984         "east" : "right",
36985         "north" : "top",
36986         "south" : "bottom"
36987     },
36988
36989     sanchors : {
36990         "west" : "l",
36991         "east" : "r",
36992         "north" : "t",
36993         "south" : "b"
36994     },
36995
36996     canchors : {
36997         "west" : "tl-tr",
36998         "east" : "tr-tl",
36999         "north" : "tl-bl",
37000         "south" : "bl-tl"
37001     },
37002
37003     getAnchor : function(){
37004         return this.anchors[this.position];
37005     },
37006
37007     getCollapseAnchor : function(){
37008         return this.canchors[this.position];
37009     },
37010
37011     getSlideAnchor : function(){
37012         return this.sanchors[this.position];
37013     },
37014
37015     getAlignAdj : function(){
37016         var cm = this.cmargins;
37017         switch(this.position){
37018             case "west":
37019                 return [0, 0];
37020             break;
37021             case "east":
37022                 return [0, 0];
37023             break;
37024             case "north":
37025                 return [0, 0];
37026             break;
37027             case "south":
37028                 return [0, 0];
37029             break;
37030         }
37031     },
37032
37033     getExpandAdj : function(){
37034         var c = this.collapsedEl, cm = this.cmargins;
37035         switch(this.position){
37036             case "west":
37037                 return [-(cm.right+c.getWidth()+cm.left), 0];
37038             break;
37039             case "east":
37040                 return [cm.right+c.getWidth()+cm.left, 0];
37041             break;
37042             case "north":
37043                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37044             break;
37045             case "south":
37046                 return [0, cm.top+cm.bottom+c.getHeight()];
37047             break;
37048         }
37049     }
37050 });/*
37051  * Based on:
37052  * Ext JS Library 1.1.1
37053  * Copyright(c) 2006-2007, Ext JS, LLC.
37054  *
37055  * Originally Released Under LGPL - original licence link has changed is not relivant.
37056  *
37057  * Fork - LGPL
37058  * <script type="text/javascript">
37059  */
37060 /*
37061  * These classes are private internal classes
37062  */
37063 Roo.bootstrap.layout.Center = function(config){
37064     config.region = "center";
37065     Roo.bootstrap.layout.Region.call(this, config);
37066     this.visible = true;
37067     this.minWidth = config.minWidth || 20;
37068     this.minHeight = config.minHeight || 20;
37069 };
37070
37071 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37072     hide : function(){
37073         // center panel can't be hidden
37074     },
37075     
37076     show : function(){
37077         // center panel can't be hidden
37078     },
37079     
37080     getMinWidth: function(){
37081         return this.minWidth;
37082     },
37083     
37084     getMinHeight: function(){
37085         return this.minHeight;
37086     }
37087 });
37088
37089
37090
37091
37092  
37093
37094
37095
37096
37097
37098
37099 Roo.bootstrap.layout.North = function(config)
37100 {
37101     config.region = 'north';
37102     config.cursor = 'n-resize';
37103     
37104     Roo.bootstrap.layout.Split.call(this, config);
37105     
37106     
37107     if(this.split){
37108         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37109         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37110         this.split.el.addClass("roo-layout-split-v");
37111     }
37112     var size = config.initialSize || config.height;
37113     if(typeof size != "undefined"){
37114         this.el.setHeight(size);
37115     }
37116 };
37117 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37118 {
37119     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37120     
37121     
37122     
37123     getBox : function(){
37124         if(this.collapsed){
37125             return this.collapsedEl.getBox();
37126         }
37127         var box = this.el.getBox();
37128         if(this.split){
37129             box.height += this.split.el.getHeight();
37130         }
37131         return box;
37132     },
37133     
37134     updateBox : function(box){
37135         if(this.split && !this.collapsed){
37136             box.height -= this.split.el.getHeight();
37137             this.split.el.setLeft(box.x);
37138             this.split.el.setTop(box.y+box.height);
37139             this.split.el.setWidth(box.width);
37140         }
37141         if(this.collapsed){
37142             this.updateBody(box.width, null);
37143         }
37144         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37145     }
37146 });
37147
37148
37149
37150
37151
37152 Roo.bootstrap.layout.South = function(config){
37153     config.region = 'south';
37154     config.cursor = 's-resize';
37155     Roo.bootstrap.layout.Split.call(this, config);
37156     if(this.split){
37157         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37158         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37159         this.split.el.addClass("roo-layout-split-v");
37160     }
37161     var size = config.initialSize || config.height;
37162     if(typeof size != "undefined"){
37163         this.el.setHeight(size);
37164     }
37165 };
37166
37167 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37168     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37169     getBox : function(){
37170         if(this.collapsed){
37171             return this.collapsedEl.getBox();
37172         }
37173         var box = this.el.getBox();
37174         if(this.split){
37175             var sh = this.split.el.getHeight();
37176             box.height += sh;
37177             box.y -= sh;
37178         }
37179         return box;
37180     },
37181     
37182     updateBox : function(box){
37183         if(this.split && !this.collapsed){
37184             var sh = this.split.el.getHeight();
37185             box.height -= sh;
37186             box.y += sh;
37187             this.split.el.setLeft(box.x);
37188             this.split.el.setTop(box.y-sh);
37189             this.split.el.setWidth(box.width);
37190         }
37191         if(this.collapsed){
37192             this.updateBody(box.width, null);
37193         }
37194         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37195     }
37196 });
37197
37198 Roo.bootstrap.layout.East = function(config){
37199     config.region = "east";
37200     config.cursor = "e-resize";
37201     Roo.bootstrap.layout.Split.call(this, config);
37202     if(this.split){
37203         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37204         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37205         this.split.el.addClass("roo-layout-split-h");
37206     }
37207     var size = config.initialSize || config.width;
37208     if(typeof size != "undefined"){
37209         this.el.setWidth(size);
37210     }
37211 };
37212 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37213     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37214     getBox : function(){
37215         if(this.collapsed){
37216             return this.collapsedEl.getBox();
37217         }
37218         var box = this.el.getBox();
37219         if(this.split){
37220             var sw = this.split.el.getWidth();
37221             box.width += sw;
37222             box.x -= sw;
37223         }
37224         return box;
37225     },
37226
37227     updateBox : function(box){
37228         if(this.split && !this.collapsed){
37229             var sw = this.split.el.getWidth();
37230             box.width -= sw;
37231             this.split.el.setLeft(box.x);
37232             this.split.el.setTop(box.y);
37233             this.split.el.setHeight(box.height);
37234             box.x += sw;
37235         }
37236         if(this.collapsed){
37237             this.updateBody(null, box.height);
37238         }
37239         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37240     }
37241 });
37242
37243 Roo.bootstrap.layout.West = function(config){
37244     config.region = "west";
37245     config.cursor = "w-resize";
37246     
37247     Roo.bootstrap.layout.Split.call(this, config);
37248     if(this.split){
37249         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37250         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37251         this.split.el.addClass("roo-layout-split-h");
37252     }
37253     
37254 };
37255 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37256     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37257     
37258     onRender: function(ctr, pos)
37259     {
37260         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37261         var size = this.config.initialSize || this.config.width;
37262         if(typeof size != "undefined"){
37263             this.el.setWidth(size);
37264         }
37265     },
37266     
37267     getBox : function(){
37268         if(this.collapsed){
37269             return this.collapsedEl.getBox();
37270         }
37271         var box = this.el.getBox();
37272         if(this.split){
37273             box.width += this.split.el.getWidth();
37274         }
37275         return box;
37276     },
37277     
37278     updateBox : function(box){
37279         if(this.split && !this.collapsed){
37280             var sw = this.split.el.getWidth();
37281             box.width -= sw;
37282             this.split.el.setLeft(box.x+box.width);
37283             this.split.el.setTop(box.y);
37284             this.split.el.setHeight(box.height);
37285         }
37286         if(this.collapsed){
37287             this.updateBody(null, box.height);
37288         }
37289         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37290     }
37291 });Roo.namespace("Roo.bootstrap.panel");/*
37292  * Based on:
37293  * Ext JS Library 1.1.1
37294  * Copyright(c) 2006-2007, Ext JS, LLC.
37295  *
37296  * Originally Released Under LGPL - original licence link has changed is not relivant.
37297  *
37298  * Fork - LGPL
37299  * <script type="text/javascript">
37300  */
37301 /**
37302  * @class Roo.ContentPanel
37303  * @extends Roo.util.Observable
37304  * A basic ContentPanel element.
37305  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37306  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37307  * @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
37308  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37309  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37310  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37311  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37312  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37313  * @cfg {String} title          The title for this panel
37314  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37315  * @cfg {String} url            Calls {@link #setUrl} with this value
37316  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37317  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37318  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37319  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37320  * @cfg {Boolean} badges render the badges
37321
37322  * @constructor
37323  * Create a new ContentPanel.
37324  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37325  * @param {String/Object} config A string to set only the title or a config object
37326  * @param {String} content (optional) Set the HTML content for this panel
37327  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37328  */
37329 Roo.bootstrap.panel.Content = function( config){
37330     
37331     this.tpl = config.tpl || false;
37332     
37333     var el = config.el;
37334     var content = config.content;
37335
37336     if(config.autoCreate){ // xtype is available if this is called from factory
37337         el = Roo.id();
37338     }
37339     this.el = Roo.get(el);
37340     if(!this.el && config && config.autoCreate){
37341         if(typeof config.autoCreate == "object"){
37342             if(!config.autoCreate.id){
37343                 config.autoCreate.id = config.id||el;
37344             }
37345             this.el = Roo.DomHelper.append(document.body,
37346                         config.autoCreate, true);
37347         }else{
37348             var elcfg =  {   tag: "div",
37349                             cls: "roo-layout-inactive-content",
37350                             id: config.id||el
37351                             };
37352             if (config.html) {
37353                 elcfg.html = config.html;
37354                 
37355             }
37356                         
37357             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37358         }
37359     } 
37360     this.closable = false;
37361     this.loaded = false;
37362     this.active = false;
37363    
37364       
37365     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37366         
37367         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37368         
37369         this.wrapEl = this.el; //this.el.wrap();
37370         var ti = [];
37371         if (config.toolbar.items) {
37372             ti = config.toolbar.items ;
37373             delete config.toolbar.items ;
37374         }
37375         
37376         var nitems = [];
37377         this.toolbar.render(this.wrapEl, 'before');
37378         for(var i =0;i < ti.length;i++) {
37379           //  Roo.log(['add child', items[i]]);
37380             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37381         }
37382         this.toolbar.items = nitems;
37383         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37384         delete config.toolbar;
37385         
37386     }
37387     /*
37388     // xtype created footer. - not sure if will work as we normally have to render first..
37389     if (this.footer && !this.footer.el && this.footer.xtype) {
37390         if (!this.wrapEl) {
37391             this.wrapEl = this.el.wrap();
37392         }
37393     
37394         this.footer.container = this.wrapEl.createChild();
37395          
37396         this.footer = Roo.factory(this.footer, Roo);
37397         
37398     }
37399     */
37400     
37401      if(typeof config == "string"){
37402         this.title = config;
37403     }else{
37404         Roo.apply(this, config);
37405     }
37406     
37407     if(this.resizeEl){
37408         this.resizeEl = Roo.get(this.resizeEl, true);
37409     }else{
37410         this.resizeEl = this.el;
37411     }
37412     // handle view.xtype
37413     
37414  
37415     
37416     
37417     this.addEvents({
37418         /**
37419          * @event activate
37420          * Fires when this panel is activated. 
37421          * @param {Roo.ContentPanel} this
37422          */
37423         "activate" : true,
37424         /**
37425          * @event deactivate
37426          * Fires when this panel is activated. 
37427          * @param {Roo.ContentPanel} this
37428          */
37429         "deactivate" : true,
37430
37431         /**
37432          * @event resize
37433          * Fires when this panel is resized if fitToFrame is true.
37434          * @param {Roo.ContentPanel} this
37435          * @param {Number} width The width after any component adjustments
37436          * @param {Number} height The height after any component adjustments
37437          */
37438         "resize" : true,
37439         
37440          /**
37441          * @event render
37442          * Fires when this tab is created
37443          * @param {Roo.ContentPanel} this
37444          */
37445         "render" : true
37446         
37447         
37448         
37449     });
37450     
37451
37452     
37453     
37454     if(this.autoScroll){
37455         this.resizeEl.setStyle("overflow", "auto");
37456     } else {
37457         // fix randome scrolling
37458         //this.el.on('scroll', function() {
37459         //    Roo.log('fix random scolling');
37460         //    this.scrollTo('top',0); 
37461         //});
37462     }
37463     content = content || this.content;
37464     if(content){
37465         this.setContent(content);
37466     }
37467     if(config && config.url){
37468         this.setUrl(this.url, this.params, this.loadOnce);
37469     }
37470     
37471     
37472     
37473     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37474     
37475     if (this.view && typeof(this.view.xtype) != 'undefined') {
37476         this.view.el = this.el.appendChild(document.createElement("div"));
37477         this.view = Roo.factory(this.view); 
37478         this.view.render  &&  this.view.render(false, '');  
37479     }
37480     
37481     
37482     this.fireEvent('render', this);
37483 };
37484
37485 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37486     
37487     tabTip : '',
37488     
37489     setRegion : function(region){
37490         this.region = region;
37491         this.setActiveClass(region && !this.background);
37492     },
37493     
37494     
37495     setActiveClass: function(state)
37496     {
37497         if(state){
37498            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37499            this.el.setStyle('position','relative');
37500         }else{
37501            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37502            this.el.setStyle('position', 'absolute');
37503         } 
37504     },
37505     
37506     /**
37507      * Returns the toolbar for this Panel if one was configured. 
37508      * @return {Roo.Toolbar} 
37509      */
37510     getToolbar : function(){
37511         return this.toolbar;
37512     },
37513     
37514     setActiveState : function(active)
37515     {
37516         this.active = active;
37517         this.setActiveClass(active);
37518         if(!active){
37519             if(this.fireEvent("deactivate", this) === false){
37520                 return false;
37521             }
37522             return true;
37523         }
37524         this.fireEvent("activate", this);
37525         return true;
37526     },
37527     /**
37528      * Updates this panel's element
37529      * @param {String} content The new content
37530      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37531     */
37532     setContent : function(content, loadScripts){
37533         this.el.update(content, loadScripts);
37534     },
37535
37536     ignoreResize : function(w, h){
37537         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37538             return true;
37539         }else{
37540             this.lastSize = {width: w, height: h};
37541             return false;
37542         }
37543     },
37544     /**
37545      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37546      * @return {Roo.UpdateManager} The UpdateManager
37547      */
37548     getUpdateManager : function(){
37549         return this.el.getUpdateManager();
37550     },
37551      /**
37552      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37553      * @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:
37554 <pre><code>
37555 panel.load({
37556     url: "your-url.php",
37557     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37558     callback: yourFunction,
37559     scope: yourObject, //(optional scope)
37560     discardUrl: false,
37561     nocache: false,
37562     text: "Loading...",
37563     timeout: 30,
37564     scripts: false
37565 });
37566 </code></pre>
37567      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37568      * 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.
37569      * @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}
37570      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37571      * @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.
37572      * @return {Roo.ContentPanel} this
37573      */
37574     load : function(){
37575         var um = this.el.getUpdateManager();
37576         um.update.apply(um, arguments);
37577         return this;
37578     },
37579
37580
37581     /**
37582      * 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.
37583      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37584      * @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)
37585      * @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)
37586      * @return {Roo.UpdateManager} The UpdateManager
37587      */
37588     setUrl : function(url, params, loadOnce){
37589         if(this.refreshDelegate){
37590             this.removeListener("activate", this.refreshDelegate);
37591         }
37592         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37593         this.on("activate", this.refreshDelegate);
37594         return this.el.getUpdateManager();
37595     },
37596     
37597     _handleRefresh : function(url, params, loadOnce){
37598         if(!loadOnce || !this.loaded){
37599             var updater = this.el.getUpdateManager();
37600             updater.update(url, params, this._setLoaded.createDelegate(this));
37601         }
37602     },
37603     
37604     _setLoaded : function(){
37605         this.loaded = true;
37606     }, 
37607     
37608     /**
37609      * Returns this panel's id
37610      * @return {String} 
37611      */
37612     getId : function(){
37613         return this.el.id;
37614     },
37615     
37616     /** 
37617      * Returns this panel's element - used by regiosn to add.
37618      * @return {Roo.Element} 
37619      */
37620     getEl : function(){
37621         return this.wrapEl || this.el;
37622     },
37623     
37624    
37625     
37626     adjustForComponents : function(width, height)
37627     {
37628         //Roo.log('adjustForComponents ');
37629         if(this.resizeEl != this.el){
37630             width -= this.el.getFrameWidth('lr');
37631             height -= this.el.getFrameWidth('tb');
37632         }
37633         if(this.toolbar){
37634             var te = this.toolbar.getEl();
37635             te.setWidth(width);
37636             height -= te.getHeight();
37637         }
37638         if(this.footer){
37639             var te = this.footer.getEl();
37640             te.setWidth(width);
37641             height -= te.getHeight();
37642         }
37643         
37644         
37645         if(this.adjustments){
37646             width += this.adjustments[0];
37647             height += this.adjustments[1];
37648         }
37649         return {"width": width, "height": height};
37650     },
37651     
37652     setSize : function(width, height){
37653         if(this.fitToFrame && !this.ignoreResize(width, height)){
37654             if(this.fitContainer && this.resizeEl != this.el){
37655                 this.el.setSize(width, height);
37656             }
37657             var size = this.adjustForComponents(width, height);
37658             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37659             this.fireEvent('resize', this, size.width, size.height);
37660         }
37661     },
37662     
37663     /**
37664      * Returns this panel's title
37665      * @return {String} 
37666      */
37667     getTitle : function(){
37668         
37669         if (typeof(this.title) != 'object') {
37670             return this.title;
37671         }
37672         
37673         var t = '';
37674         for (var k in this.title) {
37675             if (!this.title.hasOwnProperty(k)) {
37676                 continue;
37677             }
37678             
37679             if (k.indexOf('-') >= 0) {
37680                 var s = k.split('-');
37681                 for (var i = 0; i<s.length; i++) {
37682                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37683                 }
37684             } else {
37685                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37686             }
37687         }
37688         return t;
37689     },
37690     
37691     /**
37692      * Set this panel's title
37693      * @param {String} title
37694      */
37695     setTitle : function(title){
37696         this.title = title;
37697         if(this.region){
37698             this.region.updatePanelTitle(this, title);
37699         }
37700     },
37701     
37702     /**
37703      * Returns true is this panel was configured to be closable
37704      * @return {Boolean} 
37705      */
37706     isClosable : function(){
37707         return this.closable;
37708     },
37709     
37710     beforeSlide : function(){
37711         this.el.clip();
37712         this.resizeEl.clip();
37713     },
37714     
37715     afterSlide : function(){
37716         this.el.unclip();
37717         this.resizeEl.unclip();
37718     },
37719     
37720     /**
37721      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37722      *   Will fail silently if the {@link #setUrl} method has not been called.
37723      *   This does not activate the panel, just updates its content.
37724      */
37725     refresh : function(){
37726         if(this.refreshDelegate){
37727            this.loaded = false;
37728            this.refreshDelegate();
37729         }
37730     },
37731     
37732     /**
37733      * Destroys this panel
37734      */
37735     destroy : function(){
37736         this.el.removeAllListeners();
37737         var tempEl = document.createElement("span");
37738         tempEl.appendChild(this.el.dom);
37739         tempEl.innerHTML = "";
37740         this.el.remove();
37741         this.el = null;
37742     },
37743     
37744     /**
37745      * form - if the content panel contains a form - this is a reference to it.
37746      * @type {Roo.form.Form}
37747      */
37748     form : false,
37749     /**
37750      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37751      *    This contains a reference to it.
37752      * @type {Roo.View}
37753      */
37754     view : false,
37755     
37756       /**
37757      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37758      * <pre><code>
37759
37760 layout.addxtype({
37761        xtype : 'Form',
37762        items: [ .... ]
37763    }
37764 );
37765
37766 </code></pre>
37767      * @param {Object} cfg Xtype definition of item to add.
37768      */
37769     
37770     
37771     getChildContainer: function () {
37772         return this.getEl();
37773     }
37774     
37775     
37776     /*
37777         var  ret = new Roo.factory(cfg);
37778         return ret;
37779         
37780         
37781         // add form..
37782         if (cfg.xtype.match(/^Form$/)) {
37783             
37784             var el;
37785             //if (this.footer) {
37786             //    el = this.footer.container.insertSibling(false, 'before');
37787             //} else {
37788                 el = this.el.createChild();
37789             //}
37790
37791             this.form = new  Roo.form.Form(cfg);
37792             
37793             
37794             if ( this.form.allItems.length) {
37795                 this.form.render(el.dom);
37796             }
37797             return this.form;
37798         }
37799         // should only have one of theses..
37800         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37801             // views.. should not be just added - used named prop 'view''
37802             
37803             cfg.el = this.el.appendChild(document.createElement("div"));
37804             // factory?
37805             
37806             var ret = new Roo.factory(cfg);
37807              
37808              ret.render && ret.render(false, ''); // render blank..
37809             this.view = ret;
37810             return ret;
37811         }
37812         return false;
37813     }
37814     \*/
37815 });
37816  
37817 /**
37818  * @class Roo.bootstrap.panel.Grid
37819  * @extends Roo.bootstrap.panel.Content
37820  * @constructor
37821  * Create a new GridPanel.
37822  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37823  * @param {Object} config A the config object
37824   
37825  */
37826
37827
37828
37829 Roo.bootstrap.panel.Grid = function(config)
37830 {
37831     
37832       
37833     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37834         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37835
37836     config.el = this.wrapper;
37837     //this.el = this.wrapper;
37838     
37839       if (config.container) {
37840         // ctor'ed from a Border/panel.grid
37841         
37842         
37843         this.wrapper.setStyle("overflow", "hidden");
37844         this.wrapper.addClass('roo-grid-container');
37845
37846     }
37847     
37848     
37849     if(config.toolbar){
37850         var tool_el = this.wrapper.createChild();    
37851         this.toolbar = Roo.factory(config.toolbar);
37852         var ti = [];
37853         if (config.toolbar.items) {
37854             ti = config.toolbar.items ;
37855             delete config.toolbar.items ;
37856         }
37857         
37858         var nitems = [];
37859         this.toolbar.render(tool_el);
37860         for(var i =0;i < ti.length;i++) {
37861           //  Roo.log(['add child', items[i]]);
37862             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37863         }
37864         this.toolbar.items = nitems;
37865         
37866         delete config.toolbar;
37867     }
37868     
37869     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37870     config.grid.scrollBody = true;;
37871     config.grid.monitorWindowResize = false; // turn off autosizing
37872     config.grid.autoHeight = false;
37873     config.grid.autoWidth = false;
37874     
37875     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37876     
37877     if (config.background) {
37878         // render grid on panel activation (if panel background)
37879         this.on('activate', function(gp) {
37880             if (!gp.grid.rendered) {
37881                 gp.grid.render(this.wrapper);
37882                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37883             }
37884         });
37885             
37886     } else {
37887         this.grid.render(this.wrapper);
37888         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37889
37890     }
37891     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37892     // ??? needed ??? config.el = this.wrapper;
37893     
37894     
37895     
37896   
37897     // xtype created footer. - not sure if will work as we normally have to render first..
37898     if (this.footer && !this.footer.el && this.footer.xtype) {
37899         
37900         var ctr = this.grid.getView().getFooterPanel(true);
37901         this.footer.dataSource = this.grid.dataSource;
37902         this.footer = Roo.factory(this.footer, Roo);
37903         this.footer.render(ctr);
37904         
37905     }
37906     
37907     
37908     
37909     
37910      
37911 };
37912
37913 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37914     getId : function(){
37915         return this.grid.id;
37916     },
37917     
37918     /**
37919      * Returns the grid for this panel
37920      * @return {Roo.bootstrap.Table} 
37921      */
37922     getGrid : function(){
37923         return this.grid;    
37924     },
37925     
37926     setSize : function(width, height){
37927         if(!this.ignoreResize(width, height)){
37928             var grid = this.grid;
37929             var size = this.adjustForComponents(width, height);
37930             var gridel = grid.getGridEl();
37931             gridel.setSize(size.width, size.height);
37932             /*
37933             var thd = grid.getGridEl().select('thead',true).first();
37934             var tbd = grid.getGridEl().select('tbody', true).first();
37935             if (tbd) {
37936                 tbd.setSize(width, height - thd.getHeight());
37937             }
37938             */
37939             grid.autoSize();
37940         }
37941     },
37942      
37943     
37944     
37945     beforeSlide : function(){
37946         this.grid.getView().scroller.clip();
37947     },
37948     
37949     afterSlide : function(){
37950         this.grid.getView().scroller.unclip();
37951     },
37952     
37953     destroy : function(){
37954         this.grid.destroy();
37955         delete this.grid;
37956         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37957     }
37958 });
37959
37960 /**
37961  * @class Roo.bootstrap.panel.Nest
37962  * @extends Roo.bootstrap.panel.Content
37963  * @constructor
37964  * Create a new Panel, that can contain a layout.Border.
37965  * 
37966  * 
37967  * @param {Roo.BorderLayout} layout The layout for this panel
37968  * @param {String/Object} config A string to set only the title or a config object
37969  */
37970 Roo.bootstrap.panel.Nest = function(config)
37971 {
37972     // construct with only one argument..
37973     /* FIXME - implement nicer consturctors
37974     if (layout.layout) {
37975         config = layout;
37976         layout = config.layout;
37977         delete config.layout;
37978     }
37979     if (layout.xtype && !layout.getEl) {
37980         // then layout needs constructing..
37981         layout = Roo.factory(layout, Roo);
37982     }
37983     */
37984     
37985     config.el =  config.layout.getEl();
37986     
37987     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37988     
37989     config.layout.monitorWindowResize = false; // turn off autosizing
37990     this.layout = config.layout;
37991     this.layout.getEl().addClass("roo-layout-nested-layout");
37992     this.layout.parent = this;
37993     
37994     
37995     
37996     
37997 };
37998
37999 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38000
38001     setSize : function(width, height){
38002         if(!this.ignoreResize(width, height)){
38003             var size = this.adjustForComponents(width, height);
38004             var el = this.layout.getEl();
38005             if (size.height < 1) {
38006                 el.setWidth(size.width);   
38007             } else {
38008                 el.setSize(size.width, size.height);
38009             }
38010             var touch = el.dom.offsetWidth;
38011             this.layout.layout();
38012             // ie requires a double layout on the first pass
38013             if(Roo.isIE && !this.initialized){
38014                 this.initialized = true;
38015                 this.layout.layout();
38016             }
38017         }
38018     },
38019     
38020     // activate all subpanels if not currently active..
38021     
38022     setActiveState : function(active){
38023         this.active = active;
38024         this.setActiveClass(active);
38025         
38026         if(!active){
38027             this.fireEvent("deactivate", this);
38028             return;
38029         }
38030         
38031         this.fireEvent("activate", this);
38032         // not sure if this should happen before or after..
38033         if (!this.layout) {
38034             return; // should not happen..
38035         }
38036         var reg = false;
38037         for (var r in this.layout.regions) {
38038             reg = this.layout.getRegion(r);
38039             if (reg.getActivePanel()) {
38040                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38041                 reg.setActivePanel(reg.getActivePanel());
38042                 continue;
38043             }
38044             if (!reg.panels.length) {
38045                 continue;
38046             }
38047             reg.showPanel(reg.getPanel(0));
38048         }
38049         
38050         
38051         
38052         
38053     },
38054     
38055     /**
38056      * Returns the nested BorderLayout for this panel
38057      * @return {Roo.BorderLayout} 
38058      */
38059     getLayout : function(){
38060         return this.layout;
38061     },
38062     
38063      /**
38064      * Adds a xtype elements to the layout of the nested panel
38065      * <pre><code>
38066
38067 panel.addxtype({
38068        xtype : 'ContentPanel',
38069        region: 'west',
38070        items: [ .... ]
38071    }
38072 );
38073
38074 panel.addxtype({
38075         xtype : 'NestedLayoutPanel',
38076         region: 'west',
38077         layout: {
38078            center: { },
38079            west: { }   
38080         },
38081         items : [ ... list of content panels or nested layout panels.. ]
38082    }
38083 );
38084 </code></pre>
38085      * @param {Object} cfg Xtype definition of item to add.
38086      */
38087     addxtype : function(cfg) {
38088         return this.layout.addxtype(cfg);
38089     
38090     }
38091 });/*
38092  * Based on:
38093  * Ext JS Library 1.1.1
38094  * Copyright(c) 2006-2007, Ext JS, LLC.
38095  *
38096  * Originally Released Under LGPL - original licence link has changed is not relivant.
38097  *
38098  * Fork - LGPL
38099  * <script type="text/javascript">
38100  */
38101 /**
38102  * @class Roo.TabPanel
38103  * @extends Roo.util.Observable
38104  * A lightweight tab container.
38105  * <br><br>
38106  * Usage:
38107  * <pre><code>
38108 // basic tabs 1, built from existing content
38109 var tabs = new Roo.TabPanel("tabs1");
38110 tabs.addTab("script", "View Script");
38111 tabs.addTab("markup", "View Markup");
38112 tabs.activate("script");
38113
38114 // more advanced tabs, built from javascript
38115 var jtabs = new Roo.TabPanel("jtabs");
38116 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38117
38118 // set up the UpdateManager
38119 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38120 var updater = tab2.getUpdateManager();
38121 updater.setDefaultUrl("ajax1.htm");
38122 tab2.on('activate', updater.refresh, updater, true);
38123
38124 // Use setUrl for Ajax loading
38125 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38126 tab3.setUrl("ajax2.htm", null, true);
38127
38128 // Disabled tab
38129 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38130 tab4.disable();
38131
38132 jtabs.activate("jtabs-1");
38133  * </code></pre>
38134  * @constructor
38135  * Create a new TabPanel.
38136  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38137  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38138  */
38139 Roo.bootstrap.panel.Tabs = function(config){
38140     /**
38141     * The container element for this TabPanel.
38142     * @type Roo.Element
38143     */
38144     this.el = Roo.get(config.el);
38145     delete config.el;
38146     if(config){
38147         if(typeof config == "boolean"){
38148             this.tabPosition = config ? "bottom" : "top";
38149         }else{
38150             Roo.apply(this, config);
38151         }
38152     }
38153     
38154     if(this.tabPosition == "bottom"){
38155         // if tabs are at the bottom = create the body first.
38156         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38157         this.el.addClass("roo-tabs-bottom");
38158     }
38159     // next create the tabs holders
38160     
38161     if (this.tabPosition == "west"){
38162         
38163         var reg = this.region; // fake it..
38164         while (reg) {
38165             if (!reg.mgr.parent) {
38166                 break;
38167             }
38168             reg = reg.mgr.parent.region;
38169         }
38170         Roo.log("got nest?");
38171         Roo.log(reg);
38172         if (reg.mgr.getRegion('west')) {
38173             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38174             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38175             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38176             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38177             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38178         
38179             
38180         }
38181         
38182         
38183     } else {
38184      
38185         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38186         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38187         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38188         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38189     }
38190     
38191     
38192     if(Roo.isIE){
38193         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38194     }
38195     
38196     // finally - if tabs are at the top, then create the body last..
38197     if(this.tabPosition != "bottom"){
38198         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38199          * @type Roo.Element
38200          */
38201         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38202         this.el.addClass("roo-tabs-top");
38203     }
38204     this.items = [];
38205
38206     this.bodyEl.setStyle("position", "relative");
38207
38208     this.active = null;
38209     this.activateDelegate = this.activate.createDelegate(this);
38210
38211     this.addEvents({
38212         /**
38213          * @event tabchange
38214          * Fires when the active tab changes
38215          * @param {Roo.TabPanel} this
38216          * @param {Roo.TabPanelItem} activePanel The new active tab
38217          */
38218         "tabchange": true,
38219         /**
38220          * @event beforetabchange
38221          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38222          * @param {Roo.TabPanel} this
38223          * @param {Object} e Set cancel to true on this object to cancel the tab change
38224          * @param {Roo.TabPanelItem} tab The tab being changed to
38225          */
38226         "beforetabchange" : true
38227     });
38228
38229     Roo.EventManager.onWindowResize(this.onResize, this);
38230     this.cpad = this.el.getPadding("lr");
38231     this.hiddenCount = 0;
38232
38233
38234     // toolbar on the tabbar support...
38235     if (this.toolbar) {
38236         alert("no toolbar support yet");
38237         this.toolbar  = false;
38238         /*
38239         var tcfg = this.toolbar;
38240         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38241         this.toolbar = new Roo.Toolbar(tcfg);
38242         if (Roo.isSafari) {
38243             var tbl = tcfg.container.child('table', true);
38244             tbl.setAttribute('width', '100%');
38245         }
38246         */
38247         
38248     }
38249    
38250
38251
38252     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38253 };
38254
38255 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38256     /*
38257      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38258      */
38259     tabPosition : "top",
38260     /*
38261      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38262      */
38263     currentTabWidth : 0,
38264     /*
38265      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38266      */
38267     minTabWidth : 40,
38268     /*
38269      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38270      */
38271     maxTabWidth : 250,
38272     /*
38273      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38274      */
38275     preferredTabWidth : 175,
38276     /*
38277      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38278      */
38279     resizeTabs : false,
38280     /*
38281      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38282      */
38283     monitorResize : true,
38284     /*
38285      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38286      */
38287     toolbar : false,  // set by caller..
38288     
38289     region : false, /// set by caller
38290     
38291     disableTooltips : true, // not used yet...
38292
38293     /**
38294      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38295      * @param {String} id The id of the div to use <b>or create</b>
38296      * @param {String} text The text for the tab
38297      * @param {String} content (optional) Content to put in the TabPanelItem body
38298      * @param {Boolean} closable (optional) True to create a close icon on the tab
38299      * @return {Roo.TabPanelItem} The created TabPanelItem
38300      */
38301     addTab : function(id, text, content, closable, tpl)
38302     {
38303         var item = new Roo.bootstrap.panel.TabItem({
38304             panel: this,
38305             id : id,
38306             text : text,
38307             closable : closable,
38308             tpl : tpl
38309         });
38310         this.addTabItem(item);
38311         if(content){
38312             item.setContent(content);
38313         }
38314         return item;
38315     },
38316
38317     /**
38318      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38319      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38320      * @return {Roo.TabPanelItem}
38321      */
38322     getTab : function(id){
38323         return this.items[id];
38324     },
38325
38326     /**
38327      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38328      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38329      */
38330     hideTab : function(id){
38331         var t = this.items[id];
38332         if(!t.isHidden()){
38333            t.setHidden(true);
38334            this.hiddenCount++;
38335            this.autoSizeTabs();
38336         }
38337     },
38338
38339     /**
38340      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38341      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38342      */
38343     unhideTab : function(id){
38344         var t = this.items[id];
38345         if(t.isHidden()){
38346            t.setHidden(false);
38347            this.hiddenCount--;
38348            this.autoSizeTabs();
38349         }
38350     },
38351
38352     /**
38353      * Adds an existing {@link Roo.TabPanelItem}.
38354      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38355      */
38356     addTabItem : function(item)
38357     {
38358         this.items[item.id] = item;
38359         this.items.push(item);
38360         this.autoSizeTabs();
38361       //  if(this.resizeTabs){
38362     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38363   //         this.autoSizeTabs();
38364 //        }else{
38365 //            item.autoSize();
38366        // }
38367     },
38368
38369     /**
38370      * Removes a {@link Roo.TabPanelItem}.
38371      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38372      */
38373     removeTab : function(id){
38374         var items = this.items;
38375         var tab = items[id];
38376         if(!tab) { return; }
38377         var index = items.indexOf(tab);
38378         if(this.active == tab && items.length > 1){
38379             var newTab = this.getNextAvailable(index);
38380             if(newTab) {
38381                 newTab.activate();
38382             }
38383         }
38384         this.stripEl.dom.removeChild(tab.pnode.dom);
38385         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38386             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38387         }
38388         items.splice(index, 1);
38389         delete this.items[tab.id];
38390         tab.fireEvent("close", tab);
38391         tab.purgeListeners();
38392         this.autoSizeTabs();
38393     },
38394
38395     getNextAvailable : function(start){
38396         var items = this.items;
38397         var index = start;
38398         // look for a next tab that will slide over to
38399         // replace the one being removed
38400         while(index < items.length){
38401             var item = items[++index];
38402             if(item && !item.isHidden()){
38403                 return item;
38404             }
38405         }
38406         // if one isn't found select the previous tab (on the left)
38407         index = start;
38408         while(index >= 0){
38409             var item = items[--index];
38410             if(item && !item.isHidden()){
38411                 return item;
38412             }
38413         }
38414         return null;
38415     },
38416
38417     /**
38418      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38419      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38420      */
38421     disableTab : function(id){
38422         var tab = this.items[id];
38423         if(tab && this.active != tab){
38424             tab.disable();
38425         }
38426     },
38427
38428     /**
38429      * Enables a {@link Roo.TabPanelItem} that is disabled.
38430      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38431      */
38432     enableTab : function(id){
38433         var tab = this.items[id];
38434         tab.enable();
38435     },
38436
38437     /**
38438      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38439      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38440      * @return {Roo.TabPanelItem} The TabPanelItem.
38441      */
38442     activate : function(id)
38443     {
38444         //Roo.log('activite:'  + id);
38445         
38446         var tab = this.items[id];
38447         if(!tab){
38448             return null;
38449         }
38450         if(tab == this.active || tab.disabled){
38451             return tab;
38452         }
38453         var e = {};
38454         this.fireEvent("beforetabchange", this, e, tab);
38455         if(e.cancel !== true && !tab.disabled){
38456             if(this.active){
38457                 this.active.hide();
38458             }
38459             this.active = this.items[id];
38460             this.active.show();
38461             this.fireEvent("tabchange", this, this.active);
38462         }
38463         return tab;
38464     },
38465
38466     /**
38467      * Gets the active {@link Roo.TabPanelItem}.
38468      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38469      */
38470     getActiveTab : function(){
38471         return this.active;
38472     },
38473
38474     /**
38475      * Updates the tab body element to fit the height of the container element
38476      * for overflow scrolling
38477      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38478      */
38479     syncHeight : function(targetHeight){
38480         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38481         var bm = this.bodyEl.getMargins();
38482         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38483         this.bodyEl.setHeight(newHeight);
38484         return newHeight;
38485     },
38486
38487     onResize : function(){
38488         if(this.monitorResize){
38489             this.autoSizeTabs();
38490         }
38491     },
38492
38493     /**
38494      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38495      */
38496     beginUpdate : function(){
38497         this.updating = true;
38498     },
38499
38500     /**
38501      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38502      */
38503     endUpdate : function(){
38504         this.updating = false;
38505         this.autoSizeTabs();
38506     },
38507
38508     /**
38509      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38510      */
38511     autoSizeTabs : function()
38512     {
38513         var count = this.items.length;
38514         var vcount = count - this.hiddenCount;
38515         
38516         if (vcount < 2) {
38517             this.stripEl.hide();
38518         } else {
38519             this.stripEl.show();
38520         }
38521         
38522         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38523             return;
38524         }
38525         
38526         
38527         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38528         var availWidth = Math.floor(w / vcount);
38529         var b = this.stripBody;
38530         if(b.getWidth() > w){
38531             var tabs = this.items;
38532             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38533             if(availWidth < this.minTabWidth){
38534                 /*if(!this.sleft){    // incomplete scrolling code
38535                     this.createScrollButtons();
38536                 }
38537                 this.showScroll();
38538                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38539             }
38540         }else{
38541             if(this.currentTabWidth < this.preferredTabWidth){
38542                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38543             }
38544         }
38545     },
38546
38547     /**
38548      * Returns the number of tabs in this TabPanel.
38549      * @return {Number}
38550      */
38551      getCount : function(){
38552          return this.items.length;
38553      },
38554
38555     /**
38556      * Resizes all the tabs to the passed width
38557      * @param {Number} The new width
38558      */
38559     setTabWidth : function(width){
38560         this.currentTabWidth = width;
38561         for(var i = 0, len = this.items.length; i < len; i++) {
38562                 if(!this.items[i].isHidden()) {
38563                 this.items[i].setWidth(width);
38564             }
38565         }
38566     },
38567
38568     /**
38569      * Destroys this TabPanel
38570      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38571      */
38572     destroy : function(removeEl){
38573         Roo.EventManager.removeResizeListener(this.onResize, this);
38574         for(var i = 0, len = this.items.length; i < len; i++){
38575             this.items[i].purgeListeners();
38576         }
38577         if(removeEl === true){
38578             this.el.update("");
38579             this.el.remove();
38580         }
38581     },
38582     
38583     createStrip : function(container)
38584     {
38585         var strip = document.createElement("nav");
38586         strip.className = Roo.bootstrap.version == 4 ?
38587             "navbar-light bg-light" : 
38588             "navbar navbar-default"; //"x-tabs-wrap";
38589         container.appendChild(strip);
38590         return strip;
38591     },
38592     
38593     createStripList : function(strip)
38594     {
38595         // div wrapper for retard IE
38596         // returns the "tr" element.
38597         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38598         //'<div class="x-tabs-strip-wrap">'+
38599           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38600           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38601         return strip.firstChild; //.firstChild.firstChild.firstChild;
38602     },
38603     createBody : function(container)
38604     {
38605         var body = document.createElement("div");
38606         Roo.id(body, "tab-body");
38607         //Roo.fly(body).addClass("x-tabs-body");
38608         Roo.fly(body).addClass("tab-content");
38609         container.appendChild(body);
38610         return body;
38611     },
38612     createItemBody :function(bodyEl, id){
38613         var body = Roo.getDom(id);
38614         if(!body){
38615             body = document.createElement("div");
38616             body.id = id;
38617         }
38618         //Roo.fly(body).addClass("x-tabs-item-body");
38619         Roo.fly(body).addClass("tab-pane");
38620          bodyEl.insertBefore(body, bodyEl.firstChild);
38621         return body;
38622     },
38623     /** @private */
38624     createStripElements :  function(stripEl, text, closable, tpl)
38625     {
38626         var td = document.createElement("li"); // was td..
38627         td.className = 'nav-item';
38628         
38629         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38630         
38631         
38632         stripEl.appendChild(td);
38633         /*if(closable){
38634             td.className = "x-tabs-closable";
38635             if(!this.closeTpl){
38636                 this.closeTpl = new Roo.Template(
38637                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38638                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38639                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38640                 );
38641             }
38642             var el = this.closeTpl.overwrite(td, {"text": text});
38643             var close = el.getElementsByTagName("div")[0];
38644             var inner = el.getElementsByTagName("em")[0];
38645             return {"el": el, "close": close, "inner": inner};
38646         } else {
38647         */
38648         // not sure what this is..
38649 //            if(!this.tabTpl){
38650                 //this.tabTpl = new Roo.Template(
38651                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38652                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38653                 //);
38654 //                this.tabTpl = new Roo.Template(
38655 //                   '<a href="#">' +
38656 //                   '<span unselectable="on"' +
38657 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38658 //                            ' >{text}</span></a>'
38659 //                );
38660 //                
38661 //            }
38662
38663
38664             var template = tpl || this.tabTpl || false;
38665             
38666             if(!template){
38667                 template =  new Roo.Template(
38668                         Roo.bootstrap.version == 4 ? 
38669                             (
38670                                 '<a class="nav-link" href="#" unselectable="on"' +
38671                                      (this.disableTooltips ? '' : ' title="{text}"') +
38672                                      ' >{text}</a>'
38673                             ) : (
38674                                 '<a class="nav-link" href="#">' +
38675                                 '<span unselectable="on"' +
38676                                          (this.disableTooltips ? '' : ' title="{text}"') +
38677                                     ' >{text}</span></a>'
38678                             )
38679                 );
38680             }
38681             
38682             switch (typeof(template)) {
38683                 case 'object' :
38684                     break;
38685                 case 'string' :
38686                     template = new Roo.Template(template);
38687                     break;
38688                 default :
38689                     break;
38690             }
38691             
38692             var el = template.overwrite(td, {"text": text});
38693             
38694             var inner = el.getElementsByTagName("span")[0];
38695             
38696             return {"el": el, "inner": inner};
38697             
38698     }
38699         
38700     
38701 });
38702
38703 /**
38704  * @class Roo.TabPanelItem
38705  * @extends Roo.util.Observable
38706  * Represents an individual item (tab plus body) in a TabPanel.
38707  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38708  * @param {String} id The id of this TabPanelItem
38709  * @param {String} text The text for the tab of this TabPanelItem
38710  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38711  */
38712 Roo.bootstrap.panel.TabItem = function(config){
38713     /**
38714      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38715      * @type Roo.TabPanel
38716      */
38717     this.tabPanel = config.panel;
38718     /**
38719      * The id for this TabPanelItem
38720      * @type String
38721      */
38722     this.id = config.id;
38723     /** @private */
38724     this.disabled = false;
38725     /** @private */
38726     this.text = config.text;
38727     /** @private */
38728     this.loaded = false;
38729     this.closable = config.closable;
38730
38731     /**
38732      * The body element for this TabPanelItem.
38733      * @type Roo.Element
38734      */
38735     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38736     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38737     this.bodyEl.setStyle("display", "block");
38738     this.bodyEl.setStyle("zoom", "1");
38739     //this.hideAction();
38740
38741     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38742     /** @private */
38743     this.el = Roo.get(els.el);
38744     this.inner = Roo.get(els.inner, true);
38745      this.textEl = Roo.bootstrap.version == 4 ?
38746         this.el : Roo.get(this.el.dom.firstChild, true);
38747
38748     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38749     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38750
38751     
38752 //    this.el.on("mousedown", this.onTabMouseDown, this);
38753     this.el.on("click", this.onTabClick, this);
38754     /** @private */
38755     if(config.closable){
38756         var c = Roo.get(els.close, true);
38757         c.dom.title = this.closeText;
38758         c.addClassOnOver("close-over");
38759         c.on("click", this.closeClick, this);
38760      }
38761
38762     this.addEvents({
38763          /**
38764          * @event activate
38765          * Fires when this tab becomes the active tab.
38766          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38767          * @param {Roo.TabPanelItem} this
38768          */
38769         "activate": true,
38770         /**
38771          * @event beforeclose
38772          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38773          * @param {Roo.TabPanelItem} this
38774          * @param {Object} e Set cancel to true on this object to cancel the close.
38775          */
38776         "beforeclose": true,
38777         /**
38778          * @event close
38779          * Fires when this tab is closed.
38780          * @param {Roo.TabPanelItem} this
38781          */
38782          "close": true,
38783         /**
38784          * @event deactivate
38785          * Fires when this tab is no longer the active tab.
38786          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38787          * @param {Roo.TabPanelItem} this
38788          */
38789          "deactivate" : true
38790     });
38791     this.hidden = false;
38792
38793     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38794 };
38795
38796 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38797            {
38798     purgeListeners : function(){
38799        Roo.util.Observable.prototype.purgeListeners.call(this);
38800        this.el.removeAllListeners();
38801     },
38802     /**
38803      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38804      */
38805     show : function(){
38806         this.status_node.addClass("active");
38807         this.showAction();
38808         if(Roo.isOpera){
38809             this.tabPanel.stripWrap.repaint();
38810         }
38811         this.fireEvent("activate", this.tabPanel, this);
38812     },
38813
38814     /**
38815      * Returns true if this tab is the active tab.
38816      * @return {Boolean}
38817      */
38818     isActive : function(){
38819         return this.tabPanel.getActiveTab() == this;
38820     },
38821
38822     /**
38823      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38824      */
38825     hide : function(){
38826         this.status_node.removeClass("active");
38827         this.hideAction();
38828         this.fireEvent("deactivate", this.tabPanel, this);
38829     },
38830
38831     hideAction : function(){
38832         this.bodyEl.hide();
38833         this.bodyEl.setStyle("position", "absolute");
38834         this.bodyEl.setLeft("-20000px");
38835         this.bodyEl.setTop("-20000px");
38836     },
38837
38838     showAction : function(){
38839         this.bodyEl.setStyle("position", "relative");
38840         this.bodyEl.setTop("");
38841         this.bodyEl.setLeft("");
38842         this.bodyEl.show();
38843     },
38844
38845     /**
38846      * Set the tooltip for the tab.
38847      * @param {String} tooltip The tab's tooltip
38848      */
38849     setTooltip : function(text){
38850         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38851             this.textEl.dom.qtip = text;
38852             this.textEl.dom.removeAttribute('title');
38853         }else{
38854             this.textEl.dom.title = text;
38855         }
38856     },
38857
38858     onTabClick : function(e){
38859         e.preventDefault();
38860         this.tabPanel.activate(this.id);
38861     },
38862
38863     onTabMouseDown : function(e){
38864         e.preventDefault();
38865         this.tabPanel.activate(this.id);
38866     },
38867 /*
38868     getWidth : function(){
38869         return this.inner.getWidth();
38870     },
38871
38872     setWidth : function(width){
38873         var iwidth = width - this.linode.getPadding("lr");
38874         this.inner.setWidth(iwidth);
38875         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38876         this.linode.setWidth(width);
38877     },
38878 */
38879     /**
38880      * Show or hide the tab
38881      * @param {Boolean} hidden True to hide or false to show.
38882      */
38883     setHidden : function(hidden){
38884         this.hidden = hidden;
38885         this.linode.setStyle("display", hidden ? "none" : "");
38886     },
38887
38888     /**
38889      * Returns true if this tab is "hidden"
38890      * @return {Boolean}
38891      */
38892     isHidden : function(){
38893         return this.hidden;
38894     },
38895
38896     /**
38897      * Returns the text for this tab
38898      * @return {String}
38899      */
38900     getText : function(){
38901         return this.text;
38902     },
38903     /*
38904     autoSize : function(){
38905         //this.el.beginMeasure();
38906         this.textEl.setWidth(1);
38907         /*
38908          *  #2804 [new] Tabs in Roojs
38909          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38910          */
38911         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38912         //this.el.endMeasure();
38913     //},
38914
38915     /**
38916      * Sets the text for the tab (Note: this also sets the tooltip text)
38917      * @param {String} text The tab's text and tooltip
38918      */
38919     setText : function(text){
38920         this.text = text;
38921         this.textEl.update(text);
38922         this.setTooltip(text);
38923         //if(!this.tabPanel.resizeTabs){
38924         //    this.autoSize();
38925         //}
38926     },
38927     /**
38928      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38929      */
38930     activate : function(){
38931         this.tabPanel.activate(this.id);
38932     },
38933
38934     /**
38935      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38936      */
38937     disable : function(){
38938         if(this.tabPanel.active != this){
38939             this.disabled = true;
38940             this.status_node.addClass("disabled");
38941         }
38942     },
38943
38944     /**
38945      * Enables this TabPanelItem if it was previously disabled.
38946      */
38947     enable : function(){
38948         this.disabled = false;
38949         this.status_node.removeClass("disabled");
38950     },
38951
38952     /**
38953      * Sets the content for this TabPanelItem.
38954      * @param {String} content The content
38955      * @param {Boolean} loadScripts true to look for and load scripts
38956      */
38957     setContent : function(content, loadScripts){
38958         this.bodyEl.update(content, loadScripts);
38959     },
38960
38961     /**
38962      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38963      * @return {Roo.UpdateManager} The UpdateManager
38964      */
38965     getUpdateManager : function(){
38966         return this.bodyEl.getUpdateManager();
38967     },
38968
38969     /**
38970      * Set a URL to be used to load the content for this TabPanelItem.
38971      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38972      * @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)
38973      * @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)
38974      * @return {Roo.UpdateManager} The UpdateManager
38975      */
38976     setUrl : function(url, params, loadOnce){
38977         if(this.refreshDelegate){
38978             this.un('activate', this.refreshDelegate);
38979         }
38980         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38981         this.on("activate", this.refreshDelegate);
38982         return this.bodyEl.getUpdateManager();
38983     },
38984
38985     /** @private */
38986     _handleRefresh : function(url, params, loadOnce){
38987         if(!loadOnce || !this.loaded){
38988             var updater = this.bodyEl.getUpdateManager();
38989             updater.update(url, params, this._setLoaded.createDelegate(this));
38990         }
38991     },
38992
38993     /**
38994      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38995      *   Will fail silently if the setUrl method has not been called.
38996      *   This does not activate the panel, just updates its content.
38997      */
38998     refresh : function(){
38999         if(this.refreshDelegate){
39000            this.loaded = false;
39001            this.refreshDelegate();
39002         }
39003     },
39004
39005     /** @private */
39006     _setLoaded : function(){
39007         this.loaded = true;
39008     },
39009
39010     /** @private */
39011     closeClick : function(e){
39012         var o = {};
39013         e.stopEvent();
39014         this.fireEvent("beforeclose", this, o);
39015         if(o.cancel !== true){
39016             this.tabPanel.removeTab(this.id);
39017         }
39018     },
39019     /**
39020      * The text displayed in the tooltip for the close icon.
39021      * @type String
39022      */
39023     closeText : "Close this tab"
39024 });
39025 /**
39026 *    This script refer to:
39027 *    Title: International Telephone Input
39028 *    Author: Jack O'Connor
39029 *    Code version:  v12.1.12
39030 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39031 **/
39032
39033 Roo.bootstrap.PhoneInputData = function() {
39034     var d = [
39035       [
39036         "Afghanistan (‫افغانستان‬‎)",
39037         "af",
39038         "93"
39039       ],
39040       [
39041         "Albania (Shqipëri)",
39042         "al",
39043         "355"
39044       ],
39045       [
39046         "Algeria (‫الجزائر‬‎)",
39047         "dz",
39048         "213"
39049       ],
39050       [
39051         "American Samoa",
39052         "as",
39053         "1684"
39054       ],
39055       [
39056         "Andorra",
39057         "ad",
39058         "376"
39059       ],
39060       [
39061         "Angola",
39062         "ao",
39063         "244"
39064       ],
39065       [
39066         "Anguilla",
39067         "ai",
39068         "1264"
39069       ],
39070       [
39071         "Antigua and Barbuda",
39072         "ag",
39073         "1268"
39074       ],
39075       [
39076         "Argentina",
39077         "ar",
39078         "54"
39079       ],
39080       [
39081         "Armenia (Հայաստան)",
39082         "am",
39083         "374"
39084       ],
39085       [
39086         "Aruba",
39087         "aw",
39088         "297"
39089       ],
39090       [
39091         "Australia",
39092         "au",
39093         "61",
39094         0
39095       ],
39096       [
39097         "Austria (Österreich)",
39098         "at",
39099         "43"
39100       ],
39101       [
39102         "Azerbaijan (Azərbaycan)",
39103         "az",
39104         "994"
39105       ],
39106       [
39107         "Bahamas",
39108         "bs",
39109         "1242"
39110       ],
39111       [
39112         "Bahrain (‫البحرين‬‎)",
39113         "bh",
39114         "973"
39115       ],
39116       [
39117         "Bangladesh (বাংলাদেশ)",
39118         "bd",
39119         "880"
39120       ],
39121       [
39122         "Barbados",
39123         "bb",
39124         "1246"
39125       ],
39126       [
39127         "Belarus (Беларусь)",
39128         "by",
39129         "375"
39130       ],
39131       [
39132         "Belgium (België)",
39133         "be",
39134         "32"
39135       ],
39136       [
39137         "Belize",
39138         "bz",
39139         "501"
39140       ],
39141       [
39142         "Benin (Bénin)",
39143         "bj",
39144         "229"
39145       ],
39146       [
39147         "Bermuda",
39148         "bm",
39149         "1441"
39150       ],
39151       [
39152         "Bhutan (འབྲུག)",
39153         "bt",
39154         "975"
39155       ],
39156       [
39157         "Bolivia",
39158         "bo",
39159         "591"
39160       ],
39161       [
39162         "Bosnia and Herzegovina (Босна и Херцеговина)",
39163         "ba",
39164         "387"
39165       ],
39166       [
39167         "Botswana",
39168         "bw",
39169         "267"
39170       ],
39171       [
39172         "Brazil (Brasil)",
39173         "br",
39174         "55"
39175       ],
39176       [
39177         "British Indian Ocean Territory",
39178         "io",
39179         "246"
39180       ],
39181       [
39182         "British Virgin Islands",
39183         "vg",
39184         "1284"
39185       ],
39186       [
39187         "Brunei",
39188         "bn",
39189         "673"
39190       ],
39191       [
39192         "Bulgaria (България)",
39193         "bg",
39194         "359"
39195       ],
39196       [
39197         "Burkina Faso",
39198         "bf",
39199         "226"
39200       ],
39201       [
39202         "Burundi (Uburundi)",
39203         "bi",
39204         "257"
39205       ],
39206       [
39207         "Cambodia (កម្ពុជា)",
39208         "kh",
39209         "855"
39210       ],
39211       [
39212         "Cameroon (Cameroun)",
39213         "cm",
39214         "237"
39215       ],
39216       [
39217         "Canada",
39218         "ca",
39219         "1",
39220         1,
39221         ["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"]
39222       ],
39223       [
39224         "Cape Verde (Kabu Verdi)",
39225         "cv",
39226         "238"
39227       ],
39228       [
39229         "Caribbean Netherlands",
39230         "bq",
39231         "599",
39232         1
39233       ],
39234       [
39235         "Cayman Islands",
39236         "ky",
39237         "1345"
39238       ],
39239       [
39240         "Central African Republic (République centrafricaine)",
39241         "cf",
39242         "236"
39243       ],
39244       [
39245         "Chad (Tchad)",
39246         "td",
39247         "235"
39248       ],
39249       [
39250         "Chile",
39251         "cl",
39252         "56"
39253       ],
39254       [
39255         "China (中国)",
39256         "cn",
39257         "86"
39258       ],
39259       [
39260         "Christmas Island",
39261         "cx",
39262         "61",
39263         2
39264       ],
39265       [
39266         "Cocos (Keeling) Islands",
39267         "cc",
39268         "61",
39269         1
39270       ],
39271       [
39272         "Colombia",
39273         "co",
39274         "57"
39275       ],
39276       [
39277         "Comoros (‫جزر القمر‬‎)",
39278         "km",
39279         "269"
39280       ],
39281       [
39282         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39283         "cd",
39284         "243"
39285       ],
39286       [
39287         "Congo (Republic) (Congo-Brazzaville)",
39288         "cg",
39289         "242"
39290       ],
39291       [
39292         "Cook Islands",
39293         "ck",
39294         "682"
39295       ],
39296       [
39297         "Costa Rica",
39298         "cr",
39299         "506"
39300       ],
39301       [
39302         "Côte d’Ivoire",
39303         "ci",
39304         "225"
39305       ],
39306       [
39307         "Croatia (Hrvatska)",
39308         "hr",
39309         "385"
39310       ],
39311       [
39312         "Cuba",
39313         "cu",
39314         "53"
39315       ],
39316       [
39317         "Curaçao",
39318         "cw",
39319         "599",
39320         0
39321       ],
39322       [
39323         "Cyprus (Κύπρος)",
39324         "cy",
39325         "357"
39326       ],
39327       [
39328         "Czech Republic (Česká republika)",
39329         "cz",
39330         "420"
39331       ],
39332       [
39333         "Denmark (Danmark)",
39334         "dk",
39335         "45"
39336       ],
39337       [
39338         "Djibouti",
39339         "dj",
39340         "253"
39341       ],
39342       [
39343         "Dominica",
39344         "dm",
39345         "1767"
39346       ],
39347       [
39348         "Dominican Republic (República Dominicana)",
39349         "do",
39350         "1",
39351         2,
39352         ["809", "829", "849"]
39353       ],
39354       [
39355         "Ecuador",
39356         "ec",
39357         "593"
39358       ],
39359       [
39360         "Egypt (‫مصر‬‎)",
39361         "eg",
39362         "20"
39363       ],
39364       [
39365         "El Salvador",
39366         "sv",
39367         "503"
39368       ],
39369       [
39370         "Equatorial Guinea (Guinea Ecuatorial)",
39371         "gq",
39372         "240"
39373       ],
39374       [
39375         "Eritrea",
39376         "er",
39377         "291"
39378       ],
39379       [
39380         "Estonia (Eesti)",
39381         "ee",
39382         "372"
39383       ],
39384       [
39385         "Ethiopia",
39386         "et",
39387         "251"
39388       ],
39389       [
39390         "Falkland Islands (Islas Malvinas)",
39391         "fk",
39392         "500"
39393       ],
39394       [
39395         "Faroe Islands (Føroyar)",
39396         "fo",
39397         "298"
39398       ],
39399       [
39400         "Fiji",
39401         "fj",
39402         "679"
39403       ],
39404       [
39405         "Finland (Suomi)",
39406         "fi",
39407         "358",
39408         0
39409       ],
39410       [
39411         "France",
39412         "fr",
39413         "33"
39414       ],
39415       [
39416         "French Guiana (Guyane française)",
39417         "gf",
39418         "594"
39419       ],
39420       [
39421         "French Polynesia (Polynésie française)",
39422         "pf",
39423         "689"
39424       ],
39425       [
39426         "Gabon",
39427         "ga",
39428         "241"
39429       ],
39430       [
39431         "Gambia",
39432         "gm",
39433         "220"
39434       ],
39435       [
39436         "Georgia (საქართველო)",
39437         "ge",
39438         "995"
39439       ],
39440       [
39441         "Germany (Deutschland)",
39442         "de",
39443         "49"
39444       ],
39445       [
39446         "Ghana (Gaana)",
39447         "gh",
39448         "233"
39449       ],
39450       [
39451         "Gibraltar",
39452         "gi",
39453         "350"
39454       ],
39455       [
39456         "Greece (Ελλάδα)",
39457         "gr",
39458         "30"
39459       ],
39460       [
39461         "Greenland (Kalaallit Nunaat)",
39462         "gl",
39463         "299"
39464       ],
39465       [
39466         "Grenada",
39467         "gd",
39468         "1473"
39469       ],
39470       [
39471         "Guadeloupe",
39472         "gp",
39473         "590",
39474         0
39475       ],
39476       [
39477         "Guam",
39478         "gu",
39479         "1671"
39480       ],
39481       [
39482         "Guatemala",
39483         "gt",
39484         "502"
39485       ],
39486       [
39487         "Guernsey",
39488         "gg",
39489         "44",
39490         1
39491       ],
39492       [
39493         "Guinea (Guinée)",
39494         "gn",
39495         "224"
39496       ],
39497       [
39498         "Guinea-Bissau (Guiné Bissau)",
39499         "gw",
39500         "245"
39501       ],
39502       [
39503         "Guyana",
39504         "gy",
39505         "592"
39506       ],
39507       [
39508         "Haiti",
39509         "ht",
39510         "509"
39511       ],
39512       [
39513         "Honduras",
39514         "hn",
39515         "504"
39516       ],
39517       [
39518         "Hong Kong (香港)",
39519         "hk",
39520         "852"
39521       ],
39522       [
39523         "Hungary (Magyarország)",
39524         "hu",
39525         "36"
39526       ],
39527       [
39528         "Iceland (Ísland)",
39529         "is",
39530         "354"
39531       ],
39532       [
39533         "India (भारत)",
39534         "in",
39535         "91"
39536       ],
39537       [
39538         "Indonesia",
39539         "id",
39540         "62"
39541       ],
39542       [
39543         "Iran (‫ایران‬‎)",
39544         "ir",
39545         "98"
39546       ],
39547       [
39548         "Iraq (‫العراق‬‎)",
39549         "iq",
39550         "964"
39551       ],
39552       [
39553         "Ireland",
39554         "ie",
39555         "353"
39556       ],
39557       [
39558         "Isle of Man",
39559         "im",
39560         "44",
39561         2
39562       ],
39563       [
39564         "Israel (‫ישראל‬‎)",
39565         "il",
39566         "972"
39567       ],
39568       [
39569         "Italy (Italia)",
39570         "it",
39571         "39",
39572         0
39573       ],
39574       [
39575         "Jamaica",
39576         "jm",
39577         "1876"
39578       ],
39579       [
39580         "Japan (日本)",
39581         "jp",
39582         "81"
39583       ],
39584       [
39585         "Jersey",
39586         "je",
39587         "44",
39588         3
39589       ],
39590       [
39591         "Jordan (‫الأردن‬‎)",
39592         "jo",
39593         "962"
39594       ],
39595       [
39596         "Kazakhstan (Казахстан)",
39597         "kz",
39598         "7",
39599         1
39600       ],
39601       [
39602         "Kenya",
39603         "ke",
39604         "254"
39605       ],
39606       [
39607         "Kiribati",
39608         "ki",
39609         "686"
39610       ],
39611       [
39612         "Kosovo",
39613         "xk",
39614         "383"
39615       ],
39616       [
39617         "Kuwait (‫الكويت‬‎)",
39618         "kw",
39619         "965"
39620       ],
39621       [
39622         "Kyrgyzstan (Кыргызстан)",
39623         "kg",
39624         "996"
39625       ],
39626       [
39627         "Laos (ລາວ)",
39628         "la",
39629         "856"
39630       ],
39631       [
39632         "Latvia (Latvija)",
39633         "lv",
39634         "371"
39635       ],
39636       [
39637         "Lebanon (‫لبنان‬‎)",
39638         "lb",
39639         "961"
39640       ],
39641       [
39642         "Lesotho",
39643         "ls",
39644         "266"
39645       ],
39646       [
39647         "Liberia",
39648         "lr",
39649         "231"
39650       ],
39651       [
39652         "Libya (‫ليبيا‬‎)",
39653         "ly",
39654         "218"
39655       ],
39656       [
39657         "Liechtenstein",
39658         "li",
39659         "423"
39660       ],
39661       [
39662         "Lithuania (Lietuva)",
39663         "lt",
39664         "370"
39665       ],
39666       [
39667         "Luxembourg",
39668         "lu",
39669         "352"
39670       ],
39671       [
39672         "Macau (澳門)",
39673         "mo",
39674         "853"
39675       ],
39676       [
39677         "Macedonia (FYROM) (Македонија)",
39678         "mk",
39679         "389"
39680       ],
39681       [
39682         "Madagascar (Madagasikara)",
39683         "mg",
39684         "261"
39685       ],
39686       [
39687         "Malawi",
39688         "mw",
39689         "265"
39690       ],
39691       [
39692         "Malaysia",
39693         "my",
39694         "60"
39695       ],
39696       [
39697         "Maldives",
39698         "mv",
39699         "960"
39700       ],
39701       [
39702         "Mali",
39703         "ml",
39704         "223"
39705       ],
39706       [
39707         "Malta",
39708         "mt",
39709         "356"
39710       ],
39711       [
39712         "Marshall Islands",
39713         "mh",
39714         "692"
39715       ],
39716       [
39717         "Martinique",
39718         "mq",
39719         "596"
39720       ],
39721       [
39722         "Mauritania (‫موريتانيا‬‎)",
39723         "mr",
39724         "222"
39725       ],
39726       [
39727         "Mauritius (Moris)",
39728         "mu",
39729         "230"
39730       ],
39731       [
39732         "Mayotte",
39733         "yt",
39734         "262",
39735         1
39736       ],
39737       [
39738         "Mexico (México)",
39739         "mx",
39740         "52"
39741       ],
39742       [
39743         "Micronesia",
39744         "fm",
39745         "691"
39746       ],
39747       [
39748         "Moldova (Republica Moldova)",
39749         "md",
39750         "373"
39751       ],
39752       [
39753         "Monaco",
39754         "mc",
39755         "377"
39756       ],
39757       [
39758         "Mongolia (Монгол)",
39759         "mn",
39760         "976"
39761       ],
39762       [
39763         "Montenegro (Crna Gora)",
39764         "me",
39765         "382"
39766       ],
39767       [
39768         "Montserrat",
39769         "ms",
39770         "1664"
39771       ],
39772       [
39773         "Morocco (‫المغرب‬‎)",
39774         "ma",
39775         "212",
39776         0
39777       ],
39778       [
39779         "Mozambique (Moçambique)",
39780         "mz",
39781         "258"
39782       ],
39783       [
39784         "Myanmar (Burma) (မြန်မာ)",
39785         "mm",
39786         "95"
39787       ],
39788       [
39789         "Namibia (Namibië)",
39790         "na",
39791         "264"
39792       ],
39793       [
39794         "Nauru",
39795         "nr",
39796         "674"
39797       ],
39798       [
39799         "Nepal (नेपाल)",
39800         "np",
39801         "977"
39802       ],
39803       [
39804         "Netherlands (Nederland)",
39805         "nl",
39806         "31"
39807       ],
39808       [
39809         "New Caledonia (Nouvelle-Calédonie)",
39810         "nc",
39811         "687"
39812       ],
39813       [
39814         "New Zealand",
39815         "nz",
39816         "64"
39817       ],
39818       [
39819         "Nicaragua",
39820         "ni",
39821         "505"
39822       ],
39823       [
39824         "Niger (Nijar)",
39825         "ne",
39826         "227"
39827       ],
39828       [
39829         "Nigeria",
39830         "ng",
39831         "234"
39832       ],
39833       [
39834         "Niue",
39835         "nu",
39836         "683"
39837       ],
39838       [
39839         "Norfolk Island",
39840         "nf",
39841         "672"
39842       ],
39843       [
39844         "North Korea (조선 민주주의 인민 공화국)",
39845         "kp",
39846         "850"
39847       ],
39848       [
39849         "Northern Mariana Islands",
39850         "mp",
39851         "1670"
39852       ],
39853       [
39854         "Norway (Norge)",
39855         "no",
39856         "47",
39857         0
39858       ],
39859       [
39860         "Oman (‫عُمان‬‎)",
39861         "om",
39862         "968"
39863       ],
39864       [
39865         "Pakistan (‫پاکستان‬‎)",
39866         "pk",
39867         "92"
39868       ],
39869       [
39870         "Palau",
39871         "pw",
39872         "680"
39873       ],
39874       [
39875         "Palestine (‫فلسطين‬‎)",
39876         "ps",
39877         "970"
39878       ],
39879       [
39880         "Panama (Panamá)",
39881         "pa",
39882         "507"
39883       ],
39884       [
39885         "Papua New Guinea",
39886         "pg",
39887         "675"
39888       ],
39889       [
39890         "Paraguay",
39891         "py",
39892         "595"
39893       ],
39894       [
39895         "Peru (Perú)",
39896         "pe",
39897         "51"
39898       ],
39899       [
39900         "Philippines",
39901         "ph",
39902         "63"
39903       ],
39904       [
39905         "Poland (Polska)",
39906         "pl",
39907         "48"
39908       ],
39909       [
39910         "Portugal",
39911         "pt",
39912         "351"
39913       ],
39914       [
39915         "Puerto Rico",
39916         "pr",
39917         "1",
39918         3,
39919         ["787", "939"]
39920       ],
39921       [
39922         "Qatar (‫قطر‬‎)",
39923         "qa",
39924         "974"
39925       ],
39926       [
39927         "Réunion (La Réunion)",
39928         "re",
39929         "262",
39930         0
39931       ],
39932       [
39933         "Romania (România)",
39934         "ro",
39935         "40"
39936       ],
39937       [
39938         "Russia (Россия)",
39939         "ru",
39940         "7",
39941         0
39942       ],
39943       [
39944         "Rwanda",
39945         "rw",
39946         "250"
39947       ],
39948       [
39949         "Saint Barthélemy",
39950         "bl",
39951         "590",
39952         1
39953       ],
39954       [
39955         "Saint Helena",
39956         "sh",
39957         "290"
39958       ],
39959       [
39960         "Saint Kitts and Nevis",
39961         "kn",
39962         "1869"
39963       ],
39964       [
39965         "Saint Lucia",
39966         "lc",
39967         "1758"
39968       ],
39969       [
39970         "Saint Martin (Saint-Martin (partie française))",
39971         "mf",
39972         "590",
39973         2
39974       ],
39975       [
39976         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39977         "pm",
39978         "508"
39979       ],
39980       [
39981         "Saint Vincent and the Grenadines",
39982         "vc",
39983         "1784"
39984       ],
39985       [
39986         "Samoa",
39987         "ws",
39988         "685"
39989       ],
39990       [
39991         "San Marino",
39992         "sm",
39993         "378"
39994       ],
39995       [
39996         "São Tomé and Príncipe (São Tomé e Príncipe)",
39997         "st",
39998         "239"
39999       ],
40000       [
40001         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40002         "sa",
40003         "966"
40004       ],
40005       [
40006         "Senegal (Sénégal)",
40007         "sn",
40008         "221"
40009       ],
40010       [
40011         "Serbia (Србија)",
40012         "rs",
40013         "381"
40014       ],
40015       [
40016         "Seychelles",
40017         "sc",
40018         "248"
40019       ],
40020       [
40021         "Sierra Leone",
40022         "sl",
40023         "232"
40024       ],
40025       [
40026         "Singapore",
40027         "sg",
40028         "65"
40029       ],
40030       [
40031         "Sint Maarten",
40032         "sx",
40033         "1721"
40034       ],
40035       [
40036         "Slovakia (Slovensko)",
40037         "sk",
40038         "421"
40039       ],
40040       [
40041         "Slovenia (Slovenija)",
40042         "si",
40043         "386"
40044       ],
40045       [
40046         "Solomon Islands",
40047         "sb",
40048         "677"
40049       ],
40050       [
40051         "Somalia (Soomaaliya)",
40052         "so",
40053         "252"
40054       ],
40055       [
40056         "South Africa",
40057         "za",
40058         "27"
40059       ],
40060       [
40061         "South Korea (대한민국)",
40062         "kr",
40063         "82"
40064       ],
40065       [
40066         "South Sudan (‫جنوب السودان‬‎)",
40067         "ss",
40068         "211"
40069       ],
40070       [
40071         "Spain (España)",
40072         "es",
40073         "34"
40074       ],
40075       [
40076         "Sri Lanka (ශ්‍රී ලංකාව)",
40077         "lk",
40078         "94"
40079       ],
40080       [
40081         "Sudan (‫السودان‬‎)",
40082         "sd",
40083         "249"
40084       ],
40085       [
40086         "Suriname",
40087         "sr",
40088         "597"
40089       ],
40090       [
40091         "Svalbard and Jan Mayen",
40092         "sj",
40093         "47",
40094         1
40095       ],
40096       [
40097         "Swaziland",
40098         "sz",
40099         "268"
40100       ],
40101       [
40102         "Sweden (Sverige)",
40103         "se",
40104         "46"
40105       ],
40106       [
40107         "Switzerland (Schweiz)",
40108         "ch",
40109         "41"
40110       ],
40111       [
40112         "Syria (‫سوريا‬‎)",
40113         "sy",
40114         "963"
40115       ],
40116       [
40117         "Taiwan (台灣)",
40118         "tw",
40119         "886"
40120       ],
40121       [
40122         "Tajikistan",
40123         "tj",
40124         "992"
40125       ],
40126       [
40127         "Tanzania",
40128         "tz",
40129         "255"
40130       ],
40131       [
40132         "Thailand (ไทย)",
40133         "th",
40134         "66"
40135       ],
40136       [
40137         "Timor-Leste",
40138         "tl",
40139         "670"
40140       ],
40141       [
40142         "Togo",
40143         "tg",
40144         "228"
40145       ],
40146       [
40147         "Tokelau",
40148         "tk",
40149         "690"
40150       ],
40151       [
40152         "Tonga",
40153         "to",
40154         "676"
40155       ],
40156       [
40157         "Trinidad and Tobago",
40158         "tt",
40159         "1868"
40160       ],
40161       [
40162         "Tunisia (‫تونس‬‎)",
40163         "tn",
40164         "216"
40165       ],
40166       [
40167         "Turkey (Türkiye)",
40168         "tr",
40169         "90"
40170       ],
40171       [
40172         "Turkmenistan",
40173         "tm",
40174         "993"
40175       ],
40176       [
40177         "Turks and Caicos Islands",
40178         "tc",
40179         "1649"
40180       ],
40181       [
40182         "Tuvalu",
40183         "tv",
40184         "688"
40185       ],
40186       [
40187         "U.S. Virgin Islands",
40188         "vi",
40189         "1340"
40190       ],
40191       [
40192         "Uganda",
40193         "ug",
40194         "256"
40195       ],
40196       [
40197         "Ukraine (Україна)",
40198         "ua",
40199         "380"
40200       ],
40201       [
40202         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40203         "ae",
40204         "971"
40205       ],
40206       [
40207         "United Kingdom",
40208         "gb",
40209         "44",
40210         0
40211       ],
40212       [
40213         "United States",
40214         "us",
40215         "1",
40216         0
40217       ],
40218       [
40219         "Uruguay",
40220         "uy",
40221         "598"
40222       ],
40223       [
40224         "Uzbekistan (Oʻzbekiston)",
40225         "uz",
40226         "998"
40227       ],
40228       [
40229         "Vanuatu",
40230         "vu",
40231         "678"
40232       ],
40233       [
40234         "Vatican City (Città del Vaticano)",
40235         "va",
40236         "39",
40237         1
40238       ],
40239       [
40240         "Venezuela",
40241         "ve",
40242         "58"
40243       ],
40244       [
40245         "Vietnam (Việt Nam)",
40246         "vn",
40247         "84"
40248       ],
40249       [
40250         "Wallis and Futuna (Wallis-et-Futuna)",
40251         "wf",
40252         "681"
40253       ],
40254       [
40255         "Western Sahara (‫الصحراء الغربية‬‎)",
40256         "eh",
40257         "212",
40258         1
40259       ],
40260       [
40261         "Yemen (‫اليمن‬‎)",
40262         "ye",
40263         "967"
40264       ],
40265       [
40266         "Zambia",
40267         "zm",
40268         "260"
40269       ],
40270       [
40271         "Zimbabwe",
40272         "zw",
40273         "263"
40274       ],
40275       [
40276         "Åland Islands",
40277         "ax",
40278         "358",
40279         1
40280       ]
40281   ];
40282   
40283   return d;
40284 }/**
40285 *    This script refer to:
40286 *    Title: International Telephone Input
40287 *    Author: Jack O'Connor
40288 *    Code version:  v12.1.12
40289 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40290 **/
40291
40292 /**
40293  * @class Roo.bootstrap.PhoneInput
40294  * @extends Roo.bootstrap.TriggerField
40295  * An input with International dial-code selection
40296  
40297  * @cfg {String} defaultDialCode default '+852'
40298  * @cfg {Array} preferedCountries default []
40299   
40300  * @constructor
40301  * Create a new PhoneInput.
40302  * @param {Object} config Configuration options
40303  */
40304
40305 Roo.bootstrap.PhoneInput = function(config) {
40306     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40307 };
40308
40309 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40310         
40311         listWidth: undefined,
40312         
40313         selectedClass: 'active',
40314         
40315         invalidClass : "has-warning",
40316         
40317         validClass: 'has-success',
40318         
40319         allowed: '0123456789',
40320         
40321         max_length: 15,
40322         
40323         /**
40324          * @cfg {String} defaultDialCode The default dial code when initializing the input
40325          */
40326         defaultDialCode: '+852',
40327         
40328         /**
40329          * @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
40330          */
40331         preferedCountries: false,
40332         
40333         getAutoCreate : function()
40334         {
40335             var data = Roo.bootstrap.PhoneInputData();
40336             var align = this.labelAlign || this.parentLabelAlign();
40337             var id = Roo.id();
40338             
40339             this.allCountries = [];
40340             this.dialCodeMapping = [];
40341             
40342             for (var i = 0; i < data.length; i++) {
40343               var c = data[i];
40344               this.allCountries[i] = {
40345                 name: c[0],
40346                 iso2: c[1],
40347                 dialCode: c[2],
40348                 priority: c[3] || 0,
40349                 areaCodes: c[4] || null
40350               };
40351               this.dialCodeMapping[c[2]] = {
40352                   name: c[0],
40353                   iso2: c[1],
40354                   priority: c[3] || 0,
40355                   areaCodes: c[4] || null
40356               };
40357             }
40358             
40359             var cfg = {
40360                 cls: 'form-group',
40361                 cn: []
40362             };
40363             
40364             var input =  {
40365                 tag: 'input',
40366                 id : id,
40367                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40368                 maxlength: this.max_length,
40369                 cls : 'form-control tel-input',
40370                 autocomplete: 'new-password'
40371             };
40372             
40373             var hiddenInput = {
40374                 tag: 'input',
40375                 type: 'hidden',
40376                 cls: 'hidden-tel-input'
40377             };
40378             
40379             if (this.name) {
40380                 hiddenInput.name = this.name;
40381             }
40382             
40383             if (this.disabled) {
40384                 input.disabled = true;
40385             }
40386             
40387             var flag_container = {
40388                 tag: 'div',
40389                 cls: 'flag-box',
40390                 cn: [
40391                     {
40392                         tag: 'div',
40393                         cls: 'flag'
40394                     },
40395                     {
40396                         tag: 'div',
40397                         cls: 'caret'
40398                     }
40399                 ]
40400             };
40401             
40402             var box = {
40403                 tag: 'div',
40404                 cls: this.hasFeedback ? 'has-feedback' : '',
40405                 cn: [
40406                     hiddenInput,
40407                     input,
40408                     {
40409                         tag: 'input',
40410                         cls: 'dial-code-holder',
40411                         disabled: true
40412                     }
40413                 ]
40414             };
40415             
40416             var container = {
40417                 cls: 'roo-select2-container input-group',
40418                 cn: [
40419                     flag_container,
40420                     box
40421                 ]
40422             };
40423             
40424             if (this.fieldLabel.length) {
40425                 var indicator = {
40426                     tag: 'i',
40427                     tooltip: 'This field is required'
40428                 };
40429                 
40430                 var label = {
40431                     tag: 'label',
40432                     'for':  id,
40433                     cls: 'control-label',
40434                     cn: []
40435                 };
40436                 
40437                 var label_text = {
40438                     tag: 'span',
40439                     html: this.fieldLabel
40440                 };
40441                 
40442                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40443                 label.cn = [
40444                     indicator,
40445                     label_text
40446                 ];
40447                 
40448                 if(this.indicatorpos == 'right') {
40449                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40450                     label.cn = [
40451                         label_text,
40452                         indicator
40453                     ];
40454                 }
40455                 
40456                 if(align == 'left') {
40457                     container = {
40458                         tag: 'div',
40459                         cn: [
40460                             container
40461                         ]
40462                     };
40463                     
40464                     if(this.labelWidth > 12){
40465                         label.style = "width: " + this.labelWidth + 'px';
40466                     }
40467                     if(this.labelWidth < 13 && this.labelmd == 0){
40468                         this.labelmd = this.labelWidth;
40469                     }
40470                     if(this.labellg > 0){
40471                         label.cls += ' col-lg-' + this.labellg;
40472                         input.cls += ' col-lg-' + (12 - this.labellg);
40473                     }
40474                     if(this.labelmd > 0){
40475                         label.cls += ' col-md-' + this.labelmd;
40476                         container.cls += ' col-md-' + (12 - this.labelmd);
40477                     }
40478                     if(this.labelsm > 0){
40479                         label.cls += ' col-sm-' + this.labelsm;
40480                         container.cls += ' col-sm-' + (12 - this.labelsm);
40481                     }
40482                     if(this.labelxs > 0){
40483                         label.cls += ' col-xs-' + this.labelxs;
40484                         container.cls += ' col-xs-' + (12 - this.labelxs);
40485                     }
40486                 }
40487             }
40488             
40489             cfg.cn = [
40490                 label,
40491                 container
40492             ];
40493             
40494             var settings = this;
40495             
40496             ['xs','sm','md','lg'].map(function(size){
40497                 if (settings[size]) {
40498                     cfg.cls += ' col-' + size + '-' + settings[size];
40499                 }
40500             });
40501             
40502             this.store = new Roo.data.Store({
40503                 proxy : new Roo.data.MemoryProxy({}),
40504                 reader : new Roo.data.JsonReader({
40505                     fields : [
40506                         {
40507                             'name' : 'name',
40508                             'type' : 'string'
40509                         },
40510                         {
40511                             'name' : 'iso2',
40512                             'type' : 'string'
40513                         },
40514                         {
40515                             'name' : 'dialCode',
40516                             'type' : 'string'
40517                         },
40518                         {
40519                             'name' : 'priority',
40520                             'type' : 'string'
40521                         },
40522                         {
40523                             'name' : 'areaCodes',
40524                             'type' : 'string'
40525                         }
40526                     ]
40527                 })
40528             });
40529             
40530             if(!this.preferedCountries) {
40531                 this.preferedCountries = [
40532                     'hk',
40533                     'gb',
40534                     'us'
40535                 ];
40536             }
40537             
40538             var p = this.preferedCountries.reverse();
40539             
40540             if(p) {
40541                 for (var i = 0; i < p.length; i++) {
40542                     for (var j = 0; j < this.allCountries.length; j++) {
40543                         if(this.allCountries[j].iso2 == p[i]) {
40544                             var t = this.allCountries[j];
40545                             this.allCountries.splice(j,1);
40546                             this.allCountries.unshift(t);
40547                         }
40548                     } 
40549                 }
40550             }
40551             
40552             this.store.proxy.data = {
40553                 success: true,
40554                 data: this.allCountries
40555             };
40556             
40557             return cfg;
40558         },
40559         
40560         initEvents : function()
40561         {
40562             this.createList();
40563             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40564             
40565             this.indicator = this.indicatorEl();
40566             this.flag = this.flagEl();
40567             this.dialCodeHolder = this.dialCodeHolderEl();
40568             
40569             this.trigger = this.el.select('div.flag-box',true).first();
40570             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40571             
40572             var _this = this;
40573             
40574             (function(){
40575                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40576                 _this.list.setWidth(lw);
40577             }).defer(100);
40578             
40579             this.list.on('mouseover', this.onViewOver, this);
40580             this.list.on('mousemove', this.onViewMove, this);
40581             this.inputEl().on("keyup", this.onKeyUp, this);
40582             this.inputEl().on("keypress", this.onKeyPress, this);
40583             
40584             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40585
40586             this.view = new Roo.View(this.list, this.tpl, {
40587                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40588             });
40589             
40590             this.view.on('click', this.onViewClick, this);
40591             this.setValue(this.defaultDialCode);
40592         },
40593         
40594         onTriggerClick : function(e)
40595         {
40596             Roo.log('trigger click');
40597             if(this.disabled){
40598                 return;
40599             }
40600             
40601             if(this.isExpanded()){
40602                 this.collapse();
40603                 this.hasFocus = false;
40604             }else {
40605                 this.store.load({});
40606                 this.hasFocus = true;
40607                 this.expand();
40608             }
40609         },
40610         
40611         isExpanded : function()
40612         {
40613             return this.list.isVisible();
40614         },
40615         
40616         collapse : function()
40617         {
40618             if(!this.isExpanded()){
40619                 return;
40620             }
40621             this.list.hide();
40622             Roo.get(document).un('mousedown', this.collapseIf, this);
40623             Roo.get(document).un('mousewheel', this.collapseIf, this);
40624             this.fireEvent('collapse', this);
40625             this.validate();
40626         },
40627         
40628         expand : function()
40629         {
40630             Roo.log('expand');
40631
40632             if(this.isExpanded() || !this.hasFocus){
40633                 return;
40634             }
40635             
40636             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40637             this.list.setWidth(lw);
40638             
40639             this.list.show();
40640             this.restrictHeight();
40641             
40642             Roo.get(document).on('mousedown', this.collapseIf, this);
40643             Roo.get(document).on('mousewheel', this.collapseIf, this);
40644             
40645             this.fireEvent('expand', this);
40646         },
40647         
40648         restrictHeight : function()
40649         {
40650             this.list.alignTo(this.inputEl(), this.listAlign);
40651             this.list.alignTo(this.inputEl(), this.listAlign);
40652         },
40653         
40654         onViewOver : function(e, t)
40655         {
40656             if(this.inKeyMode){
40657                 return;
40658             }
40659             var item = this.view.findItemFromChild(t);
40660             
40661             if(item){
40662                 var index = this.view.indexOf(item);
40663                 this.select(index, false);
40664             }
40665         },
40666
40667         // private
40668         onViewClick : function(view, doFocus, el, e)
40669         {
40670             var index = this.view.getSelectedIndexes()[0];
40671             
40672             var r = this.store.getAt(index);
40673             
40674             if(r){
40675                 this.onSelect(r, index);
40676             }
40677             if(doFocus !== false && !this.blockFocus){
40678                 this.inputEl().focus();
40679             }
40680         },
40681         
40682         onViewMove : function(e, t)
40683         {
40684             this.inKeyMode = false;
40685         },
40686         
40687         select : function(index, scrollIntoView)
40688         {
40689             this.selectedIndex = index;
40690             this.view.select(index);
40691             if(scrollIntoView !== false){
40692                 var el = this.view.getNode(index);
40693                 if(el){
40694                     this.list.scrollChildIntoView(el, false);
40695                 }
40696             }
40697         },
40698         
40699         createList : function()
40700         {
40701             this.list = Roo.get(document.body).createChild({
40702                 tag: 'ul',
40703                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40704                 style: 'display:none'
40705             });
40706             
40707             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40708         },
40709         
40710         collapseIf : function(e)
40711         {
40712             var in_combo  = e.within(this.el);
40713             var in_list =  e.within(this.list);
40714             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40715             
40716             if (in_combo || in_list || is_list) {
40717                 return;
40718             }
40719             this.collapse();
40720         },
40721         
40722         onSelect : function(record, index)
40723         {
40724             if(this.fireEvent('beforeselect', this, record, index) !== false){
40725                 
40726                 this.setFlagClass(record.data.iso2);
40727                 this.setDialCode(record.data.dialCode);
40728                 this.hasFocus = false;
40729                 this.collapse();
40730                 this.fireEvent('select', this, record, index);
40731             }
40732         },
40733         
40734         flagEl : function()
40735         {
40736             var flag = this.el.select('div.flag',true).first();
40737             if(!flag){
40738                 return false;
40739             }
40740             return flag;
40741         },
40742         
40743         dialCodeHolderEl : function()
40744         {
40745             var d = this.el.select('input.dial-code-holder',true).first();
40746             if(!d){
40747                 return false;
40748             }
40749             return d;
40750         },
40751         
40752         setDialCode : function(v)
40753         {
40754             this.dialCodeHolder.dom.value = '+'+v;
40755         },
40756         
40757         setFlagClass : function(n)
40758         {
40759             this.flag.dom.className = 'flag '+n;
40760         },
40761         
40762         getValue : function()
40763         {
40764             var v = this.inputEl().getValue();
40765             if(this.dialCodeHolder) {
40766                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40767             }
40768             return v;
40769         },
40770         
40771         setValue : function(v)
40772         {
40773             var d = this.getDialCode(v);
40774             
40775             //invalid dial code
40776             if(v.length == 0 || !d || d.length == 0) {
40777                 if(this.rendered){
40778                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40779                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40780                 }
40781                 return;
40782             }
40783             
40784             //valid dial code
40785             this.setFlagClass(this.dialCodeMapping[d].iso2);
40786             this.setDialCode(d);
40787             this.inputEl().dom.value = v.replace('+'+d,'');
40788             this.hiddenEl().dom.value = this.getValue();
40789             
40790             this.validate();
40791         },
40792         
40793         getDialCode : function(v)
40794         {
40795             v = v ||  '';
40796             
40797             if (v.length == 0) {
40798                 return this.dialCodeHolder.dom.value;
40799             }
40800             
40801             var dialCode = "";
40802             if (v.charAt(0) != "+") {
40803                 return false;
40804             }
40805             var numericChars = "";
40806             for (var i = 1; i < v.length; i++) {
40807               var c = v.charAt(i);
40808               if (!isNaN(c)) {
40809                 numericChars += c;
40810                 if (this.dialCodeMapping[numericChars]) {
40811                   dialCode = v.substr(1, i);
40812                 }
40813                 if (numericChars.length == 4) {
40814                   break;
40815                 }
40816               }
40817             }
40818             return dialCode;
40819         },
40820         
40821         reset : function()
40822         {
40823             this.setValue(this.defaultDialCode);
40824             this.validate();
40825         },
40826         
40827         hiddenEl : function()
40828         {
40829             return this.el.select('input.hidden-tel-input',true).first();
40830         },
40831         
40832         // after setting val
40833         onKeyUp : function(e){
40834             this.setValue(this.getValue());
40835         },
40836         
40837         onKeyPress : function(e){
40838             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40839                 e.stopEvent();
40840             }
40841         }
40842         
40843 });
40844 /**
40845  * @class Roo.bootstrap.MoneyField
40846  * @extends Roo.bootstrap.ComboBox
40847  * Bootstrap MoneyField class
40848  * 
40849  * @constructor
40850  * Create a new MoneyField.
40851  * @param {Object} config Configuration options
40852  */
40853
40854 Roo.bootstrap.MoneyField = function(config) {
40855     
40856     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40857     
40858 };
40859
40860 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40861     
40862     /**
40863      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40864      */
40865     allowDecimals : true,
40866     /**
40867      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40868      */
40869     decimalSeparator : ".",
40870     /**
40871      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40872      */
40873     decimalPrecision : 0,
40874     /**
40875      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40876      */
40877     allowNegative : true,
40878     /**
40879      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40880      */
40881     allowZero: true,
40882     /**
40883      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40884      */
40885     minValue : Number.NEGATIVE_INFINITY,
40886     /**
40887      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40888      */
40889     maxValue : Number.MAX_VALUE,
40890     /**
40891      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40892      */
40893     minText : "The minimum value for this field is {0}",
40894     /**
40895      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40896      */
40897     maxText : "The maximum value for this field is {0}",
40898     /**
40899      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40900      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40901      */
40902     nanText : "{0} is not a valid number",
40903     /**
40904      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40905      */
40906     castInt : true,
40907     /**
40908      * @cfg {String} defaults currency of the MoneyField
40909      * value should be in lkey
40910      */
40911     defaultCurrency : false,
40912     /**
40913      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40914      */
40915     thousandsDelimiter : false,
40916     /**
40917      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40918      */
40919     max_length: false,
40920     
40921     inputlg : 9,
40922     inputmd : 9,
40923     inputsm : 9,
40924     inputxs : 6,
40925     
40926     store : false,
40927     
40928     getAutoCreate : function()
40929     {
40930         var align = this.labelAlign || this.parentLabelAlign();
40931         
40932         var id = Roo.id();
40933
40934         var cfg = {
40935             cls: 'form-group',
40936             cn: []
40937         };
40938
40939         var input =  {
40940             tag: 'input',
40941             id : id,
40942             cls : 'form-control roo-money-amount-input',
40943             autocomplete: 'new-password'
40944         };
40945         
40946         var hiddenInput = {
40947             tag: 'input',
40948             type: 'hidden',
40949             id: Roo.id(),
40950             cls: 'hidden-number-input'
40951         };
40952         
40953         if(this.max_length) {
40954             input.maxlength = this.max_length; 
40955         }
40956         
40957         if (this.name) {
40958             hiddenInput.name = this.name;
40959         }
40960
40961         if (this.disabled) {
40962             input.disabled = true;
40963         }
40964
40965         var clg = 12 - this.inputlg;
40966         var cmd = 12 - this.inputmd;
40967         var csm = 12 - this.inputsm;
40968         var cxs = 12 - this.inputxs;
40969         
40970         var container = {
40971             tag : 'div',
40972             cls : 'row roo-money-field',
40973             cn : [
40974                 {
40975                     tag : 'div',
40976                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40977                     cn : [
40978                         {
40979                             tag : 'div',
40980                             cls: 'roo-select2-container input-group',
40981                             cn: [
40982                                 {
40983                                     tag : 'input',
40984                                     cls : 'form-control roo-money-currency-input',
40985                                     autocomplete: 'new-password',
40986                                     readOnly : 1,
40987                                     name : this.currencyName
40988                                 },
40989                                 {
40990                                     tag :'span',
40991                                     cls : 'input-group-addon',
40992                                     cn : [
40993                                         {
40994                                             tag: 'span',
40995                                             cls: 'caret'
40996                                         }
40997                                     ]
40998                                 }
40999                             ]
41000                         }
41001                     ]
41002                 },
41003                 {
41004                     tag : 'div',
41005                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41006                     cn : [
41007                         {
41008                             tag: 'div',
41009                             cls: this.hasFeedback ? 'has-feedback' : '',
41010                             cn: [
41011                                 input
41012                             ]
41013                         }
41014                     ]
41015                 }
41016             ]
41017             
41018         };
41019         
41020         if (this.fieldLabel.length) {
41021             var indicator = {
41022                 tag: 'i',
41023                 tooltip: 'This field is required'
41024             };
41025
41026             var label = {
41027                 tag: 'label',
41028                 'for':  id,
41029                 cls: 'control-label',
41030                 cn: []
41031             };
41032
41033             var label_text = {
41034                 tag: 'span',
41035                 html: this.fieldLabel
41036             };
41037
41038             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41039             label.cn = [
41040                 indicator,
41041                 label_text
41042             ];
41043
41044             if(this.indicatorpos == 'right') {
41045                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41046                 label.cn = [
41047                     label_text,
41048                     indicator
41049                 ];
41050             }
41051
41052             if(align == 'left') {
41053                 container = {
41054                     tag: 'div',
41055                     cn: [
41056                         container
41057                     ]
41058                 };
41059
41060                 if(this.labelWidth > 12){
41061                     label.style = "width: " + this.labelWidth + 'px';
41062                 }
41063                 if(this.labelWidth < 13 && this.labelmd == 0){
41064                     this.labelmd = this.labelWidth;
41065                 }
41066                 if(this.labellg > 0){
41067                     label.cls += ' col-lg-' + this.labellg;
41068                     input.cls += ' col-lg-' + (12 - this.labellg);
41069                 }
41070                 if(this.labelmd > 0){
41071                     label.cls += ' col-md-' + this.labelmd;
41072                     container.cls += ' col-md-' + (12 - this.labelmd);
41073                 }
41074                 if(this.labelsm > 0){
41075                     label.cls += ' col-sm-' + this.labelsm;
41076                     container.cls += ' col-sm-' + (12 - this.labelsm);
41077                 }
41078                 if(this.labelxs > 0){
41079                     label.cls += ' col-xs-' + this.labelxs;
41080                     container.cls += ' col-xs-' + (12 - this.labelxs);
41081                 }
41082             }
41083         }
41084
41085         cfg.cn = [
41086             label,
41087             container,
41088             hiddenInput
41089         ];
41090         
41091         var settings = this;
41092
41093         ['xs','sm','md','lg'].map(function(size){
41094             if (settings[size]) {
41095                 cfg.cls += ' col-' + size + '-' + settings[size];
41096             }
41097         });
41098         
41099         return cfg;
41100     },
41101     
41102     initEvents : function()
41103     {
41104         this.indicator = this.indicatorEl();
41105         
41106         this.initCurrencyEvent();
41107         
41108         this.initNumberEvent();
41109     },
41110     
41111     initCurrencyEvent : function()
41112     {
41113         if (!this.store) {
41114             throw "can not find store for combo";
41115         }
41116         
41117         this.store = Roo.factory(this.store, Roo.data);
41118         this.store.parent = this;
41119         
41120         this.createList();
41121         
41122         this.triggerEl = this.el.select('.input-group-addon', true).first();
41123         
41124         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41125         
41126         var _this = this;
41127         
41128         (function(){
41129             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41130             _this.list.setWidth(lw);
41131         }).defer(100);
41132         
41133         this.list.on('mouseover', this.onViewOver, this);
41134         this.list.on('mousemove', this.onViewMove, this);
41135         this.list.on('scroll', this.onViewScroll, this);
41136         
41137         if(!this.tpl){
41138             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41139         }
41140         
41141         this.view = new Roo.View(this.list, this.tpl, {
41142             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41143         });
41144         
41145         this.view.on('click', this.onViewClick, this);
41146         
41147         this.store.on('beforeload', this.onBeforeLoad, this);
41148         this.store.on('load', this.onLoad, this);
41149         this.store.on('loadexception', this.onLoadException, this);
41150         
41151         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41152             "up" : function(e){
41153                 this.inKeyMode = true;
41154                 this.selectPrev();
41155             },
41156
41157             "down" : function(e){
41158                 if(!this.isExpanded()){
41159                     this.onTriggerClick();
41160                 }else{
41161                     this.inKeyMode = true;
41162                     this.selectNext();
41163                 }
41164             },
41165
41166             "enter" : function(e){
41167                 this.collapse();
41168                 
41169                 if(this.fireEvent("specialkey", this, e)){
41170                     this.onViewClick(false);
41171                 }
41172                 
41173                 return true;
41174             },
41175
41176             "esc" : function(e){
41177                 this.collapse();
41178             },
41179
41180             "tab" : function(e){
41181                 this.collapse();
41182                 
41183                 if(this.fireEvent("specialkey", this, e)){
41184                     this.onViewClick(false);
41185                 }
41186                 
41187                 return true;
41188             },
41189
41190             scope : this,
41191
41192             doRelay : function(foo, bar, hname){
41193                 if(hname == 'down' || this.scope.isExpanded()){
41194                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41195                 }
41196                 return true;
41197             },
41198
41199             forceKeyDown: true
41200         });
41201         
41202         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41203         
41204     },
41205     
41206     initNumberEvent : function(e)
41207     {
41208         this.inputEl().on("keydown" , this.fireKey,  this);
41209         this.inputEl().on("focus", this.onFocus,  this);
41210         this.inputEl().on("blur", this.onBlur,  this);
41211         
41212         this.inputEl().relayEvent('keyup', this);
41213         
41214         if(this.indicator){
41215             this.indicator.addClass('invisible');
41216         }
41217  
41218         this.originalValue = this.getValue();
41219         
41220         if(this.validationEvent == 'keyup'){
41221             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41222             this.inputEl().on('keyup', this.filterValidation, this);
41223         }
41224         else if(this.validationEvent !== false){
41225             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41226         }
41227         
41228         if(this.selectOnFocus){
41229             this.on("focus", this.preFocus, this);
41230             
41231         }
41232         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41233             this.inputEl().on("keypress", this.filterKeys, this);
41234         } else {
41235             this.inputEl().relayEvent('keypress', this);
41236         }
41237         
41238         var allowed = "0123456789";
41239         
41240         if(this.allowDecimals){
41241             allowed += this.decimalSeparator;
41242         }
41243         
41244         if(this.allowNegative){
41245             allowed += "-";
41246         }
41247         
41248         if(this.thousandsDelimiter) {
41249             allowed += ",";
41250         }
41251         
41252         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41253         
41254         var keyPress = function(e){
41255             
41256             var k = e.getKey();
41257             
41258             var c = e.getCharCode();
41259             
41260             if(
41261                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41262                     allowed.indexOf(String.fromCharCode(c)) === -1
41263             ){
41264                 e.stopEvent();
41265                 return;
41266             }
41267             
41268             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41269                 return;
41270             }
41271             
41272             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41273                 e.stopEvent();
41274             }
41275         };
41276         
41277         this.inputEl().on("keypress", keyPress, this);
41278         
41279     },
41280     
41281     onTriggerClick : function(e)
41282     {   
41283         if(this.disabled){
41284             return;
41285         }
41286         
41287         this.page = 0;
41288         this.loadNext = false;
41289         
41290         if(this.isExpanded()){
41291             this.collapse();
41292             return;
41293         }
41294         
41295         this.hasFocus = true;
41296         
41297         if(this.triggerAction == 'all') {
41298             this.doQuery(this.allQuery, true);
41299             return;
41300         }
41301         
41302         this.doQuery(this.getRawValue());
41303     },
41304     
41305     getCurrency : function()
41306     {   
41307         var v = this.currencyEl().getValue();
41308         
41309         return v;
41310     },
41311     
41312     restrictHeight : function()
41313     {
41314         this.list.alignTo(this.currencyEl(), this.listAlign);
41315         this.list.alignTo(this.currencyEl(), this.listAlign);
41316     },
41317     
41318     onViewClick : function(view, doFocus, el, e)
41319     {
41320         var index = this.view.getSelectedIndexes()[0];
41321         
41322         var r = this.store.getAt(index);
41323         
41324         if(r){
41325             this.onSelect(r, index);
41326         }
41327     },
41328     
41329     onSelect : function(record, index){
41330         
41331         if(this.fireEvent('beforeselect', this, record, index) !== false){
41332         
41333             this.setFromCurrencyData(index > -1 ? record.data : false);
41334             
41335             this.collapse();
41336             
41337             this.fireEvent('select', this, record, index);
41338         }
41339     },
41340     
41341     setFromCurrencyData : function(o)
41342     {
41343         var currency = '';
41344         
41345         this.lastCurrency = o;
41346         
41347         if (this.currencyField) {
41348             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41349         } else {
41350             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41351         }
41352         
41353         this.lastSelectionText = currency;
41354         
41355         //setting default currency
41356         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41357             this.setCurrency(this.defaultCurrency);
41358             return;
41359         }
41360         
41361         this.setCurrency(currency);
41362     },
41363     
41364     setFromData : function(o)
41365     {
41366         var c = {};
41367         
41368         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41369         
41370         this.setFromCurrencyData(c);
41371         
41372         var value = '';
41373         
41374         if (this.name) {
41375             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41376         } else {
41377             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41378         }
41379         
41380         this.setValue(value);
41381         
41382     },
41383     
41384     setCurrency : function(v)
41385     {   
41386         this.currencyValue = v;
41387         
41388         if(this.rendered){
41389             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41390             this.validate();
41391         }
41392     },
41393     
41394     setValue : function(v)
41395     {
41396         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41397         
41398         this.value = v;
41399         
41400         if(this.rendered){
41401             
41402             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41403             
41404             this.inputEl().dom.value = (v == '') ? '' :
41405                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41406             
41407             if(!this.allowZero && v === '0') {
41408                 this.hiddenEl().dom.value = '';
41409                 this.inputEl().dom.value = '';
41410             }
41411             
41412             this.validate();
41413         }
41414     },
41415     
41416     getRawValue : function()
41417     {
41418         var v = this.inputEl().getValue();
41419         
41420         return v;
41421     },
41422     
41423     getValue : function()
41424     {
41425         return this.fixPrecision(this.parseValue(this.getRawValue()));
41426     },
41427     
41428     parseValue : function(value)
41429     {
41430         if(this.thousandsDelimiter) {
41431             value += "";
41432             r = new RegExp(",", "g");
41433             value = value.replace(r, "");
41434         }
41435         
41436         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41437         return isNaN(value) ? '' : value;
41438         
41439     },
41440     
41441     fixPrecision : function(value)
41442     {
41443         if(this.thousandsDelimiter) {
41444             value += "";
41445             r = new RegExp(",", "g");
41446             value = value.replace(r, "");
41447         }
41448         
41449         var nan = isNaN(value);
41450         
41451         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41452             return nan ? '' : value;
41453         }
41454         return parseFloat(value).toFixed(this.decimalPrecision);
41455     },
41456     
41457     decimalPrecisionFcn : function(v)
41458     {
41459         return Math.floor(v);
41460     },
41461     
41462     validateValue : function(value)
41463     {
41464         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41465             return false;
41466         }
41467         
41468         var num = this.parseValue(value);
41469         
41470         if(isNaN(num)){
41471             this.markInvalid(String.format(this.nanText, value));
41472             return false;
41473         }
41474         
41475         if(num < this.minValue){
41476             this.markInvalid(String.format(this.minText, this.minValue));
41477             return false;
41478         }
41479         
41480         if(num > this.maxValue){
41481             this.markInvalid(String.format(this.maxText, this.maxValue));
41482             return false;
41483         }
41484         
41485         return true;
41486     },
41487     
41488     validate : function()
41489     {
41490         if(this.disabled || this.allowBlank){
41491             this.markValid();
41492             return true;
41493         }
41494         
41495         var currency = this.getCurrency();
41496         
41497         if(this.validateValue(this.getRawValue()) && currency.length){
41498             this.markValid();
41499             return true;
41500         }
41501         
41502         this.markInvalid();
41503         return false;
41504     },
41505     
41506     getName: function()
41507     {
41508         return this.name;
41509     },
41510     
41511     beforeBlur : function()
41512     {
41513         if(!this.castInt){
41514             return;
41515         }
41516         
41517         var v = this.parseValue(this.getRawValue());
41518         
41519         if(v || v == 0){
41520             this.setValue(v);
41521         }
41522     },
41523     
41524     onBlur : function()
41525     {
41526         this.beforeBlur();
41527         
41528         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41529             //this.el.removeClass(this.focusClass);
41530         }
41531         
41532         this.hasFocus = false;
41533         
41534         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41535             this.validate();
41536         }
41537         
41538         var v = this.getValue();
41539         
41540         if(String(v) !== String(this.startValue)){
41541             this.fireEvent('change', this, v, this.startValue);
41542         }
41543         
41544         this.fireEvent("blur", this);
41545     },
41546     
41547     inputEl : function()
41548     {
41549         return this.el.select('.roo-money-amount-input', true).first();
41550     },
41551     
41552     currencyEl : function()
41553     {
41554         return this.el.select('.roo-money-currency-input', true).first();
41555     },
41556     
41557     hiddenEl : function()
41558     {
41559         return this.el.select('input.hidden-number-input',true).first();
41560     }
41561     
41562 });/**
41563  * @class Roo.bootstrap.BezierSignature
41564  * @extends Roo.bootstrap.Component
41565  * Bootstrap BezierSignature class
41566  * This script refer to:
41567  *    Title: Signature Pad
41568  *    Author: szimek
41569  *    Availability: https://github.com/szimek/signature_pad
41570  *
41571  * @constructor
41572  * Create a new BezierSignature
41573  * @param {Object} config The config object
41574  */
41575
41576 Roo.bootstrap.BezierSignature = function(config){
41577     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41578     this.addEvents({
41579         "resize" : true
41580     });
41581 };
41582
41583 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41584 {
41585      
41586     curve_data: [],
41587     
41588     is_empty: true,
41589     
41590     mouse_btn_down: true,
41591     
41592     /**
41593      * @cfg {int} canvas height
41594      */
41595     canvas_height: '200px',
41596     
41597     /**
41598      * @cfg {float|function} Radius of a single dot.
41599      */ 
41600     dot_size: false,
41601     
41602     /**
41603      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41604      */
41605     min_width: 0.5,
41606     
41607     /**
41608      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41609      */
41610     max_width: 2.5,
41611     
41612     /**
41613      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41614      */
41615     throttle: 16,
41616     
41617     /**
41618      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41619      */
41620     min_distance: 5,
41621     
41622     /**
41623      * @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.
41624      */
41625     bg_color: 'rgba(0, 0, 0, 0)',
41626     
41627     /**
41628      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41629      */
41630     dot_color: 'black',
41631     
41632     /**
41633      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41634      */ 
41635     velocity_filter_weight: 0.7,
41636     
41637     /**
41638      * @cfg {function} Callback when stroke begin. 
41639      */
41640     onBegin: false,
41641     
41642     /**
41643      * @cfg {function} Callback when stroke end.
41644      */
41645     onEnd: false,
41646     
41647     getAutoCreate : function()
41648     {
41649         var cls = 'roo-signature column';
41650         
41651         if(this.cls){
41652             cls += ' ' + this.cls;
41653         }
41654         
41655         var col_sizes = [
41656             'lg',
41657             'md',
41658             'sm',
41659             'xs'
41660         ];
41661         
41662         for(var i = 0; i < col_sizes.length; i++) {
41663             if(this[col_sizes[i]]) {
41664                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41665             }
41666         }
41667         
41668         var cfg = {
41669             tag: 'div',
41670             cls: cls,
41671             cn: [
41672                 {
41673                     tag: 'div',
41674                     cls: 'roo-signature-body',
41675                     cn: [
41676                         {
41677                             tag: 'canvas',
41678                             cls: 'roo-signature-body-canvas',
41679                             height: this.canvas_height,
41680                             width: this.canvas_width
41681                         }
41682                     ]
41683                 },
41684                 {
41685                     tag: 'input',
41686                     type: 'file',
41687                     style: 'display: none'
41688                 }
41689             ]
41690         };
41691         
41692         return cfg;
41693     },
41694     
41695     initEvents: function() 
41696     {
41697         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41698         
41699         var canvas = this.canvasEl();
41700         
41701         // mouse && touch event swapping...
41702         canvas.dom.style.touchAction = 'none';
41703         canvas.dom.style.msTouchAction = 'none';
41704         
41705         this.mouse_btn_down = false;
41706         canvas.on('mousedown', this._handleMouseDown, this);
41707         canvas.on('mousemove', this._handleMouseMove, this);
41708         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41709         
41710         if (window.PointerEvent) {
41711             canvas.on('pointerdown', this._handleMouseDown, this);
41712             canvas.on('pointermove', this._handleMouseMove, this);
41713             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41714         }
41715         
41716         if ('ontouchstart' in window) {
41717             canvas.on('touchstart', this._handleTouchStart, this);
41718             canvas.on('touchmove', this._handleTouchMove, this);
41719             canvas.on('touchend', this._handleTouchEnd, this);
41720         }
41721         
41722         Roo.EventManager.onWindowResize(this.resize, this, true);
41723         
41724         // file input event
41725         this.fileEl().on('change', this.uploadImage, this);
41726         
41727         this.clear();
41728         
41729         this.resize();
41730     },
41731     
41732     resize: function(){
41733         
41734         var canvas = this.canvasEl().dom;
41735         var ctx = this.canvasElCtx();
41736         var img_data = false;
41737         
41738         if(canvas.width > 0) {
41739             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41740         }
41741         // setting canvas width will clean img data
41742         canvas.width = 0;
41743         
41744         var style = window.getComputedStyle ? 
41745             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41746             
41747         var padding_left = parseInt(style.paddingLeft) || 0;
41748         var padding_right = parseInt(style.paddingRight) || 0;
41749         
41750         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41751         
41752         if(img_data) {
41753             ctx.putImageData(img_data, 0, 0);
41754         }
41755     },
41756     
41757     _handleMouseDown: function(e)
41758     {
41759         if (e.browserEvent.which === 1) {
41760             this.mouse_btn_down = true;
41761             this.strokeBegin(e);
41762         }
41763     },
41764     
41765     _handleMouseMove: function (e)
41766     {
41767         if (this.mouse_btn_down) {
41768             this.strokeMoveUpdate(e);
41769         }
41770     },
41771     
41772     _handleMouseUp: function (e)
41773     {
41774         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41775             this.mouse_btn_down = false;
41776             this.strokeEnd(e);
41777         }
41778     },
41779     
41780     _handleTouchStart: function (e) {
41781         
41782         e.preventDefault();
41783         if (e.browserEvent.targetTouches.length === 1) {
41784             // var touch = e.browserEvent.changedTouches[0];
41785             // this.strokeBegin(touch);
41786             
41787              this.strokeBegin(e); // assume e catching the correct xy...
41788         }
41789     },
41790     
41791     _handleTouchMove: function (e) {
41792         e.preventDefault();
41793         // var touch = event.targetTouches[0];
41794         // _this._strokeMoveUpdate(touch);
41795         this.strokeMoveUpdate(e);
41796     },
41797     
41798     _handleTouchEnd: function (e) {
41799         var wasCanvasTouched = e.target === this.canvasEl().dom;
41800         if (wasCanvasTouched) {
41801             e.preventDefault();
41802             // var touch = event.changedTouches[0];
41803             // _this._strokeEnd(touch);
41804             this.strokeEnd(e);
41805         }
41806     },
41807     
41808     reset: function () {
41809         this._lastPoints = [];
41810         this._lastVelocity = 0;
41811         this._lastWidth = (this.min_width + this.max_width) / 2;
41812         this.canvasElCtx().fillStyle = this.dot_color;
41813     },
41814     
41815     strokeMoveUpdate: function(e)
41816     {
41817         this.strokeUpdate(e);
41818         
41819         if (this.throttle) {
41820             this.throttleStroke(this.strokeUpdate, this.throttle);
41821         }
41822         else {
41823             this.strokeUpdate(e);
41824         }
41825     },
41826     
41827     strokeBegin: function(e)
41828     {
41829         var newPointGroup = {
41830             color: this.dot_color,
41831             points: []
41832         };
41833         
41834         if (typeof this.onBegin === 'function') {
41835             this.onBegin(e);
41836         }
41837         
41838         this.curve_data.push(newPointGroup);
41839         this.reset();
41840         this.strokeUpdate(e);
41841     },
41842     
41843     strokeUpdate: function(e)
41844     {
41845         var rect = this.canvasEl().dom.getBoundingClientRect();
41846         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41847         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41848         var lastPoints = lastPointGroup.points;
41849         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41850         var isLastPointTooClose = lastPoint
41851             ? point.distanceTo(lastPoint) <= this.min_distance
41852             : false;
41853         var color = lastPointGroup.color;
41854         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41855             var curve = this.addPoint(point);
41856             if (!lastPoint) {
41857                 this.drawDot({color: color, point: point});
41858             }
41859             else if (curve) {
41860                 this.drawCurve({color: color, curve: curve});
41861             }
41862             lastPoints.push({
41863                 time: point.time,
41864                 x: point.x,
41865                 y: point.y
41866             });
41867         }
41868     },
41869     
41870     strokeEnd: function(e)
41871     {
41872         this.strokeUpdate(e);
41873         if (typeof this.onEnd === 'function') {
41874             this.onEnd(e);
41875         }
41876     },
41877     
41878     addPoint:  function (point) {
41879         var _lastPoints = this._lastPoints;
41880         _lastPoints.push(point);
41881         if (_lastPoints.length > 2) {
41882             if (_lastPoints.length === 3) {
41883                 _lastPoints.unshift(_lastPoints[0]);
41884             }
41885             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41886             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41887             _lastPoints.shift();
41888             return curve;
41889         }
41890         return null;
41891     },
41892     
41893     calculateCurveWidths: function (startPoint, endPoint) {
41894         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41895             (1 - this.velocity_filter_weight) * this._lastVelocity;
41896
41897         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41898         var widths = {
41899             end: newWidth,
41900             start: this._lastWidth
41901         };
41902         
41903         this._lastVelocity = velocity;
41904         this._lastWidth = newWidth;
41905         return widths;
41906     },
41907     
41908     drawDot: function (_a) {
41909         var color = _a.color, point = _a.point;
41910         var ctx = this.canvasElCtx();
41911         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41912         ctx.beginPath();
41913         this.drawCurveSegment(point.x, point.y, width);
41914         ctx.closePath();
41915         ctx.fillStyle = color;
41916         ctx.fill();
41917     },
41918     
41919     drawCurve: function (_a) {
41920         var color = _a.color, curve = _a.curve;
41921         var ctx = this.canvasElCtx();
41922         var widthDelta = curve.endWidth - curve.startWidth;
41923         var drawSteps = Math.floor(curve.length()) * 2;
41924         ctx.beginPath();
41925         ctx.fillStyle = color;
41926         for (var i = 0; i < drawSteps; i += 1) {
41927         var t = i / drawSteps;
41928         var tt = t * t;
41929         var ttt = tt * t;
41930         var u = 1 - t;
41931         var uu = u * u;
41932         var uuu = uu * u;
41933         var x = uuu * curve.startPoint.x;
41934         x += 3 * uu * t * curve.control1.x;
41935         x += 3 * u * tt * curve.control2.x;
41936         x += ttt * curve.endPoint.x;
41937         var y = uuu * curve.startPoint.y;
41938         y += 3 * uu * t * curve.control1.y;
41939         y += 3 * u * tt * curve.control2.y;
41940         y += ttt * curve.endPoint.y;
41941         var width = curve.startWidth + ttt * widthDelta;
41942         this.drawCurveSegment(x, y, width);
41943         }
41944         ctx.closePath();
41945         ctx.fill();
41946     },
41947     
41948     drawCurveSegment: function (x, y, width) {
41949         var ctx = this.canvasElCtx();
41950         ctx.moveTo(x, y);
41951         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41952         this.is_empty = false;
41953     },
41954     
41955     clear: function()
41956     {
41957         var ctx = this.canvasElCtx();
41958         var canvas = this.canvasEl().dom;
41959         ctx.fillStyle = this.bg_color;
41960         ctx.clearRect(0, 0, canvas.width, canvas.height);
41961         ctx.fillRect(0, 0, canvas.width, canvas.height);
41962         this.curve_data = [];
41963         this.reset();
41964         this.is_empty = true;
41965     },
41966     
41967     fileEl: function()
41968     {
41969         return  this.el.select('input',true).first();
41970     },
41971     
41972     canvasEl: function()
41973     {
41974         return this.el.select('canvas',true).first();
41975     },
41976     
41977     canvasElCtx: function()
41978     {
41979         return this.el.select('canvas',true).first().dom.getContext('2d');
41980     },
41981     
41982     getImage: function(type)
41983     {
41984         if(this.is_empty) {
41985             return false;
41986         }
41987         
41988         // encryption ?
41989         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41990     },
41991     
41992     drawFromImage: function(img_src)
41993     {
41994         var img = new Image();
41995         
41996         img.onload = function(){
41997             this.canvasElCtx().drawImage(img, 0, 0);
41998         }.bind(this);
41999         
42000         img.src = img_src;
42001         
42002         this.is_empty = false;
42003     },
42004     
42005     selectImage: function()
42006     {
42007         this.fileEl().dom.click();
42008     },
42009     
42010     uploadImage: function(e)
42011     {
42012         var reader = new FileReader();
42013         
42014         reader.onload = function(e){
42015             var img = new Image();
42016             img.onload = function(){
42017                 this.reset();
42018                 this.canvasElCtx().drawImage(img, 0, 0);
42019             }.bind(this);
42020             img.src = e.target.result;
42021         }.bind(this);
42022         
42023         reader.readAsDataURL(e.target.files[0]);
42024     },
42025     
42026     // Bezier Point Constructor
42027     Point: (function () {
42028         function Point(x, y, time) {
42029             this.x = x;
42030             this.y = y;
42031             this.time = time || Date.now();
42032         }
42033         Point.prototype.distanceTo = function (start) {
42034             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42035         };
42036         Point.prototype.equals = function (other) {
42037             return this.x === other.x && this.y === other.y && this.time === other.time;
42038         };
42039         Point.prototype.velocityFrom = function (start) {
42040             return this.time !== start.time
42041             ? this.distanceTo(start) / (this.time - start.time)
42042             : 0;
42043         };
42044         return Point;
42045     }()),
42046     
42047     
42048     // Bezier Constructor
42049     Bezier: (function () {
42050         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42051             this.startPoint = startPoint;
42052             this.control2 = control2;
42053             this.control1 = control1;
42054             this.endPoint = endPoint;
42055             this.startWidth = startWidth;
42056             this.endWidth = endWidth;
42057         }
42058         Bezier.fromPoints = function (points, widths, scope) {
42059             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42060             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42061             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42062         };
42063         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42064             var dx1 = s1.x - s2.x;
42065             var dy1 = s1.y - s2.y;
42066             var dx2 = s2.x - s3.x;
42067             var dy2 = s2.y - s3.y;
42068             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42069             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42070             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42071             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42072             var dxm = m1.x - m2.x;
42073             var dym = m1.y - m2.y;
42074             var k = l2 / (l1 + l2);
42075             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42076             var tx = s2.x - cm.x;
42077             var ty = s2.y - cm.y;
42078             return {
42079                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42080                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42081             };
42082         };
42083         Bezier.prototype.length = function () {
42084             var steps = 10;
42085             var length = 0;
42086             var px;
42087             var py;
42088             for (var i = 0; i <= steps; i += 1) {
42089                 var t = i / steps;
42090                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42091                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42092                 if (i > 0) {
42093                     var xdiff = cx - px;
42094                     var ydiff = cy - py;
42095                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42096                 }
42097                 px = cx;
42098                 py = cy;
42099             }
42100             return length;
42101         };
42102         Bezier.prototype.point = function (t, start, c1, c2, end) {
42103             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42104             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42105             + (3.0 * c2 * (1.0 - t) * t * t)
42106             + (end * t * t * t);
42107         };
42108         return Bezier;
42109     }()),
42110     
42111     throttleStroke: function(fn, wait) {
42112       if (wait === void 0) { wait = 250; }
42113       var previous = 0;
42114       var timeout = null;
42115       var result;
42116       var storedContext;
42117       var storedArgs;
42118       var later = function () {
42119           previous = Date.now();
42120           timeout = null;
42121           result = fn.apply(storedContext, storedArgs);
42122           if (!timeout) {
42123               storedContext = null;
42124               storedArgs = [];
42125           }
42126       };
42127       return function wrapper() {
42128           var args = [];
42129           for (var _i = 0; _i < arguments.length; _i++) {
42130               args[_i] = arguments[_i];
42131           }
42132           var now = Date.now();
42133           var remaining = wait - (now - previous);
42134           storedContext = this;
42135           storedArgs = args;
42136           if (remaining <= 0 || remaining > wait) {
42137               if (timeout) {
42138                   clearTimeout(timeout);
42139                   timeout = null;
42140               }
42141               previous = now;
42142               result = fn.apply(storedContext, storedArgs);
42143               if (!timeout) {
42144                   storedContext = null;
42145                   storedArgs = [];
42146               }
42147           }
42148           else if (!timeout) {
42149               timeout = window.setTimeout(later, remaining);
42150           }
42151           return result;
42152       };
42153   }
42154   
42155 });
42156
42157  
42158
42159