Merge branch 'master' of http://git.roojs.com/roojs1
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','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;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed (return false to block)
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden (return false to block)
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271     onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu)
2292     {
2293         if (false === this.fireEvent("beforeshow", this)) {
2294             Roo.log("show canceled");
2295             return;
2296         }
2297         this.parentMenu = parentMenu;
2298         if(!this.el){
2299             this.render();
2300         }
2301         
2302         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2303     },
2304      /**
2305      * Displays this menu at a specific xy position
2306      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2307      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2308      */
2309     showAt : function(xy, parentMenu, /* private: */_e){
2310         this.parentMenu = parentMenu;
2311         if(!this.el){
2312             this.render();
2313         }
2314         if(_e !== false){
2315             this.fireEvent("beforeshow", this);
2316             //xy = this.el.adjustForConstraints(xy);
2317         }
2318         
2319         //this.el.show();
2320         this.hideMenuItems();
2321         this.hidden = false;
2322         this.triggerEl.addClass('open');
2323         this.el.addClass('show');
2324         
2325         // reassign x when hitting right
2326         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2327             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2328         }
2329         
2330         // reassign y when hitting bottom
2331         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2332             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2333         }
2334         
2335         // but the list may align on trigger left or trigger top... should it be a properity?
2336         
2337         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2338             this.el.setXY(xy);
2339         }
2340         
2341         this.focus();
2342         this.fireEvent("show", this);
2343     },
2344     
2345     focus : function(){
2346         return;
2347         if(!this.hidden){
2348             this.doFocus.defer(50, this);
2349         }
2350     },
2351
2352     doFocus : function(){
2353         if(!this.hidden){
2354             this.focusEl.focus();
2355         }
2356     },
2357
2358     /**
2359      * Hides this menu and optionally all parent menus
2360      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2361      */
2362     hide : function(deep)
2363     {
2364         if (false === this.fireEvent("beforehide", this)) {
2365             Roo.log("hide canceled");
2366             return;
2367         }
2368         this.hideMenuItems();
2369         if(this.el && this.isVisible()){
2370            
2371             if(this.activeItem){
2372                 this.activeItem.deactivate();
2373                 this.activeItem = null;
2374             }
2375             this.triggerEl.removeClass('open');;
2376             this.el.removeClass('show');
2377             this.hidden = true;
2378             this.fireEvent("hide", this);
2379         }
2380         if(deep === true && this.parentMenu){
2381             this.parentMenu.hide(true);
2382         }
2383     },
2384     
2385     onTriggerClick : function(e)
2386     {
2387         Roo.log('trigger click');
2388         
2389         var target = e.getTarget();
2390         
2391         Roo.log(target.nodeName.toLowerCase());
2392         
2393         if(target.nodeName.toLowerCase() === 'i'){
2394             e.preventDefault();
2395         }
2396         
2397     },
2398     
2399     onTriggerPress  : function(e)
2400     {
2401         Roo.log('trigger press');
2402         //Roo.log(e.getTarget());
2403        // Roo.log(this.triggerEl.dom);
2404        
2405         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2406         var pel = Roo.get(e.getTarget());
2407         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2408             Roo.log('is treeview or dropdown?');
2409             return;
2410         }
2411         
2412         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2413             return;
2414         }
2415         
2416         if (this.isVisible()) {
2417             Roo.log('hide');
2418             this.hide();
2419         } else {
2420             Roo.log('show');
2421             this.show(this.triggerEl, '?', false);
2422         }
2423         
2424         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2425             e.stopEvent();
2426         }
2427         
2428     },
2429        
2430     
2431     hideMenuItems : function()
2432     {
2433         Roo.log("hide Menu Items");
2434         if (!this.el) { 
2435             return;
2436         }
2437         
2438         this.el.select('.open',true).each(function(aa) {
2439             
2440             aa.removeClass('open');
2441          
2442         });
2443     },
2444     addxtypeChild : function (tree, cntr) {
2445         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2446           
2447         this.menuitems.add(comp);
2448         return comp;
2449
2450     },
2451     getEl : function()
2452     {
2453         Roo.log(this.el);
2454         return this.el;
2455     },
2456     
2457     clear : function()
2458     {
2459         this.getEl().dom.innerHTML = '';
2460         this.menuitems.clear();
2461     }
2462 });
2463
2464  
2465  /*
2466  * - LGPL
2467  *
2468  * menu item
2469  * 
2470  */
2471
2472
2473 /**
2474  * @class Roo.bootstrap.MenuItem
2475  * @extends Roo.bootstrap.Component
2476  * Bootstrap MenuItem class
2477  * @cfg {String} html the menu label
2478  * @cfg {String} href the link
2479  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2480  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2481  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2482  * @cfg {String} fa favicon to show on left of menu item.
2483  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2484  * 
2485  * 
2486  * @constructor
2487  * Create a new MenuItem
2488  * @param {Object} config The config object
2489  */
2490
2491
2492 Roo.bootstrap.MenuItem = function(config){
2493     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494     this.addEvents({
2495         // raw events
2496         /**
2497          * @event click
2498          * The raw click event for the entire grid.
2499          * @param {Roo.bootstrap.MenuItem} this
2500          * @param {Roo.EventObject} e
2501          */
2502         "click" : true
2503     });
2504 };
2505
2506 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2507     
2508     href : false,
2509     html : false,
2510     preventDefault: false,
2511     isContainer : false,
2512     active : false,
2513     fa: false,
2514     
2515     getAutoCreate : function(){
2516         
2517         if(this.isContainer){
2518             return {
2519                 tag: 'li',
2520                 cls: 'dropdown-menu-item '
2521             };
2522         }
2523         var ctag = {
2524             tag: 'span',
2525             html: 'Link'
2526         };
2527         
2528         var anc = {
2529             tag : 'a',
2530             cls : 'dropdown-item',
2531             href : '#',
2532             cn : [  ]
2533         };
2534         
2535         if (this.fa !== false) {
2536             anc.cn.push({
2537                 tag : 'i',
2538                 cls : 'fa fa-' + this.fa
2539             });
2540         }
2541         
2542         anc.cn.push(ctag);
2543         
2544         
2545         var cfg= {
2546             tag: 'li',
2547             cls: 'dropdown-menu-item',
2548             cn: [ anc ]
2549         };
2550         if (this.parent().type == 'treeview') {
2551             cfg.cls = 'treeview-menu';
2552         }
2553         if (this.active) {
2554             cfg.cls += ' active';
2555         }
2556         
2557         
2558         
2559         anc.href = this.href || cfg.cn[0].href ;
2560         ctag.html = this.html || cfg.cn[0].html ;
2561         return cfg;
2562     },
2563     
2564     initEvents: function()
2565     {
2566         if (this.parent().type == 'treeview') {
2567             this.el.select('a').on('click', this.onClick, this);
2568         }
2569         
2570         if (this.menu) {
2571             this.menu.parentType = this.xtype;
2572             this.menu.triggerEl = this.el;
2573             this.menu = this.addxtype(Roo.apply({}, this.menu));
2574         }
2575         
2576     },
2577     onClick : function(e)
2578     {
2579         Roo.log('item on click ');
2580         
2581         if(this.preventDefault){
2582             e.preventDefault();
2583         }
2584         //this.parent().hideMenuItems();
2585         
2586         this.fireEvent('click', this, e);
2587     },
2588     getEl : function()
2589     {
2590         return this.el;
2591     } 
2592 });
2593
2594  
2595
2596  /*
2597  * - LGPL
2598  *
2599  * menu separator
2600  * 
2601  */
2602
2603
2604 /**
2605  * @class Roo.bootstrap.MenuSeparator
2606  * @extends Roo.bootstrap.Component
2607  * Bootstrap MenuSeparator class
2608  * 
2609  * @constructor
2610  * Create a new MenuItem
2611  * @param {Object} config The config object
2612  */
2613
2614
2615 Roo.bootstrap.MenuSeparator = function(config){
2616     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2617 };
2618
2619 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2620     
2621     getAutoCreate : function(){
2622         var cfg = {
2623             cls: 'divider',
2624             tag : 'li'
2625         };
2626         
2627         return cfg;
2628     }
2629    
2630 });
2631
2632  
2633
2634  
2635 /*
2636 * Licence: LGPL
2637 */
2638
2639 /**
2640  * @class Roo.bootstrap.Modal
2641  * @extends Roo.bootstrap.Component
2642  * Bootstrap Modal class
2643  * @cfg {String} title Title of dialog
2644  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2645  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2646  * @cfg {Boolean} specificTitle default false
2647  * @cfg {Array} buttons Array of buttons or standard button set..
2648  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2649  * @cfg {Boolean} animate default true
2650  * @cfg {Boolean} allow_close default true
2651  * @cfg {Boolean} fitwindow default false
2652  * @cfg {String} size (sm|lg) default empty
2653  * @cfg {Number} max_width set the max width of modal
2654  *
2655  *
2656  * @constructor
2657  * Create a new Modal Dialog
2658  * @param {Object} config The config object
2659  */
2660
2661 Roo.bootstrap.Modal = function(config){
2662     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2663     this.addEvents({
2664         // raw events
2665         /**
2666          * @event btnclick
2667          * The raw btnclick event for the button
2668          * @param {Roo.EventObject} e
2669          */
2670         "btnclick" : true,
2671         /**
2672          * @event resize
2673          * Fire when dialog resize
2674          * @param {Roo.bootstrap.Modal} this
2675          * @param {Roo.EventObject} e
2676          */
2677         "resize" : true
2678     });
2679     this.buttons = this.buttons || [];
2680
2681     if (this.tmpl) {
2682         this.tmpl = Roo.factory(this.tmpl);
2683     }
2684
2685 };
2686
2687 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2688
2689     title : 'test dialog',
2690
2691     buttons : false,
2692
2693     // set on load...
2694
2695     html: false,
2696
2697     tmp: false,
2698
2699     specificTitle: false,
2700
2701     buttonPosition: 'right',
2702
2703     allow_close : true,
2704
2705     animate : true,
2706
2707     fitwindow: false,
2708     
2709      // private
2710     dialogEl: false,
2711     bodyEl:  false,
2712     footerEl:  false,
2713     titleEl:  false,
2714     closeEl:  false,
2715
2716     size: '',
2717     
2718     max_width: 0,
2719     
2720     max_height: 0,
2721     
2722     fit_content: false,
2723
2724     onRender : function(ct, position)
2725     {
2726         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2727
2728         if(!this.el){
2729             var cfg = Roo.apply({},  this.getAutoCreate());
2730             cfg.id = Roo.id();
2731             //if(!cfg.name){
2732             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2733             //}
2734             //if (!cfg.name.length) {
2735             //    delete cfg.name;
2736            // }
2737             if (this.cls) {
2738                 cfg.cls += ' ' + this.cls;
2739             }
2740             if (this.style) {
2741                 cfg.style = this.style;
2742             }
2743             this.el = Roo.get(document.body).createChild(cfg, position);
2744         }
2745         //var type = this.el.dom.type;
2746
2747
2748         if(this.tabIndex !== undefined){
2749             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2750         }
2751
2752         this.dialogEl = this.el.select('.modal-dialog',true).first();
2753         this.bodyEl = this.el.select('.modal-body',true).first();
2754         this.closeEl = this.el.select('.modal-header .close', true).first();
2755         this.headerEl = this.el.select('.modal-header',true).first();
2756         this.titleEl = this.el.select('.modal-title',true).first();
2757         this.footerEl = this.el.select('.modal-footer',true).first();
2758
2759         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2760         
2761         //this.el.addClass("x-dlg-modal");
2762
2763         if (this.buttons.length) {
2764             Roo.each(this.buttons, function(bb) {
2765                 var b = Roo.apply({}, bb);
2766                 b.xns = b.xns || Roo.bootstrap;
2767                 b.xtype = b.xtype || 'Button';
2768                 if (typeof(b.listeners) == 'undefined') {
2769                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2770                 }
2771
2772                 var btn = Roo.factory(b);
2773
2774                 btn.render(this.getButtonContainer());
2775
2776             },this);
2777         }
2778         // render the children.
2779         var nitems = [];
2780
2781         if(typeof(this.items) != 'undefined'){
2782             var items = this.items;
2783             delete this.items;
2784
2785             for(var i =0;i < items.length;i++) {
2786                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2787             }
2788         }
2789
2790         this.items = nitems;
2791
2792         // where are these used - they used to be body/close/footer
2793
2794
2795         this.initEvents();
2796         //this.el.addClass([this.fieldClass, this.cls]);
2797
2798     },
2799
2800     getAutoCreate : function()
2801     {
2802         var bdy = {
2803                 cls : 'modal-body',
2804                 html : this.html || ''
2805         };
2806
2807         var title = {
2808             tag: 'h4',
2809             cls : 'modal-title',
2810             html : this.title
2811         };
2812
2813         if(this.specificTitle){
2814             title = this.title;
2815
2816         }
2817
2818         var header = [];
2819         if (this.allow_close && Roo.bootstrap.version == 3) {
2820             header.push({
2821                 tag: 'button',
2822                 cls : 'close',
2823                 html : '&times'
2824             });
2825         }
2826
2827         header.push(title);
2828
2829         if (this.allow_close && Roo.bootstrap.version == 4) {
2830             header.push({
2831                 tag: 'button',
2832                 cls : 'close',
2833                 html : '&times'
2834             });
2835         }
2836         
2837         var size = '';
2838
2839         if(this.size.length){
2840             size = 'modal-' + this.size;
2841         }
2842         
2843         var footer = Roo.bootstrap.version == 3 ?
2844             {
2845                 cls : 'modal-footer',
2846                 cn : [
2847                     {
2848                         tag: 'div',
2849                         cls: 'btn-' + this.buttonPosition
2850                     }
2851                 ]
2852
2853             } :
2854             {  // BS4 uses mr-auto on left buttons....
2855                 cls : 'modal-footer'
2856             };
2857
2858             
2859
2860         
2861         
2862         var modal = {
2863             cls: "modal",
2864              cn : [
2865                 {
2866                     cls: "modal-dialog " + size,
2867                     cn : [
2868                         {
2869                             cls : "modal-content",
2870                             cn : [
2871                                 {
2872                                     cls : 'modal-header',
2873                                     cn : header
2874                                 },
2875                                 bdy,
2876                                 footer
2877                             ]
2878
2879                         }
2880                     ]
2881
2882                 }
2883             ]
2884         };
2885
2886         if(this.animate){
2887             modal.cls += ' fade';
2888         }
2889
2890         return modal;
2891
2892     },
2893     getChildContainer : function() {
2894
2895          return this.bodyEl;
2896
2897     },
2898     getButtonContainer : function() {
2899         
2900          return Roo.bootstrap.version == 4 ?
2901             this.el.select('.modal-footer',true).first()
2902             : this.el.select('.modal-footer div',true).first();
2903
2904     },
2905     initEvents : function()
2906     {
2907         if (this.allow_close) {
2908             this.closeEl.on('click', this.hide, this);
2909         }
2910         Roo.EventManager.onWindowResize(this.resize, this, true);
2911
2912
2913     },
2914   
2915
2916     resize : function()
2917     {
2918         this.maskEl.setSize(
2919             Roo.lib.Dom.getViewWidth(true),
2920             Roo.lib.Dom.getViewHeight(true)
2921         );
2922         
2923         if (this.fitwindow) {
2924             
2925            
2926             this.setSize(
2927                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2928                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2929             );
2930             return;
2931         }
2932         
2933         if(this.max_width !== 0) {
2934             
2935             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2936             
2937             if(this.height) {
2938                 this.setSize(w, this.height);
2939                 return;
2940             }
2941             
2942             if(this.max_height) {
2943                 this.setSize(w,Math.min(
2944                     this.max_height,
2945                     Roo.lib.Dom.getViewportHeight(true) - 60
2946                 ));
2947                 
2948                 return;
2949             }
2950             
2951             if(!this.fit_content) {
2952                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2953                 return;
2954             }
2955             
2956             this.setSize(w, Math.min(
2957                 60 +
2958                 this.headerEl.getHeight() + 
2959                 this.footerEl.getHeight() + 
2960                 this.getChildHeight(this.bodyEl.dom.childNodes),
2961                 Roo.lib.Dom.getViewportHeight(true) - 60)
2962             );
2963         }
2964         
2965     },
2966
2967     setSize : function(w,h)
2968     {
2969         if (!w && !h) {
2970             return;
2971         }
2972         
2973         this.resizeTo(w,h);
2974     },
2975
2976     show : function() {
2977
2978         if (!this.rendered) {
2979             this.render();
2980         }
2981
2982         //this.el.setStyle('display', 'block');
2983         this.el.removeClass('hideing');
2984         this.el.dom.style.display='block';
2985         
2986         Roo.get(document.body).addClass('modal-open');
2987  
2988         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2989             
2990             (function(){
2991                 this.el.addClass('show');
2992                 this.el.addClass('in');
2993             }).defer(50, this);
2994         }else{
2995             this.el.addClass('show');
2996             this.el.addClass('in');
2997         }
2998
2999         // not sure how we can show data in here..
3000         //if (this.tmpl) {
3001         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3002         //}
3003
3004         Roo.get(document.body).addClass("x-body-masked");
3005         
3006         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3007         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3008         this.maskEl.dom.style.display = 'block';
3009         this.maskEl.addClass('show');
3010         
3011         
3012         this.resize();
3013         
3014         this.fireEvent('show', this);
3015
3016         // set zindex here - otherwise it appears to be ignored...
3017         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3018
3019         (function () {
3020             this.items.forEach( function(e) {
3021                 e.layout ? e.layout() : false;
3022
3023             });
3024         }).defer(100,this);
3025
3026     },
3027     hide : function()
3028     {
3029         if(this.fireEvent("beforehide", this) !== false){
3030             
3031             this.maskEl.removeClass('show');
3032             
3033             this.maskEl.dom.style.display = '';
3034             Roo.get(document.body).removeClass("x-body-masked");
3035             this.el.removeClass('in');
3036             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3037
3038             if(this.animate){ // why
3039                 this.el.addClass('hideing');
3040                 this.el.removeClass('show');
3041                 (function(){
3042                     if (!this.el.hasClass('hideing')) {
3043                         return; // it's been shown again...
3044                     }
3045                     
3046                     this.el.dom.style.display='';
3047
3048                     Roo.get(document.body).removeClass('modal-open');
3049                     this.el.removeClass('hideing');
3050                 }).defer(150,this);
3051                 
3052             }else{
3053                 this.el.removeClass('show');
3054                 this.el.dom.style.display='';
3055                 Roo.get(document.body).removeClass('modal-open');
3056
3057             }
3058             this.fireEvent('hide', this);
3059         }
3060     },
3061     isVisible : function()
3062     {
3063         
3064         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3065         
3066     },
3067
3068     addButton : function(str, cb)
3069     {
3070
3071
3072         var b = Roo.apply({}, { html : str } );
3073         b.xns = b.xns || Roo.bootstrap;
3074         b.xtype = b.xtype || 'Button';
3075         if (typeof(b.listeners) == 'undefined') {
3076             b.listeners = { click : cb.createDelegate(this)  };
3077         }
3078
3079         var btn = Roo.factory(b);
3080
3081         btn.render(this.getButtonContainer());
3082
3083         return btn;
3084
3085     },
3086
3087     setDefaultButton : function(btn)
3088     {
3089         //this.el.select('.modal-footer').()
3090     },
3091
3092     resizeTo: function(w,h)
3093     {
3094         this.dialogEl.setWidth(w);
3095         
3096         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3097
3098         this.bodyEl.setHeight(h - diff);
3099         
3100         this.fireEvent('resize', this);
3101     },
3102     
3103     setContentSize  : function(w, h)
3104     {
3105
3106     },
3107     onButtonClick: function(btn,e)
3108     {
3109         //Roo.log([a,b,c]);
3110         this.fireEvent('btnclick', btn.name, e);
3111     },
3112      /**
3113      * Set the title of the Dialog
3114      * @param {String} str new Title
3115      */
3116     setTitle: function(str) {
3117         this.titleEl.dom.innerHTML = str;
3118     },
3119     /**
3120      * Set the body of the Dialog
3121      * @param {String} str new Title
3122      */
3123     setBody: function(str) {
3124         this.bodyEl.dom.innerHTML = str;
3125     },
3126     /**
3127      * Set the body of the Dialog using the template
3128      * @param {Obj} data - apply this data to the template and replace the body contents.
3129      */
3130     applyBody: function(obj)
3131     {
3132         if (!this.tmpl) {
3133             Roo.log("Error - using apply Body without a template");
3134             //code
3135         }
3136         this.tmpl.overwrite(this.bodyEl, obj);
3137     },
3138     
3139     getChildHeight : function(child_nodes)
3140     {
3141         if(
3142             !child_nodes ||
3143             child_nodes.length == 0
3144         ) {
3145             return;
3146         }
3147         
3148         var child_height = 0;
3149         
3150         for(var i = 0; i < child_nodes.length; i++) {
3151             
3152             /*
3153             * for modal with tabs...
3154             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3155                 
3156                 var layout_childs = child_nodes[i].childNodes;
3157                 
3158                 for(var j = 0; j < layout_childs.length; j++) {
3159                     
3160                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3161                         
3162                         var layout_body_childs = layout_childs[j].childNodes;
3163                         
3164                         for(var k = 0; k < layout_body_childs.length; k++) {
3165                             
3166                             if(layout_body_childs[k].classList.contains('navbar')) {
3167                                 child_height += layout_body_childs[k].offsetHeight;
3168                                 continue;
3169                             }
3170                             
3171                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3172                                 
3173                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3174                                 
3175                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3176                                     
3177                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3178                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3179                                         continue;
3180                                     }
3181                                     
3182                                 }
3183                                 
3184                             }
3185                             
3186                         }
3187                     }
3188                 }
3189                 continue;
3190             }
3191             */
3192             
3193             child_height += child_nodes[i].offsetHeight;
3194             // Roo.log(child_nodes[i].offsetHeight);
3195         }
3196         
3197         return child_height;
3198     }
3199
3200 });
3201
3202
3203 Roo.apply(Roo.bootstrap.Modal,  {
3204     /**
3205          * Button config that displays a single OK button
3206          * @type Object
3207          */
3208         OK :  [{
3209             name : 'ok',
3210             weight : 'primary',
3211             html : 'OK'
3212         }],
3213         /**
3214          * Button config that displays Yes and No buttons
3215          * @type Object
3216          */
3217         YESNO : [
3218             {
3219                 name  : 'no',
3220                 html : 'No'
3221             },
3222             {
3223                 name  :'yes',
3224                 weight : 'primary',
3225                 html : 'Yes'
3226             }
3227         ],
3228
3229         /**
3230          * Button config that displays OK and Cancel buttons
3231          * @type Object
3232          */
3233         OKCANCEL : [
3234             {
3235                name : 'cancel',
3236                 html : 'Cancel'
3237             },
3238             {
3239                 name : 'ok',
3240                 weight : 'primary',
3241                 html : 'OK'
3242             }
3243         ],
3244         /**
3245          * Button config that displays Yes, No and Cancel buttons
3246          * @type Object
3247          */
3248         YESNOCANCEL : [
3249             {
3250                 name : 'yes',
3251                 weight : 'primary',
3252                 html : 'Yes'
3253             },
3254             {
3255                 name : 'no',
3256                 html : 'No'
3257             },
3258             {
3259                 name : 'cancel',
3260                 html : 'Cancel'
3261             }
3262         ],
3263         
3264         zIndex : 10001
3265 });
3266 /*
3267  * - LGPL
3268  *
3269  * messagebox - can be used as a replace
3270  * 
3271  */
3272 /**
3273  * @class Roo.MessageBox
3274  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3275  * Example usage:
3276  *<pre><code>
3277 // Basic alert:
3278 Roo.Msg.alert('Status', 'Changes saved successfully.');
3279
3280 // Prompt for user data:
3281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3282     if (btn == 'ok'){
3283         // process text value...
3284     }
3285 });
3286
3287 // Show a dialog using config options:
3288 Roo.Msg.show({
3289    title:'Save Changes?',
3290    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3291    buttons: Roo.Msg.YESNOCANCEL,
3292    fn: processResult,
3293    animEl: 'elId'
3294 });
3295 </code></pre>
3296  * @singleton
3297  */
3298 Roo.bootstrap.MessageBox = function(){
3299     var dlg, opt, mask, waitTimer;
3300     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3301     var buttons, activeTextEl, bwidth;
3302
3303     
3304     // private
3305     var handleButton = function(button){
3306         dlg.hide();
3307         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3308     };
3309
3310     // private
3311     var handleHide = function(){
3312         if(opt && opt.cls){
3313             dlg.el.removeClass(opt.cls);
3314         }
3315         //if(waitTimer){
3316         //    Roo.TaskMgr.stop(waitTimer);
3317         //    waitTimer = null;
3318         //}
3319     };
3320
3321     // private
3322     var updateButtons = function(b){
3323         var width = 0;
3324         if(!b){
3325             buttons["ok"].hide();
3326             buttons["cancel"].hide();
3327             buttons["yes"].hide();
3328             buttons["no"].hide();
3329             dlg.footerEl.hide();
3330             
3331             return width;
3332         }
3333         dlg.footerEl.show();
3334         for(var k in buttons){
3335             if(typeof buttons[k] != "function"){
3336                 if(b[k]){
3337                     buttons[k].show();
3338                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3339                     width += buttons[k].el.getWidth()+15;
3340                 }else{
3341                     buttons[k].hide();
3342                 }
3343             }
3344         }
3345         return width;
3346     };
3347
3348     // private
3349     var handleEsc = function(d, k, e){
3350         if(opt && opt.closable !== false){
3351             dlg.hide();
3352         }
3353         if(e){
3354             e.stopEvent();
3355         }
3356     };
3357
3358     return {
3359         /**
3360          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3361          * @return {Roo.BasicDialog} The BasicDialog element
3362          */
3363         getDialog : function(){
3364            if(!dlg){
3365                 dlg = new Roo.bootstrap.Modal( {
3366                     //draggable: true,
3367                     //resizable:false,
3368                     //constraintoviewport:false,
3369                     //fixedcenter:true,
3370                     //collapsible : false,
3371                     //shim:true,
3372                     //modal: true,
3373                 //    width: 'auto',
3374                   //  height:100,
3375                     //buttonAlign:"center",
3376                     closeClick : function(){
3377                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3378                             handleButton("no");
3379                         }else{
3380                             handleButton("cancel");
3381                         }
3382                     }
3383                 });
3384                 dlg.render();
3385                 dlg.on("hide", handleHide);
3386                 mask = dlg.mask;
3387                 //dlg.addKeyListener(27, handleEsc);
3388                 buttons = {};
3389                 this.buttons = buttons;
3390                 var bt = this.buttonText;
3391                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3392                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3393                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3394                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3395                 //Roo.log(buttons);
3396                 bodyEl = dlg.bodyEl.createChild({
3397
3398                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3399                         '<textarea class="roo-mb-textarea"></textarea>' +
3400                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3401                 });
3402                 msgEl = bodyEl.dom.firstChild;
3403                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3404                 textboxEl.enableDisplayMode();
3405                 textboxEl.addKeyListener([10,13], function(){
3406                     if(dlg.isVisible() && opt && opt.buttons){
3407                         if(opt.buttons.ok){
3408                             handleButton("ok");
3409                         }else if(opt.buttons.yes){
3410                             handleButton("yes");
3411                         }
3412                     }
3413                 });
3414                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3415                 textareaEl.enableDisplayMode();
3416                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3417                 progressEl.enableDisplayMode();
3418                 
3419                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3420                 var pf = progressEl.dom.firstChild;
3421                 if (pf) {
3422                     pp = Roo.get(pf.firstChild);
3423                     pp.setHeight(pf.offsetHeight);
3424                 }
3425                 
3426             }
3427             return dlg;
3428         },
3429
3430         /**
3431          * Updates the message box body text
3432          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3433          * the XHTML-compliant non-breaking space character '&amp;#160;')
3434          * @return {Roo.MessageBox} This message box
3435          */
3436         updateText : function(text)
3437         {
3438             if(!dlg.isVisible() && !opt.width){
3439                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3440                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3441             }
3442             msgEl.innerHTML = text || '&#160;';
3443       
3444             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3445             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3446             var w = Math.max(
3447                     Math.min(opt.width || cw , this.maxWidth), 
3448                     Math.max(opt.minWidth || this.minWidth, bwidth)
3449             );
3450             if(opt.prompt){
3451                 activeTextEl.setWidth(w);
3452             }
3453             if(dlg.isVisible()){
3454                 dlg.fixedcenter = false;
3455             }
3456             // to big, make it scroll. = But as usual stupid IE does not support
3457             // !important..
3458             
3459             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3460                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3461                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3462             } else {
3463                 bodyEl.dom.style.height = '';
3464                 bodyEl.dom.style.overflowY = '';
3465             }
3466             if (cw > w) {
3467                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3468             } else {
3469                 bodyEl.dom.style.overflowX = '';
3470             }
3471             
3472             dlg.setContentSize(w, bodyEl.getHeight());
3473             if(dlg.isVisible()){
3474                 dlg.fixedcenter = true;
3475             }
3476             return this;
3477         },
3478
3479         /**
3480          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3481          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3482          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3483          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3484          * @return {Roo.MessageBox} This message box
3485          */
3486         updateProgress : function(value, text){
3487             if(text){
3488                 this.updateText(text);
3489             }
3490             
3491             if (pp) { // weird bug on my firefox - for some reason this is not defined
3492                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3493                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3494             }
3495             return this;
3496         },        
3497
3498         /**
3499          * Returns true if the message box is currently displayed
3500          * @return {Boolean} True if the message box is visible, else false
3501          */
3502         isVisible : function(){
3503             return dlg && dlg.isVisible();  
3504         },
3505
3506         /**
3507          * Hides the message box if it is displayed
3508          */
3509         hide : function(){
3510             if(this.isVisible()){
3511                 dlg.hide();
3512             }  
3513         },
3514
3515         /**
3516          * Displays a new message box, or reinitializes an existing message box, based on the config options
3517          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3518          * The following config object properties are supported:
3519          * <pre>
3520 Property    Type             Description
3521 ----------  ---------------  ------------------------------------------------------------------------------------
3522 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3523                                    closes (defaults to undefined)
3524 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3525                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3526 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3527                                    progress and wait dialogs will ignore this property and always hide the
3528                                    close button as they can only be closed programmatically.
3529 cls               String           A custom CSS class to apply to the message box element
3530 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3531                                    displayed (defaults to 75)
3532 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3533                                    function will be btn (the name of the button that was clicked, if applicable,
3534                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3535                                    Progress and wait dialogs will ignore this option since they do not respond to
3536                                    user actions and can only be closed programmatically, so any required function
3537                                    should be called by the same code after it closes the dialog.
3538 icon              String           A CSS class that provides a background image to be used as an icon for
3539                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3540 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3541 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3542 modal             Boolean          False to allow user interaction with the page while the message box is
3543                                    displayed (defaults to true)
3544 msg               String           A string that will replace the existing message box body text (defaults
3545                                    to the XHTML-compliant non-breaking space character '&#160;')
3546 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3547 progress          Boolean          True to display a progress bar (defaults to false)
3548 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3549 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3550 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3551 title             String           The title text
3552 value             String           The string value to set into the active textbox element if displayed
3553 wait              Boolean          True to display a progress bar (defaults to false)
3554 width             Number           The width of the dialog in pixels
3555 </pre>
3556          *
3557          * Example usage:
3558          * <pre><code>
3559 Roo.Msg.show({
3560    title: 'Address',
3561    msg: 'Please enter your address:',
3562    width: 300,
3563    buttons: Roo.MessageBox.OKCANCEL,
3564    multiline: true,
3565    fn: saveAddress,
3566    animEl: 'addAddressBtn'
3567 });
3568 </code></pre>
3569          * @param {Object} config Configuration options
3570          * @return {Roo.MessageBox} This message box
3571          */
3572         show : function(options)
3573         {
3574             
3575             // this causes nightmares if you show one dialog after another
3576             // especially on callbacks..
3577              
3578             if(this.isVisible()){
3579                 
3580                 this.hide();
3581                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3582                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3583                 Roo.log("New Dialog Message:" +  options.msg )
3584                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3585                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3586                 
3587             }
3588             var d = this.getDialog();
3589             opt = options;
3590             d.setTitle(opt.title || "&#160;");
3591             d.closeEl.setDisplayed(opt.closable !== false);
3592             activeTextEl = textboxEl;
3593             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3594             if(opt.prompt){
3595                 if(opt.multiline){
3596                     textboxEl.hide();
3597                     textareaEl.show();
3598                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3599                         opt.multiline : this.defaultTextHeight);
3600                     activeTextEl = textareaEl;
3601                 }else{
3602                     textboxEl.show();
3603                     textareaEl.hide();
3604                 }
3605             }else{
3606                 textboxEl.hide();
3607                 textareaEl.hide();
3608             }
3609             progressEl.setDisplayed(opt.progress === true);
3610             if (opt.progress) {
3611                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3612             }
3613             this.updateProgress(0);
3614             activeTextEl.dom.value = opt.value || "";
3615             if(opt.prompt){
3616                 dlg.setDefaultButton(activeTextEl);
3617             }else{
3618                 var bs = opt.buttons;
3619                 var db = null;
3620                 if(bs && bs.ok){
3621                     db = buttons["ok"];
3622                 }else if(bs && bs.yes){
3623                     db = buttons["yes"];
3624                 }
3625                 dlg.setDefaultButton(db);
3626             }
3627             bwidth = updateButtons(opt.buttons);
3628             this.updateText(opt.msg);
3629             if(opt.cls){
3630                 d.el.addClass(opt.cls);
3631             }
3632             d.proxyDrag = opt.proxyDrag === true;
3633             d.modal = opt.modal !== false;
3634             d.mask = opt.modal !== false ? mask : false;
3635             if(!d.isVisible()){
3636                 // force it to the end of the z-index stack so it gets a cursor in FF
3637                 document.body.appendChild(dlg.el.dom);
3638                 d.animateTarget = null;
3639                 d.show(options.animEl);
3640             }
3641             return this;
3642         },
3643
3644         /**
3645          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3646          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3647          * and closing the message box when the process is complete.
3648          * @param {String} title The title bar text
3649          * @param {String} msg The message box body text
3650          * @return {Roo.MessageBox} This message box
3651          */
3652         progress : function(title, msg){
3653             this.show({
3654                 title : title,
3655                 msg : msg,
3656                 buttons: false,
3657                 progress:true,
3658                 closable:false,
3659                 minWidth: this.minProgressWidth,
3660                 modal : true
3661             });
3662             return this;
3663         },
3664
3665         /**
3666          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3667          * If a callback function is passed it will be called after the user clicks the button, and the
3668          * id of the button that was clicked will be passed as the only parameter to the callback
3669          * (could also be the top-right close button).
3670          * @param {String} title The title bar text
3671          * @param {String} msg The message box body text
3672          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3673          * @param {Object} scope (optional) The scope of the callback function
3674          * @return {Roo.MessageBox} This message box
3675          */
3676         alert : function(title, msg, fn, scope)
3677         {
3678             this.show({
3679                 title : title,
3680                 msg : msg,
3681                 buttons: this.OK,
3682                 fn: fn,
3683                 closable : false,
3684                 scope : scope,
3685                 modal : true
3686             });
3687             return this;
3688         },
3689
3690         /**
3691          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3692          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3693          * You are responsible for closing the message box when the process is complete.
3694          * @param {String} msg The message box body text
3695          * @param {String} title (optional) The title bar text
3696          * @return {Roo.MessageBox} This message box
3697          */
3698         wait : function(msg, title){
3699             this.show({
3700                 title : title,
3701                 msg : msg,
3702                 buttons: false,
3703                 closable:false,
3704                 progress:true,
3705                 modal:true,
3706                 width:300,
3707                 wait:true
3708             });
3709             waitTimer = Roo.TaskMgr.start({
3710                 run: function(i){
3711                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3712                 },
3713                 interval: 1000
3714             });
3715             return this;
3716         },
3717
3718         /**
3719          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3720          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3721          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3722          * @param {String} title The title bar text
3723          * @param {String} msg The message box body text
3724          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3725          * @param {Object} scope (optional) The scope of the callback function
3726          * @return {Roo.MessageBox} This message box
3727          */
3728         confirm : function(title, msg, fn, scope){
3729             this.show({
3730                 title : title,
3731                 msg : msg,
3732                 buttons: this.YESNO,
3733                 fn: fn,
3734                 scope : scope,
3735                 modal : true
3736             });
3737             return this;
3738         },
3739
3740         /**
3741          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3742          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3743          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3744          * (could also be the top-right close button) and the text that was entered will be passed as the two
3745          * parameters to the callback.
3746          * @param {String} title The title bar text
3747          * @param {String} msg The message box body text
3748          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3749          * @param {Object} scope (optional) The scope of the callback function
3750          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3751          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3752          * @return {Roo.MessageBox} This message box
3753          */
3754         prompt : function(title, msg, fn, scope, multiline){
3755             this.show({
3756                 title : title,
3757                 msg : msg,
3758                 buttons: this.OKCANCEL,
3759                 fn: fn,
3760                 minWidth:250,
3761                 scope : scope,
3762                 prompt:true,
3763                 multiline: multiline,
3764                 modal : true
3765             });
3766             return this;
3767         },
3768
3769         /**
3770          * Button config that displays a single OK button
3771          * @type Object
3772          */
3773         OK : {ok:true},
3774         /**
3775          * Button config that displays Yes and No buttons
3776          * @type Object
3777          */
3778         YESNO : {yes:true, no:true},
3779         /**
3780          * Button config that displays OK and Cancel buttons
3781          * @type Object
3782          */
3783         OKCANCEL : {ok:true, cancel:true},
3784         /**
3785          * Button config that displays Yes, No and Cancel buttons
3786          * @type Object
3787          */
3788         YESNOCANCEL : {yes:true, no:true, cancel:true},
3789
3790         /**
3791          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3792          * @type Number
3793          */
3794         defaultTextHeight : 75,
3795         /**
3796          * The maximum width in pixels of the message box (defaults to 600)
3797          * @type Number
3798          */
3799         maxWidth : 600,
3800         /**
3801          * The minimum width in pixels of the message box (defaults to 100)
3802          * @type Number
3803          */
3804         minWidth : 100,
3805         /**
3806          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3807          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3808          * @type Number
3809          */
3810         minProgressWidth : 250,
3811         /**
3812          * An object containing the default button text strings that can be overriden for localized language support.
3813          * Supported properties are: ok, cancel, yes and no.
3814          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3815          * @type Object
3816          */
3817         buttonText : {
3818             ok : "OK",
3819             cancel : "Cancel",
3820             yes : "Yes",
3821             no : "No"
3822         }
3823     };
3824 }();
3825
3826 /**
3827  * Shorthand for {@link Roo.MessageBox}
3828  */
3829 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3830 Roo.Msg = Roo.Msg || Roo.MessageBox;
3831 /*
3832  * - LGPL
3833  *
3834  * navbar
3835  * 
3836  */
3837
3838 /**
3839  * @class Roo.bootstrap.Navbar
3840  * @extends Roo.bootstrap.Component
3841  * Bootstrap Navbar class
3842
3843  * @constructor
3844  * Create a new Navbar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.Navbar = function(config){
3850     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3851     this.addEvents({
3852         // raw events
3853         /**
3854          * @event beforetoggle
3855          * Fire before toggle the menu
3856          * @param {Roo.EventObject} e
3857          */
3858         "beforetoggle" : true
3859     });
3860 };
3861
3862 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3863     
3864     
3865    
3866     // private
3867     navItems : false,
3868     loadMask : false,
3869     
3870     
3871     getAutoCreate : function(){
3872         
3873         
3874         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3875         
3876     },
3877     
3878     initEvents :function ()
3879     {
3880         //Roo.log(this.el.select('.navbar-toggle',true));
3881         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3882         
3883         var mark = {
3884             tag: "div",
3885             cls:"x-dlg-mask"
3886         };
3887         
3888         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3889         
3890         var size = this.el.getSize();
3891         this.maskEl.setSize(size.width, size.height);
3892         this.maskEl.enableDisplayMode("block");
3893         this.maskEl.hide();
3894         
3895         if(this.loadMask){
3896             this.maskEl.show();
3897         }
3898     },
3899     
3900     
3901     getChildContainer : function()
3902     {
3903         if (this.el && this.el.select('.collapse').getCount()) {
3904             return this.el.select('.collapse',true).first();
3905         }
3906         
3907         return this.el;
3908     },
3909     
3910     mask : function()
3911     {
3912         this.maskEl.show();
3913     },
3914     
3915     unmask : function()
3916     {
3917         this.maskEl.hide();
3918     },
3919     onToggle : function()
3920     {
3921         
3922         if(this.fireEvent('beforetoggle', this) === false){
3923             return;
3924         }
3925         var ce = this.el.select('.navbar-collapse',true).first();
3926       
3927         if (!ce.hasClass('show')) {
3928            this.expand();
3929         } else {
3930             this.collapse();
3931         }
3932         
3933         
3934     
3935     },
3936     /**
3937      * Expand the navbar pulldown 
3938      */
3939     expand : function ()
3940     {
3941        
3942         var ce = this.el.select('.navbar-collapse',true).first();
3943         if (ce.hasClass('collapsing')) {
3944             return;
3945         }
3946         ce.dom.style.height = '';
3947                // show it...
3948         ce.addClass('in'); // old...
3949         ce.removeClass('collapse');
3950         ce.addClass('show');
3951         var h = ce.getHeight();
3952         Roo.log(h);
3953         ce.removeClass('show');
3954         // at this point we should be able to see it..
3955         ce.addClass('collapsing');
3956         
3957         ce.setHeight(0); // resize it ...
3958         ce.on('transitionend', function() {
3959             //Roo.log('done transition');
3960             ce.removeClass('collapsing');
3961             ce.addClass('show');
3962             ce.removeClass('collapse');
3963
3964             ce.dom.style.height = '';
3965         }, this, { single: true} );
3966         ce.setHeight(h);
3967         ce.dom.scrollTop = 0;
3968     },
3969     /**
3970      * Collapse the navbar pulldown 
3971      */
3972     collapse : function()
3973     {
3974          var ce = this.el.select('.navbar-collapse',true).first();
3975        
3976         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3977             // it's collapsed or collapsing..
3978             return;
3979         }
3980         ce.removeClass('in'); // old...
3981         ce.setHeight(ce.getHeight());
3982         ce.removeClass('show');
3983         ce.addClass('collapsing');
3984         
3985         ce.on('transitionend', function() {
3986             ce.dom.style.height = '';
3987             ce.removeClass('collapsing');
3988             ce.addClass('collapse');
3989         }, this, { single: true} );
3990         ce.setHeight(0);
3991     }
3992     
3993     
3994     
3995 });
3996
3997
3998
3999  
4000
4001  /*
4002  * - LGPL
4003  *
4004  * navbar
4005  * 
4006  */
4007
4008 /**
4009  * @class Roo.bootstrap.NavSimplebar
4010  * @extends Roo.bootstrap.Navbar
4011  * Bootstrap Sidebar class
4012  *
4013  * @cfg {Boolean} inverse is inverted color
4014  * 
4015  * @cfg {String} type (nav | pills | tabs)
4016  * @cfg {Boolean} arrangement stacked | justified
4017  * @cfg {String} align (left | right) alignment
4018  * 
4019  * @cfg {Boolean} main (true|false) main nav bar? default false
4020  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4021  * 
4022  * @cfg {String} tag (header|footer|nav|div) default is nav 
4023
4024  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4025  * 
4026  * 
4027  * @constructor
4028  * Create a new Sidebar
4029  * @param {Object} config The config object
4030  */
4031
4032
4033 Roo.bootstrap.NavSimplebar = function(config){
4034     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4035 };
4036
4037 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4038     
4039     inverse: false,
4040     
4041     type: false,
4042     arrangement: '',
4043     align : false,
4044     
4045     weight : 'light',
4046     
4047     main : false,
4048     
4049     
4050     tag : false,
4051     
4052     
4053     getAutoCreate : function(){
4054         
4055         
4056         var cfg = {
4057             tag : this.tag || 'div',
4058             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4059         };
4060         if (['light','white'].indexOf(this.weight) > -1) {
4061             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4062         }
4063         cfg.cls += ' bg-' + this.weight;
4064         
4065         if (this.inverse) {
4066             cfg.cls += ' navbar-inverse';
4067             
4068         }
4069         
4070         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4071         
4072         //if (Roo.bootstrap.version == 4) {
4073         //    return cfg;
4074         //}
4075         
4076         cfg.cn = [
4077             {
4078                 cls: 'nav',
4079                 tag : 'ul'
4080             }
4081         ];
4082         
4083          
4084         this.type = this.type || 'nav';
4085         if (['tabs','pills'].indexOf(this.type) != -1) {
4086             cfg.cn[0].cls += ' nav-' + this.type
4087         
4088         
4089         } else {
4090             if (this.type!=='nav') {
4091                 Roo.log('nav type must be nav/tabs/pills')
4092             }
4093             cfg.cn[0].cls += ' navbar-nav'
4094         }
4095         
4096         
4097         
4098         
4099         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4100             cfg.cn[0].cls += ' nav-' + this.arrangement;
4101         }
4102         
4103         
4104         if (this.align === 'right') {
4105             cfg.cn[0].cls += ' navbar-right';
4106         }
4107         
4108         
4109         
4110         
4111         return cfg;
4112     
4113         
4114     }
4115     
4116     
4117     
4118 });
4119
4120
4121
4122  
4123
4124  
4125        /*
4126  * - LGPL
4127  *
4128  * navbar
4129  * navbar-fixed-top
4130  * navbar-expand-md  fixed-top 
4131  */
4132
4133 /**
4134  * @class Roo.bootstrap.NavHeaderbar
4135  * @extends Roo.bootstrap.NavSimplebar
4136  * Bootstrap Sidebar class
4137  *
4138  * @cfg {String} brand what is brand
4139  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4140  * @cfg {String} brand_href href of the brand
4141  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4142  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4143  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4144  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4145  * 
4146  * @constructor
4147  * Create a new Sidebar
4148  * @param {Object} config The config object
4149  */
4150
4151
4152 Roo.bootstrap.NavHeaderbar = function(config){
4153     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4154       
4155 };
4156
4157 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4158     
4159     position: '',
4160     brand: '',
4161     brand_href: false,
4162     srButton : true,
4163     autohide : false,
4164     desktopCenter : false,
4165    
4166     
4167     getAutoCreate : function(){
4168         
4169         var   cfg = {
4170             tag: this.nav || 'nav',
4171             cls: 'navbar navbar-expand-md',
4172             role: 'navigation',
4173             cn: []
4174         };
4175         
4176         var cn = cfg.cn;
4177         if (this.desktopCenter) {
4178             cn.push({cls : 'container', cn : []});
4179             cn = cn[0].cn;
4180         }
4181         
4182         if(this.srButton){
4183             var btn = {
4184                 tag: 'button',
4185                 type: 'button',
4186                 cls: 'navbar-toggle navbar-toggler',
4187                 'data-toggle': 'collapse',
4188                 cn: [
4189                     {
4190                         tag: 'span',
4191                         cls: 'sr-only',
4192                         html: 'Toggle navigation'
4193                     },
4194                     {
4195                         tag: 'span',
4196                         cls: 'icon-bar navbar-toggler-icon'
4197                     },
4198                     {
4199                         tag: 'span',
4200                         cls: 'icon-bar'
4201                     },
4202                     {
4203                         tag: 'span',
4204                         cls: 'icon-bar'
4205                     }
4206                 ]
4207             };
4208             
4209             cn.push( Roo.bootstrap.version == 4 ? btn : {
4210                 tag: 'div',
4211                 cls: 'navbar-header',
4212                 cn: [
4213                     btn
4214                 ]
4215             });
4216         }
4217         
4218         cn.push({
4219             tag: 'div',
4220             cls: 'collapse navbar-collapse',
4221             cn : []
4222         });
4223         
4224         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4225         
4226         if (['light','white'].indexOf(this.weight) > -1) {
4227             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4228         }
4229         cfg.cls += ' bg-' + this.weight;
4230         
4231         
4232         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4233             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4234             
4235             // tag can override this..
4236             
4237             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4238         }
4239         
4240         if (this.brand !== '') {
4241             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4242             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4243                 tag: 'a',
4244                 href: this.brand_href ? this.brand_href : '#',
4245                 cls: 'navbar-brand',
4246                 cn: [
4247                 this.brand
4248                 ]
4249             });
4250         }
4251         
4252         if(this.main){
4253             cfg.cls += ' main-nav';
4254         }
4255         
4256         
4257         return cfg;
4258
4259         
4260     },
4261     getHeaderChildContainer : function()
4262     {
4263         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4264             return this.el.select('.navbar-header',true).first();
4265         }
4266         
4267         return this.getChildContainer();
4268     },
4269     
4270     
4271     initEvents : function()
4272     {
4273         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4274         
4275         if (this.autohide) {
4276             
4277             var prevScroll = 0;
4278             var ft = this.el;
4279             
4280             Roo.get(document).on('scroll',function(e) {
4281                 var ns = Roo.get(document).getScroll().top;
4282                 var os = prevScroll;
4283                 prevScroll = ns;
4284                 
4285                 if(ns > os){
4286                     ft.removeClass('slideDown');
4287                     ft.addClass('slideUp');
4288                     return;
4289                 }
4290                 ft.removeClass('slideUp');
4291                 ft.addClass('slideDown');
4292                  
4293               
4294           },this);
4295         }
4296     }    
4297     
4298 });
4299
4300
4301
4302  
4303
4304  /*
4305  * - LGPL
4306  *
4307  * navbar
4308  * 
4309  */
4310
4311 /**
4312  * @class Roo.bootstrap.NavSidebar
4313  * @extends Roo.bootstrap.Navbar
4314  * Bootstrap Sidebar class
4315  * 
4316  * @constructor
4317  * Create a new Sidebar
4318  * @param {Object} config The config object
4319  */
4320
4321
4322 Roo.bootstrap.NavSidebar = function(config){
4323     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4324 };
4325
4326 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4327     
4328     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4329     
4330     getAutoCreate : function(){
4331         
4332         
4333         return  {
4334             tag: 'div',
4335             cls: 'sidebar sidebar-nav'
4336         };
4337     
4338         
4339     }
4340     
4341     
4342     
4343 });
4344
4345
4346
4347  
4348
4349  /*
4350  * - LGPL
4351  *
4352  * nav group
4353  * 
4354  */
4355
4356 /**
4357  * @class Roo.bootstrap.NavGroup
4358  * @extends Roo.bootstrap.Component
4359  * Bootstrap NavGroup class
4360  * @cfg {String} align (left|right)
4361  * @cfg {Boolean} inverse
4362  * @cfg {String} type (nav|pills|tab) default nav
4363  * @cfg {String} navId - reference Id for navbar.
4364
4365  * 
4366  * @constructor
4367  * Create a new nav group
4368  * @param {Object} config The config object
4369  */
4370
4371 Roo.bootstrap.NavGroup = function(config){
4372     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4373     this.navItems = [];
4374    
4375     Roo.bootstrap.NavGroup.register(this);
4376      this.addEvents({
4377         /**
4378              * @event changed
4379              * Fires when the active item changes
4380              * @param {Roo.bootstrap.NavGroup} this
4381              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4382              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4383          */
4384         'changed': true
4385      });
4386     
4387 };
4388
4389 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4390     
4391     align: '',
4392     inverse: false,
4393     form: false,
4394     type: 'nav',
4395     navId : '',
4396     // private
4397     
4398     navItems : false, 
4399     
4400     getAutoCreate : function()
4401     {
4402         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4403         
4404         cfg = {
4405             tag : 'ul',
4406             cls: 'nav' 
4407         };
4408         if (Roo.bootstrap.version == 4) {
4409             if (['tabs','pills'].indexOf(this.type) != -1) {
4410                 cfg.cls += ' nav-' + this.type; 
4411             } else {
4412                 cfg.cls += ' navbar-nav';
4413             }
4414         } else {
4415             if (['tabs','pills'].indexOf(this.type) != -1) {
4416                 cfg.cls += ' nav-' + this.type
4417             } else {
4418                 if (this.type !== 'nav') {
4419                     Roo.log('nav type must be nav/tabs/pills')
4420                 }
4421                 cfg.cls += ' navbar-nav'
4422             }
4423         }
4424         
4425         if (this.parent() && this.parent().sidebar) {
4426             cfg = {
4427                 tag: 'ul',
4428                 cls: 'dashboard-menu sidebar-menu'
4429             };
4430             
4431             return cfg;
4432         }
4433         
4434         if (this.form === true) {
4435             cfg = {
4436                 tag: 'form',
4437                 cls: 'navbar-form form-inline'
4438             };
4439             
4440             if (this.align === 'right') {
4441                 cfg.cls += ' navbar-right ml-md-auto';
4442             } else {
4443                 cfg.cls += ' navbar-left';
4444             }
4445         }
4446         
4447         if (this.align === 'right') {
4448             cfg.cls += ' navbar-right ml-md-auto';
4449         } else {
4450             cfg.cls += ' mr-auto';
4451         }
4452         
4453         if (this.inverse) {
4454             cfg.cls += ' navbar-inverse';
4455             
4456         }
4457         
4458         
4459         return cfg;
4460     },
4461     /**
4462     * sets the active Navigation item
4463     * @param {Roo.bootstrap.NavItem} the new current navitem
4464     */
4465     setActiveItem : function(item)
4466     {
4467         var prev = false;
4468         Roo.each(this.navItems, function(v){
4469             if (v == item) {
4470                 return ;
4471             }
4472             if (v.isActive()) {
4473                 v.setActive(false, true);
4474                 prev = v;
4475                 
4476             }
4477             
4478         });
4479
4480         item.setActive(true, true);
4481         this.fireEvent('changed', this, item, prev);
4482         
4483         
4484     },
4485     /**
4486     * gets the active Navigation item
4487     * @return {Roo.bootstrap.NavItem} the current navitem
4488     */
4489     getActive : function()
4490     {
4491         
4492         var prev = false;
4493         Roo.each(this.navItems, function(v){
4494             
4495             if (v.isActive()) {
4496                 prev = v;
4497                 
4498             }
4499             
4500         });
4501         return prev;
4502     },
4503     
4504     indexOfNav : function()
4505     {
4506         
4507         var prev = false;
4508         Roo.each(this.navItems, function(v,i){
4509             
4510             if (v.isActive()) {
4511                 prev = i;
4512                 
4513             }
4514             
4515         });
4516         return prev;
4517     },
4518     /**
4519     * adds a Navigation item
4520     * @param {Roo.bootstrap.NavItem} the navitem to add
4521     */
4522     addItem : function(cfg)
4523     {
4524         if (this.form && Roo.bootstrap.version == 4) {
4525             cfg.tag = 'div';
4526         }
4527         var cn = new Roo.bootstrap.NavItem(cfg);
4528         this.register(cn);
4529         cn.parentId = this.id;
4530         cn.onRender(this.el, null);
4531         return cn;
4532     },
4533     /**
4534     * register a Navigation item
4535     * @param {Roo.bootstrap.NavItem} the navitem to add
4536     */
4537     register : function(item)
4538     {
4539         this.navItems.push( item);
4540         item.navId = this.navId;
4541     
4542     },
4543     
4544     /**
4545     * clear all the Navigation item
4546     */
4547    
4548     clearAll : function()
4549     {
4550         this.navItems = [];
4551         this.el.dom.innerHTML = '';
4552     },
4553     
4554     getNavItem: function(tabId)
4555     {
4556         var ret = false;
4557         Roo.each(this.navItems, function(e) {
4558             if (e.tabId == tabId) {
4559                ret =  e;
4560                return false;
4561             }
4562             return true;
4563             
4564         });
4565         return ret;
4566     },
4567     
4568     setActiveNext : function()
4569     {
4570         var i = this.indexOfNav(this.getActive());
4571         if (i > this.navItems.length) {
4572             return;
4573         }
4574         this.setActiveItem(this.navItems[i+1]);
4575     },
4576     setActivePrev : function()
4577     {
4578         var i = this.indexOfNav(this.getActive());
4579         if (i  < 1) {
4580             return;
4581         }
4582         this.setActiveItem(this.navItems[i-1]);
4583     },
4584     clearWasActive : function(except) {
4585         Roo.each(this.navItems, function(e) {
4586             if (e.tabId != except.tabId && e.was_active) {
4587                e.was_active = false;
4588                return false;
4589             }
4590             return true;
4591             
4592         });
4593     },
4594     getWasActive : function ()
4595     {
4596         var r = false;
4597         Roo.each(this.navItems, function(e) {
4598             if (e.was_active) {
4599                r = e;
4600                return false;
4601             }
4602             return true;
4603             
4604         });
4605         return r;
4606     }
4607     
4608     
4609 });
4610
4611  
4612 Roo.apply(Roo.bootstrap.NavGroup, {
4613     
4614     groups: {},
4615      /**
4616     * register a Navigation Group
4617     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4618     */
4619     register : function(navgrp)
4620     {
4621         this.groups[navgrp.navId] = navgrp;
4622         
4623     },
4624     /**
4625     * fetch a Navigation Group based on the navigation ID
4626     * @param {string} the navgroup to add
4627     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4628     */
4629     get: function(navId) {
4630         if (typeof(this.groups[navId]) == 'undefined') {
4631             return false;
4632             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4633         }
4634         return this.groups[navId] ;
4635     }
4636     
4637     
4638     
4639 });
4640
4641  /*
4642  * - LGPL
4643  *
4644  * row
4645  * 
4646  */
4647
4648 /**
4649  * @class Roo.bootstrap.NavItem
4650  * @extends Roo.bootstrap.Component
4651  * Bootstrap Navbar.NavItem class
4652  * @cfg {String} href  link to
4653  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4654
4655  * @cfg {String} html content of button
4656  * @cfg {String} badge text inside badge
4657  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4658  * @cfg {String} glyphicon DEPRICATED - use fa
4659  * @cfg {String} icon DEPRICATED - use fa
4660  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4661  * @cfg {Boolean} active Is item active
4662  * @cfg {Boolean} disabled Is item disabled
4663  
4664  * @cfg {Boolean} preventDefault (true | false) default false
4665  * @cfg {String} tabId the tab that this item activates.
4666  * @cfg {String} tagtype (a|span) render as a href or span?
4667  * @cfg {Boolean} animateRef (true|false) link to element default false  
4668   
4669  * @constructor
4670  * Create a new Navbar Item
4671  * @param {Object} config The config object
4672  */
4673 Roo.bootstrap.NavItem = function(config){
4674     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4675     this.addEvents({
4676         // raw events
4677         /**
4678          * @event click
4679          * The raw click event for the entire grid.
4680          * @param {Roo.EventObject} e
4681          */
4682         "click" : true,
4683          /**
4684             * @event changed
4685             * Fires when the active item active state changes
4686             * @param {Roo.bootstrap.NavItem} this
4687             * @param {boolean} state the new state
4688              
4689          */
4690         'changed': true,
4691         /**
4692             * @event scrollto
4693             * Fires when scroll to element
4694             * @param {Roo.bootstrap.NavItem} this
4695             * @param {Object} options
4696             * @param {Roo.EventObject} e
4697              
4698          */
4699         'scrollto': true
4700     });
4701    
4702 };
4703
4704 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4705     
4706     href: false,
4707     html: '',
4708     badge: '',
4709     icon: false,
4710     fa : false,
4711     glyphicon: false,
4712     active: false,
4713     preventDefault : false,
4714     tabId : false,
4715     tagtype : 'a',
4716     tag: 'li',
4717     disabled : false,
4718     animateRef : false,
4719     was_active : false,
4720     button_weight : '',
4721     button_outline : false,
4722     
4723     navLink: false,
4724     
4725     getAutoCreate : function(){
4726          
4727         var cfg = {
4728             tag: this.tag,
4729             cls: 'nav-item'
4730         };
4731         
4732         if (this.active) {
4733             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4734         }
4735         if (this.disabled) {
4736             cfg.cls += ' disabled';
4737         }
4738         
4739         // BS4 only?
4740         if (this.button_weight.length) {
4741             cfg.tag = this.href ? 'a' : 'button';
4742             cfg.html = this.html || '';
4743             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4744             if (this.href) {
4745                 cfg.href = this.href;
4746             }
4747             if (this.fa) {
4748                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4749             }
4750             
4751             // menu .. should add dropdown-menu class - so no need for carat..
4752             
4753             if (this.badge !== '') {
4754                  
4755                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4756             }
4757             return cfg;
4758         }
4759         
4760         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4761             cfg.cn = [
4762                 {
4763                     tag: this.tagtype,
4764                     href : this.href || "#",
4765                     html: this.html || ''
4766                 }
4767             ];
4768             if (this.tagtype == 'a') {
4769                 cfg.cn[0].cls = 'nav-link';
4770             }
4771             if (this.icon) {
4772                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4773             }
4774             if (this.fa) {
4775                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4776             }
4777             if(this.glyphicon) {
4778                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4779             }
4780             
4781             if (this.menu) {
4782                 
4783                 cfg.cn[0].html += " <span class='caret'></span>";
4784              
4785             }
4786             
4787             if (this.badge !== '') {
4788                  
4789                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4790             }
4791         }
4792         
4793         
4794         
4795         return cfg;
4796     },
4797     onRender : function(ct, position)
4798     {
4799        // Roo.log("Call onRender: " + this.xtype);
4800         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4801             this.tag = 'div';
4802         }
4803         
4804         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4805         this.navLink = this.el.select('.nav-link',true).first();
4806         return ret;
4807     },
4808       
4809     
4810     initEvents: function() 
4811     {
4812         if (typeof (this.menu) != 'undefined') {
4813             this.menu.parentType = this.xtype;
4814             this.menu.triggerEl = this.el;
4815             this.menu = this.addxtype(Roo.apply({}, this.menu));
4816         }
4817         
4818         this.el.select('a',true).on('click', this.onClick, this);
4819         
4820         if(this.tagtype == 'span'){
4821             this.el.select('span',true).on('click', this.onClick, this);
4822         }
4823        
4824         // at this point parent should be available..
4825         this.parent().register(this);
4826     },
4827     
4828     onClick : function(e)
4829     {
4830         if (e.getTarget('.dropdown-menu-item')) {
4831             // did you click on a menu itemm.... - then don't trigger onclick..
4832             return;
4833         }
4834         
4835         if(
4836                 this.preventDefault || 
4837                 this.href == '#' 
4838         ){
4839             Roo.log("NavItem - prevent Default?");
4840             e.preventDefault();
4841         }
4842         
4843         if (this.disabled) {
4844             return;
4845         }
4846         
4847         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4848         if (tg && tg.transition) {
4849             Roo.log("waiting for the transitionend");
4850             return;
4851         }
4852         
4853         
4854         
4855         //Roo.log("fire event clicked");
4856         if(this.fireEvent('click', this, e) === false){
4857             return;
4858         };
4859         
4860         if(this.tagtype == 'span'){
4861             return;
4862         }
4863         
4864         //Roo.log(this.href);
4865         var ael = this.el.select('a',true).first();
4866         //Roo.log(ael);
4867         
4868         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4869             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4870             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4871                 return; // ignore... - it's a 'hash' to another page.
4872             }
4873             Roo.log("NavItem - prevent Default?");
4874             e.preventDefault();
4875             this.scrollToElement(e);
4876         }
4877         
4878         
4879         var p =  this.parent();
4880    
4881         if (['tabs','pills'].indexOf(p.type)!==-1) {
4882             if (typeof(p.setActiveItem) !== 'undefined') {
4883                 p.setActiveItem(this);
4884             }
4885         }
4886         
4887         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4888         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4889             // remove the collapsed menu expand...
4890             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4891         }
4892     },
4893     
4894     isActive: function () {
4895         return this.active
4896     },
4897     setActive : function(state, fire, is_was_active)
4898     {
4899         if (this.active && !state && this.navId) {
4900             this.was_active = true;
4901             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4902             if (nv) {
4903                 nv.clearWasActive(this);
4904             }
4905             
4906         }
4907         this.active = state;
4908         
4909         if (!state ) {
4910             this.el.removeClass('active');
4911             this.navLink ? this.navLink.removeClass('active') : false;
4912         } else if (!this.el.hasClass('active')) {
4913             
4914             this.el.addClass('active');
4915             if (Roo.bootstrap.version == 4 && this.navLink ) {
4916                 this.navLink.addClass('active');
4917             }
4918             
4919         }
4920         if (fire) {
4921             this.fireEvent('changed', this, state);
4922         }
4923         
4924         // show a panel if it's registered and related..
4925         
4926         if (!this.navId || !this.tabId || !state || is_was_active) {
4927             return;
4928         }
4929         
4930         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4931         if (!tg) {
4932             return;
4933         }
4934         var pan = tg.getPanelByName(this.tabId);
4935         if (!pan) {
4936             return;
4937         }
4938         // if we can not flip to new panel - go back to old nav highlight..
4939         if (false == tg.showPanel(pan)) {
4940             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4941             if (nv) {
4942                 var onav = nv.getWasActive();
4943                 if (onav) {
4944                     onav.setActive(true, false, true);
4945                 }
4946             }
4947             
4948         }
4949         
4950         
4951         
4952     },
4953      // this should not be here...
4954     setDisabled : function(state)
4955     {
4956         this.disabled = state;
4957         if (!state ) {
4958             this.el.removeClass('disabled');
4959         } else if (!this.el.hasClass('disabled')) {
4960             this.el.addClass('disabled');
4961         }
4962         
4963     },
4964     
4965     /**
4966      * Fetch the element to display the tooltip on.
4967      * @return {Roo.Element} defaults to this.el
4968      */
4969     tooltipEl : function()
4970     {
4971         return this.el.select('' + this.tagtype + '', true).first();
4972     },
4973     
4974     scrollToElement : function(e)
4975     {
4976         var c = document.body;
4977         
4978         /*
4979          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4980          */
4981         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4982             c = document.documentElement;
4983         }
4984         
4985         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4986         
4987         if(!target){
4988             return;
4989         }
4990
4991         var o = target.calcOffsetsTo(c);
4992         
4993         var options = {
4994             target : target,
4995             value : o[1]
4996         };
4997         
4998         this.fireEvent('scrollto', this, options, e);
4999         
5000         Roo.get(c).scrollTo('top', options.value, true);
5001         
5002         return;
5003     }
5004 });
5005  
5006
5007  /*
5008  * - LGPL
5009  *
5010  * sidebar item
5011  *
5012  *  li
5013  *    <span> icon </span>
5014  *    <span> text </span>
5015  *    <span>badge </span>
5016  */
5017
5018 /**
5019  * @class Roo.bootstrap.NavSidebarItem
5020  * @extends Roo.bootstrap.NavItem
5021  * Bootstrap Navbar.NavSidebarItem class
5022  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5023  * {Boolean} open is the menu open
5024  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5025  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5026  * {String} buttonSize (sm|md|lg)the extra classes for the button
5027  * {Boolean} showArrow show arrow next to the text (default true)
5028  * @constructor
5029  * Create a new Navbar Button
5030  * @param {Object} config The config object
5031  */
5032 Roo.bootstrap.NavSidebarItem = function(config){
5033     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5034     this.addEvents({
5035         // raw events
5036         /**
5037          * @event click
5038          * The raw click event for the entire grid.
5039          * @param {Roo.EventObject} e
5040          */
5041         "click" : true,
5042          /**
5043             * @event changed
5044             * Fires when the active item active state changes
5045             * @param {Roo.bootstrap.NavSidebarItem} this
5046             * @param {boolean} state the new state
5047              
5048          */
5049         'changed': true
5050     });
5051    
5052 };
5053
5054 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5055     
5056     badgeWeight : 'default',
5057     
5058     open: false,
5059     
5060     buttonView : false,
5061     
5062     buttonWeight : 'default',
5063     
5064     buttonSize : 'md',
5065     
5066     showArrow : true,
5067     
5068     getAutoCreate : function(){
5069         
5070         
5071         var a = {
5072                 tag: 'a',
5073                 href : this.href || '#',
5074                 cls: '',
5075                 html : '',
5076                 cn : []
5077         };
5078         
5079         if(this.buttonView){
5080             a = {
5081                 tag: 'button',
5082                 href : this.href || '#',
5083                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5084                 html : this.html,
5085                 cn : []
5086             };
5087         }
5088         
5089         var cfg = {
5090             tag: 'li',
5091             cls: '',
5092             cn: [ a ]
5093         };
5094         
5095         if (this.active) {
5096             cfg.cls += ' active';
5097         }
5098         
5099         if (this.disabled) {
5100             cfg.cls += ' disabled';
5101         }
5102         if (this.open) {
5103             cfg.cls += ' open x-open';
5104         }
5105         // left icon..
5106         if (this.glyphicon || this.icon) {
5107             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5108             a.cn.push({ tag : 'i', cls : c }) ;
5109         }
5110         
5111         if(!this.buttonView){
5112             var span = {
5113                 tag: 'span',
5114                 html : this.html || ''
5115             };
5116
5117             a.cn.push(span);
5118             
5119         }
5120         
5121         if (this.badge !== '') {
5122             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5123         }
5124         
5125         if (this.menu) {
5126             
5127             if(this.showArrow){
5128                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5129             }
5130             
5131             a.cls += ' dropdown-toggle treeview' ;
5132         }
5133         
5134         return cfg;
5135     },
5136     
5137     initEvents : function()
5138     { 
5139         if (typeof (this.menu) != 'undefined') {
5140             this.menu.parentType = this.xtype;
5141             this.menu.triggerEl = this.el;
5142             this.menu = this.addxtype(Roo.apply({}, this.menu));
5143         }
5144         
5145         this.el.on('click', this.onClick, this);
5146         
5147         if(this.badge !== ''){
5148             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5149         }
5150         
5151     },
5152     
5153     onClick : function(e)
5154     {
5155         if(this.disabled){
5156             e.preventDefault();
5157             return;
5158         }
5159         
5160         if(this.preventDefault){
5161             e.preventDefault();
5162         }
5163         
5164         this.fireEvent('click', this, e);
5165     },
5166     
5167     disable : function()
5168     {
5169         this.setDisabled(true);
5170     },
5171     
5172     enable : function()
5173     {
5174         this.setDisabled(false);
5175     },
5176     
5177     setDisabled : function(state)
5178     {
5179         if(this.disabled == state){
5180             return;
5181         }
5182         
5183         this.disabled = state;
5184         
5185         if (state) {
5186             this.el.addClass('disabled');
5187             return;
5188         }
5189         
5190         this.el.removeClass('disabled');
5191         
5192         return;
5193     },
5194     
5195     setActive : function(state)
5196     {
5197         if(this.active == state){
5198             return;
5199         }
5200         
5201         this.active = state;
5202         
5203         if (state) {
5204             this.el.addClass('active');
5205             return;
5206         }
5207         
5208         this.el.removeClass('active');
5209         
5210         return;
5211     },
5212     
5213     isActive: function () 
5214     {
5215         return this.active;
5216     },
5217     
5218     setBadge : function(str)
5219     {
5220         if(!this.badgeEl){
5221             return;
5222         }
5223         
5224         this.badgeEl.dom.innerHTML = str;
5225     }
5226     
5227    
5228      
5229  
5230 });
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * row
5237  * 
5238  */
5239
5240 /**
5241  * @class Roo.bootstrap.Row
5242  * @extends Roo.bootstrap.Component
5243  * Bootstrap Row class (contains columns...)
5244  * 
5245  * @constructor
5246  * Create a new Row
5247  * @param {Object} config The config object
5248  */
5249
5250 Roo.bootstrap.Row = function(config){
5251     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5252 };
5253
5254 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5255     
5256     getAutoCreate : function(){
5257        return {
5258             cls: 'row clearfix'
5259        };
5260     }
5261     
5262     
5263 });
5264
5265  
5266
5267  /*
5268  * - LGPL
5269  *
5270  * element
5271  * 
5272  */
5273
5274 /**
5275  * @class Roo.bootstrap.Element
5276  * @extends Roo.bootstrap.Component
5277  * Bootstrap Element class
5278  * @cfg {String} html contents of the element
5279  * @cfg {String} tag tag of the element
5280  * @cfg {String} cls class of the element
5281  * @cfg {Boolean} preventDefault (true|false) default false
5282  * @cfg {Boolean} clickable (true|false) default false
5283  * 
5284  * @constructor
5285  * Create a new Element
5286  * @param {Object} config The config object
5287  */
5288
5289 Roo.bootstrap.Element = function(config){
5290     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5291     
5292     this.addEvents({
5293         // raw events
5294         /**
5295          * @event click
5296          * When a element is chick
5297          * @param {Roo.bootstrap.Element} this
5298          * @param {Roo.EventObject} e
5299          */
5300         "click" : true
5301     });
5302 };
5303
5304 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5305     
5306     tag: 'div',
5307     cls: '',
5308     html: '',
5309     preventDefault: false, 
5310     clickable: false,
5311     
5312     getAutoCreate : function(){
5313         
5314         var cfg = {
5315             tag: this.tag,
5316             // cls: this.cls, double assign in parent class Component.js :: onRender
5317             html: this.html
5318         };
5319         
5320         return cfg;
5321     },
5322     
5323     initEvents: function() 
5324     {
5325         Roo.bootstrap.Element.superclass.initEvents.call(this);
5326         
5327         if(this.clickable){
5328             this.el.on('click', this.onClick, this);
5329         }
5330         
5331     },
5332     
5333     onClick : function(e)
5334     {
5335         if(this.preventDefault){
5336             e.preventDefault();
5337         }
5338         
5339         this.fireEvent('click', this, e);
5340     },
5341     
5342     getValue : function()
5343     {
5344         return this.el.dom.innerHTML;
5345     },
5346     
5347     setValue : function(value)
5348     {
5349         this.el.dom.innerHTML = value;
5350     }
5351    
5352 });
5353
5354  
5355
5356  /*
5357  * - LGPL
5358  *
5359  * pagination
5360  * 
5361  */
5362
5363 /**
5364  * @class Roo.bootstrap.Pagination
5365  * @extends Roo.bootstrap.Component
5366  * Bootstrap Pagination class
5367  * @cfg {String} size xs | sm | md | lg
5368  * @cfg {Boolean} inverse false | true
5369  * 
5370  * @constructor
5371  * Create a new Pagination
5372  * @param {Object} config The config object
5373  */
5374
5375 Roo.bootstrap.Pagination = function(config){
5376     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5377 };
5378
5379 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5380     
5381     cls: false,
5382     size: false,
5383     inverse: false,
5384     
5385     getAutoCreate : function(){
5386         var cfg = {
5387             tag: 'ul',
5388                 cls: 'pagination'
5389         };
5390         if (this.inverse) {
5391             cfg.cls += ' inverse';
5392         }
5393         if (this.html) {
5394             cfg.html=this.html;
5395         }
5396         if (this.cls) {
5397             cfg.cls += " " + this.cls;
5398         }
5399         return cfg;
5400     }
5401    
5402 });
5403
5404  
5405
5406  /*
5407  * - LGPL
5408  *
5409  * Pagination item
5410  * 
5411  */
5412
5413
5414 /**
5415  * @class Roo.bootstrap.PaginationItem
5416  * @extends Roo.bootstrap.Component
5417  * Bootstrap PaginationItem class
5418  * @cfg {String} html text
5419  * @cfg {String} href the link
5420  * @cfg {Boolean} preventDefault (true | false) default true
5421  * @cfg {Boolean} active (true | false) default false
5422  * @cfg {Boolean} disabled default false
5423  * 
5424  * 
5425  * @constructor
5426  * Create a new PaginationItem
5427  * @param {Object} config The config object
5428  */
5429
5430
5431 Roo.bootstrap.PaginationItem = function(config){
5432     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5433     this.addEvents({
5434         // raw events
5435         /**
5436          * @event click
5437          * The raw click event for the entire grid.
5438          * @param {Roo.EventObject} e
5439          */
5440         "click" : true
5441     });
5442 };
5443
5444 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5445     
5446     href : false,
5447     html : false,
5448     preventDefault: true,
5449     active : false,
5450     cls : false,
5451     disabled: false,
5452     
5453     getAutoCreate : function(){
5454         var cfg= {
5455             tag: 'li',
5456             cn: [
5457                 {
5458                     tag : 'a',
5459                     href : this.href ? this.href : '#',
5460                     html : this.html ? this.html : ''
5461                 }
5462             ]
5463         };
5464         
5465         if(this.cls){
5466             cfg.cls = this.cls;
5467         }
5468         
5469         if(this.disabled){
5470             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5471         }
5472         
5473         if(this.active){
5474             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5475         }
5476         
5477         return cfg;
5478     },
5479     
5480     initEvents: function() {
5481         
5482         this.el.on('click', this.onClick, this);
5483         
5484     },
5485     onClick : function(e)
5486     {
5487         Roo.log('PaginationItem on click ');
5488         if(this.preventDefault){
5489             e.preventDefault();
5490         }
5491         
5492         if(this.disabled){
5493             return;
5494         }
5495         
5496         this.fireEvent('click', this, e);
5497     }
5498    
5499 });
5500
5501  
5502
5503  /*
5504  * - LGPL
5505  *
5506  * slider
5507  * 
5508  */
5509
5510
5511 /**
5512  * @class Roo.bootstrap.Slider
5513  * @extends Roo.bootstrap.Component
5514  * Bootstrap Slider class
5515  *    
5516  * @constructor
5517  * Create a new Slider
5518  * @param {Object} config The config object
5519  */
5520
5521 Roo.bootstrap.Slider = function(config){
5522     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5523 };
5524
5525 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5526     
5527     getAutoCreate : function(){
5528         
5529         var cfg = {
5530             tag: 'div',
5531             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5532             cn: [
5533                 {
5534                     tag: 'a',
5535                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5536                 }
5537             ]
5538         };
5539         
5540         return cfg;
5541     }
5542    
5543 });
5544
5545  /*
5546  * Based on:
5547  * Ext JS Library 1.1.1
5548  * Copyright(c) 2006-2007, Ext JS, LLC.
5549  *
5550  * Originally Released Under LGPL - original licence link has changed is not relivant.
5551  *
5552  * Fork - LGPL
5553  * <script type="text/javascript">
5554  */
5555  
5556
5557 /**
5558  * @class Roo.grid.ColumnModel
5559  * @extends Roo.util.Observable
5560  * This is the default implementation of a ColumnModel used by the Grid. It defines
5561  * the columns in the grid.
5562  * <br>Usage:<br>
5563  <pre><code>
5564  var colModel = new Roo.grid.ColumnModel([
5565         {header: "Ticker", width: 60, sortable: true, locked: true},
5566         {header: "Company Name", width: 150, sortable: true},
5567         {header: "Market Cap.", width: 100, sortable: true},
5568         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5569         {header: "Employees", width: 100, sortable: true, resizable: false}
5570  ]);
5571  </code></pre>
5572  * <p>
5573  
5574  * The config options listed for this class are options which may appear in each
5575  * individual column definition.
5576  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5577  * @constructor
5578  * @param {Object} config An Array of column config objects. See this class's
5579  * config objects for details.
5580 */
5581 Roo.grid.ColumnModel = function(config){
5582         /**
5583      * The config passed into the constructor
5584      */
5585     this.config = config;
5586     this.lookup = {};
5587
5588     // if no id, create one
5589     // if the column does not have a dataIndex mapping,
5590     // map it to the order it is in the config
5591     for(var i = 0, len = config.length; i < len; i++){
5592         var c = config[i];
5593         if(typeof c.dataIndex == "undefined"){
5594             c.dataIndex = i;
5595         }
5596         if(typeof c.renderer == "string"){
5597             c.renderer = Roo.util.Format[c.renderer];
5598         }
5599         if(typeof c.id == "undefined"){
5600             c.id = Roo.id();
5601         }
5602         if(c.editor && c.editor.xtype){
5603             c.editor  = Roo.factory(c.editor, Roo.grid);
5604         }
5605         if(c.editor && c.editor.isFormField){
5606             c.editor = new Roo.grid.GridEditor(c.editor);
5607         }
5608         this.lookup[c.id] = c;
5609     }
5610
5611     /**
5612      * The width of columns which have no width specified (defaults to 100)
5613      * @type Number
5614      */
5615     this.defaultWidth = 100;
5616
5617     /**
5618      * Default sortable of columns which have no sortable specified (defaults to false)
5619      * @type Boolean
5620      */
5621     this.defaultSortable = false;
5622
5623     this.addEvents({
5624         /**
5625              * @event widthchange
5626              * Fires when the width of a column changes.
5627              * @param {ColumnModel} this
5628              * @param {Number} columnIndex The column index
5629              * @param {Number} newWidth The new width
5630              */
5631             "widthchange": true,
5632         /**
5633              * @event headerchange
5634              * Fires when the text of a header changes.
5635              * @param {ColumnModel} this
5636              * @param {Number} columnIndex The column index
5637              * @param {Number} newText The new header text
5638              */
5639             "headerchange": true,
5640         /**
5641              * @event hiddenchange
5642              * Fires when a column is hidden or "unhidden".
5643              * @param {ColumnModel} this
5644              * @param {Number} columnIndex The column index
5645              * @param {Boolean} hidden true if hidden, false otherwise
5646              */
5647             "hiddenchange": true,
5648             /**
5649          * @event columnmoved
5650          * Fires when a column is moved.
5651          * @param {ColumnModel} this
5652          * @param {Number} oldIndex
5653          * @param {Number} newIndex
5654          */
5655         "columnmoved" : true,
5656         /**
5657          * @event columlockchange
5658          * Fires when a column's locked state is changed
5659          * @param {ColumnModel} this
5660          * @param {Number} colIndex
5661          * @param {Boolean} locked true if locked
5662          */
5663         "columnlockchange" : true
5664     });
5665     Roo.grid.ColumnModel.superclass.constructor.call(this);
5666 };
5667 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5668     /**
5669      * @cfg {String} header The header text to display in the Grid view.
5670      */
5671     /**
5672      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5673      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5674      * specified, the column's index is used as an index into the Record's data Array.
5675      */
5676     /**
5677      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5678      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5679      */
5680     /**
5681      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5682      * Defaults to the value of the {@link #defaultSortable} property.
5683      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5684      */
5685     /**
5686      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5687      */
5688     /**
5689      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5690      */
5691     /**
5692      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5693      */
5694     /**
5695      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5696      */
5697     /**
5698      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5699      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5700      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5701      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5702      */
5703        /**
5704      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5705      */
5706     /**
5707      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5708      */
5709     /**
5710      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5711      */
5712     /**
5713      * @cfg {String} cursor (Optional)
5714      */
5715     /**
5716      * @cfg {String} tooltip (Optional)
5717      */
5718     /**
5719      * @cfg {Number} xs (Optional)
5720      */
5721     /**
5722      * @cfg {Number} sm (Optional)
5723      */
5724     /**
5725      * @cfg {Number} md (Optional)
5726      */
5727     /**
5728      * @cfg {Number} lg (Optional)
5729      */
5730     /**
5731      * Returns the id of the column at the specified index.
5732      * @param {Number} index The column index
5733      * @return {String} the id
5734      */
5735     getColumnId : function(index){
5736         return this.config[index].id;
5737     },
5738
5739     /**
5740      * Returns the column for a specified id.
5741      * @param {String} id The column id
5742      * @return {Object} the column
5743      */
5744     getColumnById : function(id){
5745         return this.lookup[id];
5746     },
5747
5748     
5749     /**
5750      * Returns the column for a specified dataIndex.
5751      * @param {String} dataIndex The column dataIndex
5752      * @return {Object|Boolean} the column or false if not found
5753      */
5754     getColumnByDataIndex: function(dataIndex){
5755         var index = this.findColumnIndex(dataIndex);
5756         return index > -1 ? this.config[index] : false;
5757     },
5758     
5759     /**
5760      * Returns the index for a specified column id.
5761      * @param {String} id The column id
5762      * @return {Number} the index, or -1 if not found
5763      */
5764     getIndexById : function(id){
5765         for(var i = 0, len = this.config.length; i < len; i++){
5766             if(this.config[i].id == id){
5767                 return i;
5768             }
5769         }
5770         return -1;
5771     },
5772     
5773     /**
5774      * Returns the index for a specified column dataIndex.
5775      * @param {String} dataIndex The column dataIndex
5776      * @return {Number} the index, or -1 if not found
5777      */
5778     
5779     findColumnIndex : function(dataIndex){
5780         for(var i = 0, len = this.config.length; i < len; i++){
5781             if(this.config[i].dataIndex == dataIndex){
5782                 return i;
5783             }
5784         }
5785         return -1;
5786     },
5787     
5788     
5789     moveColumn : function(oldIndex, newIndex){
5790         var c = this.config[oldIndex];
5791         this.config.splice(oldIndex, 1);
5792         this.config.splice(newIndex, 0, c);
5793         this.dataMap = null;
5794         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5795     },
5796
5797     isLocked : function(colIndex){
5798         return this.config[colIndex].locked === true;
5799     },
5800
5801     setLocked : function(colIndex, value, suppressEvent){
5802         if(this.isLocked(colIndex) == value){
5803             return;
5804         }
5805         this.config[colIndex].locked = value;
5806         if(!suppressEvent){
5807             this.fireEvent("columnlockchange", this, colIndex, value);
5808         }
5809     },
5810
5811     getTotalLockedWidth : function(){
5812         var totalWidth = 0;
5813         for(var i = 0; i < this.config.length; i++){
5814             if(this.isLocked(i) && !this.isHidden(i)){
5815                 this.totalWidth += this.getColumnWidth(i);
5816             }
5817         }
5818         return totalWidth;
5819     },
5820
5821     getLockedCount : function(){
5822         for(var i = 0, len = this.config.length; i < len; i++){
5823             if(!this.isLocked(i)){
5824                 return i;
5825             }
5826         }
5827         
5828         return this.config.length;
5829     },
5830
5831     /**
5832      * Returns the number of columns.
5833      * @return {Number}
5834      */
5835     getColumnCount : function(visibleOnly){
5836         if(visibleOnly === true){
5837             var c = 0;
5838             for(var i = 0, len = this.config.length; i < len; i++){
5839                 if(!this.isHidden(i)){
5840                     c++;
5841                 }
5842             }
5843             return c;
5844         }
5845         return this.config.length;
5846     },
5847
5848     /**
5849      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5850      * @param {Function} fn
5851      * @param {Object} scope (optional)
5852      * @return {Array} result
5853      */
5854     getColumnsBy : function(fn, scope){
5855         var r = [];
5856         for(var i = 0, len = this.config.length; i < len; i++){
5857             var c = this.config[i];
5858             if(fn.call(scope||this, c, i) === true){
5859                 r[r.length] = c;
5860             }
5861         }
5862         return r;
5863     },
5864
5865     /**
5866      * Returns true if the specified column is sortable.
5867      * @param {Number} col The column index
5868      * @return {Boolean}
5869      */
5870     isSortable : function(col){
5871         if(typeof this.config[col].sortable == "undefined"){
5872             return this.defaultSortable;
5873         }
5874         return this.config[col].sortable;
5875     },
5876
5877     /**
5878      * Returns the rendering (formatting) function defined for the column.
5879      * @param {Number} col The column index.
5880      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5881      */
5882     getRenderer : function(col){
5883         if(!this.config[col].renderer){
5884             return Roo.grid.ColumnModel.defaultRenderer;
5885         }
5886         return this.config[col].renderer;
5887     },
5888
5889     /**
5890      * Sets the rendering (formatting) function for a column.
5891      * @param {Number} col The column index
5892      * @param {Function} fn The function to use to process the cell's raw data
5893      * to return HTML markup for the grid view. The render function is called with
5894      * the following parameters:<ul>
5895      * <li>Data value.</li>
5896      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5897      * <li>css A CSS style string to apply to the table cell.</li>
5898      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5899      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5900      * <li>Row index</li>
5901      * <li>Column index</li>
5902      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5903      */
5904     setRenderer : function(col, fn){
5905         this.config[col].renderer = fn;
5906     },
5907
5908     /**
5909      * Returns the width for the specified column.
5910      * @param {Number} col The column index
5911      * @return {Number}
5912      */
5913     getColumnWidth : function(col){
5914         return this.config[col].width * 1 || this.defaultWidth;
5915     },
5916
5917     /**
5918      * Sets the width for a column.
5919      * @param {Number} col The column index
5920      * @param {Number} width The new width
5921      */
5922     setColumnWidth : function(col, width, suppressEvent){
5923         this.config[col].width = width;
5924         this.totalWidth = null;
5925         if(!suppressEvent){
5926              this.fireEvent("widthchange", this, col, width);
5927         }
5928     },
5929
5930     /**
5931      * Returns the total width of all columns.
5932      * @param {Boolean} includeHidden True to include hidden column widths
5933      * @return {Number}
5934      */
5935     getTotalWidth : function(includeHidden){
5936         if(!this.totalWidth){
5937             this.totalWidth = 0;
5938             for(var i = 0, len = this.config.length; i < len; i++){
5939                 if(includeHidden || !this.isHidden(i)){
5940                     this.totalWidth += this.getColumnWidth(i);
5941                 }
5942             }
5943         }
5944         return this.totalWidth;
5945     },
5946
5947     /**
5948      * Returns the header for the specified column.
5949      * @param {Number} col The column index
5950      * @return {String}
5951      */
5952     getColumnHeader : function(col){
5953         return this.config[col].header;
5954     },
5955
5956     /**
5957      * Sets the header for a column.
5958      * @param {Number} col The column index
5959      * @param {String} header The new header
5960      */
5961     setColumnHeader : function(col, header){
5962         this.config[col].header = header;
5963         this.fireEvent("headerchange", this, col, header);
5964     },
5965
5966     /**
5967      * Returns the tooltip for the specified column.
5968      * @param {Number} col The column index
5969      * @return {String}
5970      */
5971     getColumnTooltip : function(col){
5972             return this.config[col].tooltip;
5973     },
5974     /**
5975      * Sets the tooltip for a column.
5976      * @param {Number} col The column index
5977      * @param {String} tooltip The new tooltip
5978      */
5979     setColumnTooltip : function(col, tooltip){
5980             this.config[col].tooltip = tooltip;
5981     },
5982
5983     /**
5984      * Returns the dataIndex for the specified column.
5985      * @param {Number} col The column index
5986      * @return {Number}
5987      */
5988     getDataIndex : function(col){
5989         return this.config[col].dataIndex;
5990     },
5991
5992     /**
5993      * Sets the dataIndex for a column.
5994      * @param {Number} col The column index
5995      * @param {Number} dataIndex The new dataIndex
5996      */
5997     setDataIndex : function(col, dataIndex){
5998         this.config[col].dataIndex = dataIndex;
5999     },
6000
6001     
6002     
6003     /**
6004      * Returns true if the cell is editable.
6005      * @param {Number} colIndex The column index
6006      * @param {Number} rowIndex The row index - this is nto actually used..?
6007      * @return {Boolean}
6008      */
6009     isCellEditable : function(colIndex, rowIndex){
6010         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6011     },
6012
6013     /**
6014      * Returns the editor defined for the cell/column.
6015      * return false or null to disable editing.
6016      * @param {Number} colIndex The column index
6017      * @param {Number} rowIndex The row index
6018      * @return {Object}
6019      */
6020     getCellEditor : function(colIndex, rowIndex){
6021         return this.config[colIndex].editor;
6022     },
6023
6024     /**
6025      * Sets if a column is editable.
6026      * @param {Number} col The column index
6027      * @param {Boolean} editable True if the column is editable
6028      */
6029     setEditable : function(col, editable){
6030         this.config[col].editable = editable;
6031     },
6032
6033
6034     /**
6035      * Returns true if the column is hidden.
6036      * @param {Number} colIndex The column index
6037      * @return {Boolean}
6038      */
6039     isHidden : function(colIndex){
6040         return this.config[colIndex].hidden;
6041     },
6042
6043
6044     /**
6045      * Returns true if the column width cannot be changed
6046      */
6047     isFixed : function(colIndex){
6048         return this.config[colIndex].fixed;
6049     },
6050
6051     /**
6052      * Returns true if the column can be resized
6053      * @return {Boolean}
6054      */
6055     isResizable : function(colIndex){
6056         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6057     },
6058     /**
6059      * Sets if a column is hidden.
6060      * @param {Number} colIndex The column index
6061      * @param {Boolean} hidden True if the column is hidden
6062      */
6063     setHidden : function(colIndex, hidden){
6064         this.config[colIndex].hidden = hidden;
6065         this.totalWidth = null;
6066         this.fireEvent("hiddenchange", this, colIndex, hidden);
6067     },
6068
6069     /**
6070      * Sets the editor for a column.
6071      * @param {Number} col The column index
6072      * @param {Object} editor The editor object
6073      */
6074     setEditor : function(col, editor){
6075         this.config[col].editor = editor;
6076     }
6077 });
6078
6079 Roo.grid.ColumnModel.defaultRenderer = function(value)
6080 {
6081     if(typeof value == "object") {
6082         return value;
6083     }
6084         if(typeof value == "string" && value.length < 1){
6085             return "&#160;";
6086         }
6087     
6088         return String.format("{0}", value);
6089 };
6090
6091 // Alias for backwards compatibility
6092 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6093 /*
6094  * Based on:
6095  * Ext JS Library 1.1.1
6096  * Copyright(c) 2006-2007, Ext JS, LLC.
6097  *
6098  * Originally Released Under LGPL - original licence link has changed is not relivant.
6099  *
6100  * Fork - LGPL
6101  * <script type="text/javascript">
6102  */
6103  
6104 /**
6105  * @class Roo.LoadMask
6106  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6107  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6108  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6109  * element's UpdateManager load indicator and will be destroyed after the initial load.
6110  * @constructor
6111  * Create a new LoadMask
6112  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6113  * @param {Object} config The config object
6114  */
6115 Roo.LoadMask = function(el, config){
6116     this.el = Roo.get(el);
6117     Roo.apply(this, config);
6118     if(this.store){
6119         this.store.on('beforeload', this.onBeforeLoad, this);
6120         this.store.on('load', this.onLoad, this);
6121         this.store.on('loadexception', this.onLoadException, this);
6122         this.removeMask = false;
6123     }else{
6124         var um = this.el.getUpdateManager();
6125         um.showLoadIndicator = false; // disable the default indicator
6126         um.on('beforeupdate', this.onBeforeLoad, this);
6127         um.on('update', this.onLoad, this);
6128         um.on('failure', this.onLoad, this);
6129         this.removeMask = true;
6130     }
6131 };
6132
6133 Roo.LoadMask.prototype = {
6134     /**
6135      * @cfg {Boolean} removeMask
6136      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6137      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6138      */
6139     /**
6140      * @cfg {String} msg
6141      * The text to display in a centered loading message box (defaults to 'Loading...')
6142      */
6143     msg : 'Loading...',
6144     /**
6145      * @cfg {String} msgCls
6146      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6147      */
6148     msgCls : 'x-mask-loading',
6149
6150     /**
6151      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6152      * @type Boolean
6153      */
6154     disabled: false,
6155
6156     /**
6157      * Disables the mask to prevent it from being displayed
6158      */
6159     disable : function(){
6160        this.disabled = true;
6161     },
6162
6163     /**
6164      * Enables the mask so that it can be displayed
6165      */
6166     enable : function(){
6167         this.disabled = false;
6168     },
6169     
6170     onLoadException : function()
6171     {
6172         Roo.log(arguments);
6173         
6174         if (typeof(arguments[3]) != 'undefined') {
6175             Roo.MessageBox.alert("Error loading",arguments[3]);
6176         } 
6177         /*
6178         try {
6179             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6180                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6181             }   
6182         } catch(e) {
6183             
6184         }
6185         */
6186     
6187         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6188     },
6189     // private
6190     onLoad : function()
6191     {
6192         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6193     },
6194
6195     // private
6196     onBeforeLoad : function(){
6197         if(!this.disabled){
6198             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6199         }
6200     },
6201
6202     // private
6203     destroy : function(){
6204         if(this.store){
6205             this.store.un('beforeload', this.onBeforeLoad, this);
6206             this.store.un('load', this.onLoad, this);
6207             this.store.un('loadexception', this.onLoadException, this);
6208         }else{
6209             var um = this.el.getUpdateManager();
6210             um.un('beforeupdate', this.onBeforeLoad, this);
6211             um.un('update', this.onLoad, this);
6212             um.un('failure', this.onLoad, this);
6213         }
6214     }
6215 };/*
6216  * - LGPL
6217  *
6218  * table
6219  * 
6220  */
6221
6222 /**
6223  * @class Roo.bootstrap.Table
6224  * @extends Roo.bootstrap.Component
6225  * Bootstrap Table class
6226  * @cfg {String} cls table class
6227  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6228  * @cfg {String} bgcolor Specifies the background color for a table
6229  * @cfg {Number} border Specifies whether the table cells should have borders or not
6230  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6231  * @cfg {Number} cellspacing Specifies the space between cells
6232  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6233  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6234  * @cfg {String} sortable Specifies that the table should be sortable
6235  * @cfg {String} summary Specifies a summary of the content of a table
6236  * @cfg {Number} width Specifies the width of a table
6237  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6238  * 
6239  * @cfg {boolean} striped Should the rows be alternative striped
6240  * @cfg {boolean} bordered Add borders to the table
6241  * @cfg {boolean} hover Add hover highlighting
6242  * @cfg {boolean} condensed Format condensed
6243  * @cfg {boolean} responsive Format condensed
6244  * @cfg {Boolean} loadMask (true|false) default false
6245  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6246  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6247  * @cfg {Boolean} rowSelection (true|false) default false
6248  * @cfg {Boolean} cellSelection (true|false) default false
6249  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6250  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6251  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6252  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6253  
6254  * 
6255  * @constructor
6256  * Create a new Table
6257  * @param {Object} config The config object
6258  */
6259
6260 Roo.bootstrap.Table = function(config){
6261     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6262     
6263   
6264     
6265     // BC...
6266     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6267     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6268     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6269     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6270     
6271     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6272     if (this.sm) {
6273         this.sm.grid = this;
6274         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6275         this.sm = this.selModel;
6276         this.sm.xmodule = this.xmodule || false;
6277     }
6278     
6279     if (this.cm && typeof(this.cm.config) == 'undefined') {
6280         this.colModel = new Roo.grid.ColumnModel(this.cm);
6281         this.cm = this.colModel;
6282         this.cm.xmodule = this.xmodule || false;
6283     }
6284     if (this.store) {
6285         this.store= Roo.factory(this.store, Roo.data);
6286         this.ds = this.store;
6287         this.ds.xmodule = this.xmodule || false;
6288          
6289     }
6290     if (this.footer && this.store) {
6291         this.footer.dataSource = this.ds;
6292         this.footer = Roo.factory(this.footer);
6293     }
6294     
6295     /** @private */
6296     this.addEvents({
6297         /**
6298          * @event cellclick
6299          * Fires when a cell is clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Roo.Element} el
6302          * @param {Number} rowIndex
6303          * @param {Number} columnIndex
6304          * @param {Roo.EventObject} e
6305          */
6306         "cellclick" : true,
6307         /**
6308          * @event celldblclick
6309          * Fires when a cell is double clicked
6310          * @param {Roo.bootstrap.Table} this
6311          * @param {Roo.Element} el
6312          * @param {Number} rowIndex
6313          * @param {Number} columnIndex
6314          * @param {Roo.EventObject} e
6315          */
6316         "celldblclick" : true,
6317         /**
6318          * @event rowclick
6319          * Fires when a row is clicked
6320          * @param {Roo.bootstrap.Table} this
6321          * @param {Roo.Element} el
6322          * @param {Number} rowIndex
6323          * @param {Roo.EventObject} e
6324          */
6325         "rowclick" : true,
6326         /**
6327          * @event rowdblclick
6328          * Fires when a row is double clicked
6329          * @param {Roo.bootstrap.Table} this
6330          * @param {Roo.Element} el
6331          * @param {Number} rowIndex
6332          * @param {Roo.EventObject} e
6333          */
6334         "rowdblclick" : true,
6335         /**
6336          * @event mouseover
6337          * Fires when a mouseover occur
6338          * @param {Roo.bootstrap.Table} this
6339          * @param {Roo.Element} el
6340          * @param {Number} rowIndex
6341          * @param {Number} columnIndex
6342          * @param {Roo.EventObject} e
6343          */
6344         "mouseover" : true,
6345         /**
6346          * @event mouseout
6347          * Fires when a mouseout occur
6348          * @param {Roo.bootstrap.Table} this
6349          * @param {Roo.Element} el
6350          * @param {Number} rowIndex
6351          * @param {Number} columnIndex
6352          * @param {Roo.EventObject} e
6353          */
6354         "mouseout" : true,
6355         /**
6356          * @event rowclass
6357          * Fires when a row is rendered, so you can change add a style to it.
6358          * @param {Roo.bootstrap.Table} this
6359          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6360          */
6361         'rowclass' : true,
6362           /**
6363          * @event rowsrendered
6364          * Fires when all the  rows have been rendered
6365          * @param {Roo.bootstrap.Table} this
6366          */
6367         'rowsrendered' : true,
6368         /**
6369          * @event contextmenu
6370          * The raw contextmenu event for the entire grid.
6371          * @param {Roo.EventObject} e
6372          */
6373         "contextmenu" : true,
6374         /**
6375          * @event rowcontextmenu
6376          * Fires when a row is right clicked
6377          * @param {Roo.bootstrap.Table} this
6378          * @param {Number} rowIndex
6379          * @param {Roo.EventObject} e
6380          */
6381         "rowcontextmenu" : true,
6382         /**
6383          * @event cellcontextmenu
6384          * Fires when a cell is right clicked
6385          * @param {Roo.bootstrap.Table} this
6386          * @param {Number} rowIndex
6387          * @param {Number} cellIndex
6388          * @param {Roo.EventObject} e
6389          */
6390          "cellcontextmenu" : true,
6391          /**
6392          * @event headercontextmenu
6393          * Fires when a header is right clicked
6394          * @param {Roo.bootstrap.Table} this
6395          * @param {Number} columnIndex
6396          * @param {Roo.EventObject} e
6397          */
6398         "headercontextmenu" : true
6399     });
6400 };
6401
6402 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6403     
6404     cls: false,
6405     align: false,
6406     bgcolor: false,
6407     border: false,
6408     cellpadding: false,
6409     cellspacing: false,
6410     frame: false,
6411     rules: false,
6412     sortable: false,
6413     summary: false,
6414     width: false,
6415     striped : false,
6416     scrollBody : false,
6417     bordered: false,
6418     hover:  false,
6419     condensed : false,
6420     responsive : false,
6421     sm : false,
6422     cm : false,
6423     store : false,
6424     loadMask : false,
6425     footerShow : true,
6426     headerShow : true,
6427   
6428     rowSelection : false,
6429     cellSelection : false,
6430     layout : false,
6431     
6432     // Roo.Element - the tbody
6433     mainBody: false,
6434     // Roo.Element - thead element
6435     mainHead: false,
6436     
6437     container: false, // used by gridpanel...
6438     
6439     lazyLoad : false,
6440     
6441     CSS : Roo.util.CSS,
6442     
6443     auto_hide_footer : false,
6444     
6445     getAutoCreate : function()
6446     {
6447         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6448         
6449         cfg = {
6450             tag: 'table',
6451             cls : 'table',
6452             cn : []
6453         };
6454         if (this.scrollBody) {
6455             cfg.cls += ' table-body-fixed';
6456         }    
6457         if (this.striped) {
6458             cfg.cls += ' table-striped';
6459         }
6460         
6461         if (this.hover) {
6462             cfg.cls += ' table-hover';
6463         }
6464         if (this.bordered) {
6465             cfg.cls += ' table-bordered';
6466         }
6467         if (this.condensed) {
6468             cfg.cls += ' table-condensed';
6469         }
6470         if (this.responsive) {
6471             cfg.cls += ' table-responsive';
6472         }
6473         
6474         if (this.cls) {
6475             cfg.cls+=  ' ' +this.cls;
6476         }
6477         
6478         // this lot should be simplifed...
6479         var _t = this;
6480         var cp = [
6481             'align',
6482             'bgcolor',
6483             'border',
6484             'cellpadding',
6485             'cellspacing',
6486             'frame',
6487             'rules',
6488             'sortable',
6489             'summary',
6490             'width'
6491         ].forEach(function(k) {
6492             if (_t[k]) {
6493                 cfg[k] = _t[k];
6494             }
6495         });
6496         
6497         
6498         if (this.layout) {
6499             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6500         }
6501         
6502         if(this.store || this.cm){
6503             if(this.headerShow){
6504                 cfg.cn.push(this.renderHeader());
6505             }
6506             
6507             cfg.cn.push(this.renderBody());
6508             
6509             if(this.footerShow){
6510                 cfg.cn.push(this.renderFooter());
6511             }
6512             // where does this come from?
6513             //cfg.cls+=  ' TableGrid';
6514         }
6515         
6516         return { cn : [ cfg ] };
6517     },
6518     
6519     initEvents : function()
6520     {   
6521         if(!this.store || !this.cm){
6522             return;
6523         }
6524         if (this.selModel) {
6525             this.selModel.initEvents();
6526         }
6527         
6528         
6529         //Roo.log('initEvents with ds!!!!');
6530         
6531         this.mainBody = this.el.select('tbody', true).first();
6532         this.mainHead = this.el.select('thead', true).first();
6533         this.mainFoot = this.el.select('tfoot', true).first();
6534         
6535         
6536         
6537         var _this = this;
6538         
6539         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6540             e.on('click', _this.sort, _this);
6541         });
6542         
6543         this.mainBody.on("click", this.onClick, this);
6544         this.mainBody.on("dblclick", this.onDblClick, this);
6545         
6546         // why is this done????? = it breaks dialogs??
6547         //this.parent().el.setStyle('position', 'relative');
6548         
6549         
6550         if (this.footer) {
6551             this.footer.parentId = this.id;
6552             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6553             
6554             if(this.lazyLoad){
6555                 this.el.select('tfoot tr td').first().addClass('hide');
6556             }
6557         } 
6558         
6559         if(this.loadMask) {
6560             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6561         }
6562         
6563         this.store.on('load', this.onLoad, this);
6564         this.store.on('beforeload', this.onBeforeLoad, this);
6565         this.store.on('update', this.onUpdate, this);
6566         this.store.on('add', this.onAdd, this);
6567         this.store.on("clear", this.clear, this);
6568         
6569         this.el.on("contextmenu", this.onContextMenu, this);
6570         
6571         this.mainBody.on('scroll', this.onBodyScroll, this);
6572         
6573         this.cm.on("headerchange", this.onHeaderChange, this);
6574         
6575         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6576         
6577     },
6578     
6579     onContextMenu : function(e, t)
6580     {
6581         this.processEvent("contextmenu", e);
6582     },
6583     
6584     processEvent : function(name, e)
6585     {
6586         if (name != 'touchstart' ) {
6587             this.fireEvent(name, e);    
6588         }
6589         
6590         var t = e.getTarget();
6591         
6592         var cell = Roo.get(t);
6593         
6594         if(!cell){
6595             return;
6596         }
6597         
6598         if(cell.findParent('tfoot', false, true)){
6599             return;
6600         }
6601         
6602         if(cell.findParent('thead', false, true)){
6603             
6604             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6605                 cell = Roo.get(t).findParent('th', false, true);
6606                 if (!cell) {
6607                     Roo.log("failed to find th in thead?");
6608                     Roo.log(e.getTarget());
6609                     return;
6610                 }
6611             }
6612             
6613             var cellIndex = cell.dom.cellIndex;
6614             
6615             var ename = name == 'touchstart' ? 'click' : name;
6616             this.fireEvent("header" + ename, this, cellIndex, e);
6617             
6618             return;
6619         }
6620         
6621         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6622             cell = Roo.get(t).findParent('td', false, true);
6623             if (!cell) {
6624                 Roo.log("failed to find th in tbody?");
6625                 Roo.log(e.getTarget());
6626                 return;
6627             }
6628         }
6629         
6630         var row = cell.findParent('tr', false, true);
6631         var cellIndex = cell.dom.cellIndex;
6632         var rowIndex = row.dom.rowIndex - 1;
6633         
6634         if(row !== false){
6635             
6636             this.fireEvent("row" + name, this, rowIndex, e);
6637             
6638             if(cell !== false){
6639             
6640                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6641             }
6642         }
6643         
6644     },
6645     
6646     onMouseover : function(e, el)
6647     {
6648         var cell = Roo.get(el);
6649         
6650         if(!cell){
6651             return;
6652         }
6653         
6654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6655             cell = cell.findParent('td', false, true);
6656         }
6657         
6658         var row = cell.findParent('tr', false, true);
6659         var cellIndex = cell.dom.cellIndex;
6660         var rowIndex = row.dom.rowIndex - 1; // start from 0
6661         
6662         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6663         
6664     },
6665     
6666     onMouseout : function(e, el)
6667     {
6668         var cell = Roo.get(el);
6669         
6670         if(!cell){
6671             return;
6672         }
6673         
6674         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6675             cell = cell.findParent('td', false, true);
6676         }
6677         
6678         var row = cell.findParent('tr', false, true);
6679         var cellIndex = cell.dom.cellIndex;
6680         var rowIndex = row.dom.rowIndex - 1; // start from 0
6681         
6682         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6683         
6684     },
6685     
6686     onClick : function(e, el)
6687     {
6688         var cell = Roo.get(el);
6689         
6690         if(!cell || (!this.cellSelection && !this.rowSelection)){
6691             return;
6692         }
6693         
6694         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6695             cell = cell.findParent('td', false, true);
6696         }
6697         
6698         if(!cell || typeof(cell) == 'undefined'){
6699             return;
6700         }
6701         
6702         var row = cell.findParent('tr', false, true);
6703         
6704         if(!row || typeof(row) == 'undefined'){
6705             return;
6706         }
6707         
6708         var cellIndex = cell.dom.cellIndex;
6709         var rowIndex = this.getRowIndex(row);
6710         
6711         // why??? - should these not be based on SelectionModel?
6712         if(this.cellSelection){
6713             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6714         }
6715         
6716         if(this.rowSelection){
6717             this.fireEvent('rowclick', this, row, rowIndex, e);
6718         }
6719         
6720         
6721     },
6722         
6723     onDblClick : function(e,el)
6724     {
6725         var cell = Roo.get(el);
6726         
6727         if(!cell || (!this.cellSelection && !this.rowSelection)){
6728             return;
6729         }
6730         
6731         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6732             cell = cell.findParent('td', false, true);
6733         }
6734         
6735         if(!cell || typeof(cell) == 'undefined'){
6736             return;
6737         }
6738         
6739         var row = cell.findParent('tr', false, true);
6740         
6741         if(!row || typeof(row) == 'undefined'){
6742             return;
6743         }
6744         
6745         var cellIndex = cell.dom.cellIndex;
6746         var rowIndex = this.getRowIndex(row);
6747         
6748         if(this.cellSelection){
6749             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6750         }
6751         
6752         if(this.rowSelection){
6753             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6754         }
6755     },
6756     
6757     sort : function(e,el)
6758     {
6759         var col = Roo.get(el);
6760         
6761         if(!col.hasClass('sortable')){
6762             return;
6763         }
6764         
6765         var sort = col.attr('sort');
6766         var dir = 'ASC';
6767         
6768         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6769             dir = 'DESC';
6770         }
6771         
6772         this.store.sortInfo = {field : sort, direction : dir};
6773         
6774         if (this.footer) {
6775             Roo.log("calling footer first");
6776             this.footer.onClick('first');
6777         } else {
6778         
6779             this.store.load({ params : { start : 0 } });
6780         }
6781     },
6782     
6783     renderHeader : function()
6784     {
6785         var header = {
6786             tag: 'thead',
6787             cn : []
6788         };
6789         
6790         var cm = this.cm;
6791         this.totalWidth = 0;
6792         
6793         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6794             
6795             var config = cm.config[i];
6796             
6797             var c = {
6798                 tag: 'th',
6799                 cls : 'x-hcol-' + i,
6800                 style : '',
6801                 html: cm.getColumnHeader(i)
6802             };
6803             
6804             var hh = '';
6805             
6806             if(typeof(config.sortable) != 'undefined' && config.sortable){
6807                 c.cls = 'sortable';
6808                 c.html = '<i class="glyphicon"></i>' + c.html;
6809             }
6810             
6811             if(typeof(config.lgHeader) != 'undefined'){
6812                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6813             }
6814             
6815             if(typeof(config.mdHeader) != 'undefined'){
6816                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6817             }
6818             
6819             if(typeof(config.smHeader) != 'undefined'){
6820                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6821             }
6822             
6823             if(typeof(config.xsHeader) != 'undefined'){
6824                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6825             }
6826             
6827             if(hh.length){
6828                 c.html = hh;
6829             }
6830             
6831             if(typeof(config.tooltip) != 'undefined'){
6832                 c.tooltip = config.tooltip;
6833             }
6834             
6835             if(typeof(config.colspan) != 'undefined'){
6836                 c.colspan = config.colspan;
6837             }
6838             
6839             if(typeof(config.hidden) != 'undefined' && config.hidden){
6840                 c.style += ' display:none;';
6841             }
6842             
6843             if(typeof(config.dataIndex) != 'undefined'){
6844                 c.sort = config.dataIndex;
6845             }
6846             
6847            
6848             
6849             if(typeof(config.align) != 'undefined' && config.align.length){
6850                 c.style += ' text-align:' + config.align + ';';
6851             }
6852             
6853             if(typeof(config.width) != 'undefined'){
6854                 c.style += ' width:' + config.width + 'px;';
6855                 this.totalWidth += config.width;
6856             } else {
6857                 this.totalWidth += 100; // assume minimum of 100 per column?
6858             }
6859             
6860             if(typeof(config.cls) != 'undefined'){
6861                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6862             }
6863             
6864             ['xs','sm','md','lg'].map(function(size){
6865                 
6866                 if(typeof(config[size]) == 'undefined'){
6867                     return;
6868                 }
6869                 
6870                 if (!config[size]) { // 0 = hidden
6871                     c.cls += ' hidden-' + size;
6872                     return;
6873                 }
6874                 
6875                 c.cls += ' col-' + size + '-' + config[size];
6876
6877             });
6878             
6879             header.cn.push(c)
6880         }
6881         
6882         return header;
6883     },
6884     
6885     renderBody : function()
6886     {
6887         var body = {
6888             tag: 'tbody',
6889             cn : [
6890                 {
6891                     tag: 'tr',
6892                     cn : [
6893                         {
6894                             tag : 'td',
6895                             colspan :  this.cm.getColumnCount()
6896                         }
6897                     ]
6898                 }
6899             ]
6900         };
6901         
6902         return body;
6903     },
6904     
6905     renderFooter : function()
6906     {
6907         var footer = {
6908             tag: 'tfoot',
6909             cn : [
6910                 {
6911                     tag: 'tr',
6912                     cn : [
6913                         {
6914                             tag : 'td',
6915                             colspan :  this.cm.getColumnCount()
6916                         }
6917                     ]
6918                 }
6919             ]
6920         };
6921         
6922         return footer;
6923     },
6924     
6925     
6926     
6927     onLoad : function()
6928     {
6929 //        Roo.log('ds onload');
6930         this.clear();
6931         
6932         var _this = this;
6933         var cm = this.cm;
6934         var ds = this.store;
6935         
6936         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6937             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6938             if (_this.store.sortInfo) {
6939                     
6940                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6941                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6942                 }
6943                 
6944                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6945                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6946                 }
6947             }
6948         });
6949         
6950         var tbody =  this.mainBody;
6951               
6952         if(ds.getCount() > 0){
6953             ds.data.each(function(d,rowIndex){
6954                 var row =  this.renderRow(cm, ds, rowIndex);
6955                 
6956                 tbody.createChild(row);
6957                 
6958                 var _this = this;
6959                 
6960                 if(row.cellObjects.length){
6961                     Roo.each(row.cellObjects, function(r){
6962                         _this.renderCellObject(r);
6963                     })
6964                 }
6965                 
6966             }, this);
6967         }
6968         
6969         var tfoot = this.el.select('tfoot', true).first();
6970         
6971         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6972             
6973             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6974             
6975             var total = this.ds.getTotalCount();
6976             
6977             if(this.footer.pageSize < total){
6978                 this.mainFoot.show();
6979             }
6980         }
6981         
6982         Roo.each(this.el.select('tbody td', true).elements, function(e){
6983             e.on('mouseover', _this.onMouseover, _this);
6984         });
6985         
6986         Roo.each(this.el.select('tbody td', true).elements, function(e){
6987             e.on('mouseout', _this.onMouseout, _this);
6988         });
6989         this.fireEvent('rowsrendered', this);
6990         
6991         this.autoSize();
6992     },
6993     
6994     
6995     onUpdate : function(ds,record)
6996     {
6997         this.refreshRow(record);
6998         this.autoSize();
6999     },
7000     
7001     onRemove : function(ds, record, index, isUpdate){
7002         if(isUpdate !== true){
7003             this.fireEvent("beforerowremoved", this, index, record);
7004         }
7005         var bt = this.mainBody.dom;
7006         
7007         var rows = this.el.select('tbody > tr', true).elements;
7008         
7009         if(typeof(rows[index]) != 'undefined'){
7010             bt.removeChild(rows[index].dom);
7011         }
7012         
7013 //        if(bt.rows[index]){
7014 //            bt.removeChild(bt.rows[index]);
7015 //        }
7016         
7017         if(isUpdate !== true){
7018             //this.stripeRows(index);
7019             //this.syncRowHeights(index, index);
7020             //this.layout();
7021             this.fireEvent("rowremoved", this, index, record);
7022         }
7023     },
7024     
7025     onAdd : function(ds, records, rowIndex)
7026     {
7027         //Roo.log('on Add called');
7028         // - note this does not handle multiple adding very well..
7029         var bt = this.mainBody.dom;
7030         for (var i =0 ; i < records.length;i++) {
7031             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7032             //Roo.log(records[i]);
7033             //Roo.log(this.store.getAt(rowIndex+i));
7034             this.insertRow(this.store, rowIndex + i, false);
7035             return;
7036         }
7037         
7038     },
7039     
7040     
7041     refreshRow : function(record){
7042         var ds = this.store, index;
7043         if(typeof record == 'number'){
7044             index = record;
7045             record = ds.getAt(index);
7046         }else{
7047             index = ds.indexOf(record);
7048         }
7049         this.insertRow(ds, index, true);
7050         this.autoSize();
7051         this.onRemove(ds, record, index+1, true);
7052         this.autoSize();
7053         //this.syncRowHeights(index, index);
7054         //this.layout();
7055         this.fireEvent("rowupdated", this, index, record);
7056     },
7057     
7058     insertRow : function(dm, rowIndex, isUpdate){
7059         
7060         if(!isUpdate){
7061             this.fireEvent("beforerowsinserted", this, rowIndex);
7062         }
7063             //var s = this.getScrollState();
7064         var row = this.renderRow(this.cm, this.store, rowIndex);
7065         // insert before rowIndex..
7066         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7067         
7068         var _this = this;
7069                 
7070         if(row.cellObjects.length){
7071             Roo.each(row.cellObjects, function(r){
7072                 _this.renderCellObject(r);
7073             })
7074         }
7075             
7076         if(!isUpdate){
7077             this.fireEvent("rowsinserted", this, rowIndex);
7078             //this.syncRowHeights(firstRow, lastRow);
7079             //this.stripeRows(firstRow);
7080             //this.layout();
7081         }
7082         
7083     },
7084     
7085     
7086     getRowDom : function(rowIndex)
7087     {
7088         var rows = this.el.select('tbody > tr', true).elements;
7089         
7090         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7091         
7092     },
7093     // returns the object tree for a tr..
7094   
7095     
7096     renderRow : function(cm, ds, rowIndex) 
7097     {
7098         var d = ds.getAt(rowIndex);
7099         
7100         var row = {
7101             tag : 'tr',
7102             cls : 'x-row-' + rowIndex,
7103             cn : []
7104         };
7105             
7106         var cellObjects = [];
7107         
7108         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7109             var config = cm.config[i];
7110             
7111             var renderer = cm.getRenderer(i);
7112             var value = '';
7113             var id = false;
7114             
7115             if(typeof(renderer) !== 'undefined'){
7116                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7117             }
7118             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7119             // and are rendered into the cells after the row is rendered - using the id for the element.
7120             
7121             if(typeof(value) === 'object'){
7122                 id = Roo.id();
7123                 cellObjects.push({
7124                     container : id,
7125                     cfg : value 
7126                 })
7127             }
7128             
7129             var rowcfg = {
7130                 record: d,
7131                 rowIndex : rowIndex,
7132                 colIndex : i,
7133                 rowClass : ''
7134             };
7135
7136             this.fireEvent('rowclass', this, rowcfg);
7137             
7138             var td = {
7139                 tag: 'td',
7140                 cls : rowcfg.rowClass + ' x-col-' + i,
7141                 style: '',
7142                 html: (typeof(value) === 'object') ? '' : value
7143             };
7144             
7145             if (id) {
7146                 td.id = id;
7147             }
7148             
7149             if(typeof(config.colspan) != 'undefined'){
7150                 td.colspan = config.colspan;
7151             }
7152             
7153             if(typeof(config.hidden) != 'undefined' && config.hidden){
7154                 td.style += ' display:none;';
7155             }
7156             
7157             if(typeof(config.align) != 'undefined' && config.align.length){
7158                 td.style += ' text-align:' + config.align + ';';
7159             }
7160             if(typeof(config.valign) != 'undefined' && config.valign.length){
7161                 td.style += ' vertical-align:' + config.valign + ';';
7162             }
7163             
7164             if(typeof(config.width) != 'undefined'){
7165                 td.style += ' width:' +  config.width + 'px;';
7166             }
7167             
7168             if(typeof(config.cursor) != 'undefined'){
7169                 td.style += ' cursor:' +  config.cursor + ';';
7170             }
7171             
7172             if(typeof(config.cls) != 'undefined'){
7173                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7174             }
7175             
7176             ['xs','sm','md','lg'].map(function(size){
7177                 
7178                 if(typeof(config[size]) == 'undefined'){
7179                     return;
7180                 }
7181                 
7182                 if (!config[size]) { // 0 = hidden
7183                     td.cls += ' hidden-' + size;
7184                     return;
7185                 }
7186                 
7187                 td.cls += ' col-' + size + '-' + config[size];
7188
7189             });
7190             
7191             row.cn.push(td);
7192            
7193         }
7194         
7195         row.cellObjects = cellObjects;
7196         
7197         return row;
7198           
7199     },
7200     
7201     
7202     
7203     onBeforeLoad : function()
7204     {
7205         
7206     },
7207      /**
7208      * Remove all rows
7209      */
7210     clear : function()
7211     {
7212         this.el.select('tbody', true).first().dom.innerHTML = '';
7213     },
7214     /**
7215      * Show or hide a row.
7216      * @param {Number} rowIndex to show or hide
7217      * @param {Boolean} state hide
7218      */
7219     setRowVisibility : function(rowIndex, state)
7220     {
7221         var bt = this.mainBody.dom;
7222         
7223         var rows = this.el.select('tbody > tr', true).elements;
7224         
7225         if(typeof(rows[rowIndex]) == 'undefined'){
7226             return;
7227         }
7228         rows[rowIndex].dom.style.display = state ? '' : 'none';
7229     },
7230     
7231     
7232     getSelectionModel : function(){
7233         if(!this.selModel){
7234             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7235         }
7236         return this.selModel;
7237     },
7238     /*
7239      * Render the Roo.bootstrap object from renderder
7240      */
7241     renderCellObject : function(r)
7242     {
7243         var _this = this;
7244         
7245         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7246         
7247         var t = r.cfg.render(r.container);
7248         
7249         if(r.cfg.cn){
7250             Roo.each(r.cfg.cn, function(c){
7251                 var child = {
7252                     container: t.getChildContainer(),
7253                     cfg: c
7254                 };
7255                 _this.renderCellObject(child);
7256             })
7257         }
7258     },
7259     
7260     getRowIndex : function(row)
7261     {
7262         var rowIndex = -1;
7263         
7264         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7265             if(el != row){
7266                 return;
7267             }
7268             
7269             rowIndex = index;
7270         });
7271         
7272         return rowIndex;
7273     },
7274      /**
7275      * Returns the grid's underlying element = used by panel.Grid
7276      * @return {Element} The element
7277      */
7278     getGridEl : function(){
7279         return this.el;
7280     },
7281      /**
7282      * Forces a resize - used by panel.Grid
7283      * @return {Element} The element
7284      */
7285     autoSize : function()
7286     {
7287         //var ctr = Roo.get(this.container.dom.parentElement);
7288         var ctr = Roo.get(this.el.dom);
7289         
7290         var thd = this.getGridEl().select('thead',true).first();
7291         var tbd = this.getGridEl().select('tbody', true).first();
7292         var tfd = this.getGridEl().select('tfoot', true).first();
7293         
7294         var cw = ctr.getWidth();
7295         
7296         if (tbd) {
7297             
7298             tbd.setSize(ctr.getWidth(),
7299                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7300             );
7301             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7302             cw -= barsize;
7303         }
7304         cw = Math.max(cw, this.totalWidth);
7305         this.getGridEl().select('tr',true).setWidth(cw);
7306         // resize 'expandable coloumn?
7307         
7308         return; // we doe not have a view in this design..
7309         
7310     },
7311     onBodyScroll: function()
7312     {
7313         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7314         if(this.mainHead){
7315             this.mainHead.setStyle({
7316                 'position' : 'relative',
7317                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7318             });
7319         }
7320         
7321         if(this.lazyLoad){
7322             
7323             var scrollHeight = this.mainBody.dom.scrollHeight;
7324             
7325             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7326             
7327             var height = this.mainBody.getHeight();
7328             
7329             if(scrollHeight - height == scrollTop) {
7330                 
7331                 var total = this.ds.getTotalCount();
7332                 
7333                 if(this.footer.cursor + this.footer.pageSize < total){
7334                     
7335                     this.footer.ds.load({
7336                         params : {
7337                             start : this.footer.cursor + this.footer.pageSize,
7338                             limit : this.footer.pageSize
7339                         },
7340                         add : true
7341                     });
7342                 }
7343             }
7344             
7345         }
7346     },
7347     
7348     onHeaderChange : function()
7349     {
7350         var header = this.renderHeader();
7351         var table = this.el.select('table', true).first();
7352         
7353         this.mainHead.remove();
7354         this.mainHead = table.createChild(header, this.mainBody, false);
7355     },
7356     
7357     onHiddenChange : function(colModel, colIndex, hidden)
7358     {
7359         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7360         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7361         
7362         this.CSS.updateRule(thSelector, "display", "");
7363         this.CSS.updateRule(tdSelector, "display", "");
7364         
7365         if(hidden){
7366             this.CSS.updateRule(thSelector, "display", "none");
7367             this.CSS.updateRule(tdSelector, "display", "none");
7368         }
7369         
7370         this.onHeaderChange();
7371         this.onLoad();
7372     },
7373     
7374     setColumnWidth: function(col_index, width)
7375     {
7376         // width = "md-2 xs-2..."
7377         if(!this.colModel.config[col_index]) {
7378             return;
7379         }
7380         
7381         var w = width.split(" ");
7382         
7383         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7384         
7385         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7386         
7387         
7388         for(var j = 0; j < w.length; j++) {
7389             
7390             if(!w[j]) {
7391                 continue;
7392             }
7393             
7394             var size_cls = w[j].split("-");
7395             
7396             if(!Number.isInteger(size_cls[1] * 1)) {
7397                 continue;
7398             }
7399             
7400             if(!this.colModel.config[col_index][size_cls[0]]) {
7401                 continue;
7402             }
7403             
7404             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7405                 continue;
7406             }
7407             
7408             h_row[0].classList.replace(
7409                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7410                 "col-"+size_cls[0]+"-"+size_cls[1]
7411             );
7412             
7413             for(var i = 0; i < rows.length; i++) {
7414                 
7415                 var size_cls = w[j].split("-");
7416                 
7417                 if(!Number.isInteger(size_cls[1] * 1)) {
7418                     continue;
7419                 }
7420                 
7421                 if(!this.colModel.config[col_index][size_cls[0]]) {
7422                     continue;
7423                 }
7424                 
7425                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7426                     continue;
7427                 }
7428                 
7429                 rows[i].classList.replace(
7430                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7431                     "col-"+size_cls[0]+"-"+size_cls[1]
7432                 );
7433             }
7434             
7435             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7436         }
7437     }
7438 });
7439
7440  
7441
7442  /*
7443  * - LGPL
7444  *
7445  * table cell
7446  * 
7447  */
7448
7449 /**
7450  * @class Roo.bootstrap.TableCell
7451  * @extends Roo.bootstrap.Component
7452  * Bootstrap TableCell class
7453  * @cfg {String} html cell contain text
7454  * @cfg {String} cls cell class
7455  * @cfg {String} tag cell tag (td|th) default td
7456  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7457  * @cfg {String} align Aligns the content in a cell
7458  * @cfg {String} axis Categorizes cells
7459  * @cfg {String} bgcolor Specifies the background color of a cell
7460  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7461  * @cfg {Number} colspan Specifies the number of columns a cell should span
7462  * @cfg {String} headers Specifies one or more header cells a cell is related to
7463  * @cfg {Number} height Sets the height of a cell
7464  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7465  * @cfg {Number} rowspan Sets the number of rows a cell should span
7466  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7467  * @cfg {String} valign Vertical aligns the content in a cell
7468  * @cfg {Number} width Specifies the width of a cell
7469  * 
7470  * @constructor
7471  * Create a new TableCell
7472  * @param {Object} config The config object
7473  */
7474
7475 Roo.bootstrap.TableCell = function(config){
7476     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7477 };
7478
7479 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7480     
7481     html: false,
7482     cls: false,
7483     tag: false,
7484     abbr: false,
7485     align: false,
7486     axis: false,
7487     bgcolor: false,
7488     charoff: false,
7489     colspan: false,
7490     headers: false,
7491     height: false,
7492     nowrap: false,
7493     rowspan: false,
7494     scope: false,
7495     valign: false,
7496     width: false,
7497     
7498     
7499     getAutoCreate : function(){
7500         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7501         
7502         cfg = {
7503             tag: 'td'
7504         };
7505         
7506         if(this.tag){
7507             cfg.tag = this.tag;
7508         }
7509         
7510         if (this.html) {
7511             cfg.html=this.html
7512         }
7513         if (this.cls) {
7514             cfg.cls=this.cls
7515         }
7516         if (this.abbr) {
7517             cfg.abbr=this.abbr
7518         }
7519         if (this.align) {
7520             cfg.align=this.align
7521         }
7522         if (this.axis) {
7523             cfg.axis=this.axis
7524         }
7525         if (this.bgcolor) {
7526             cfg.bgcolor=this.bgcolor
7527         }
7528         if (this.charoff) {
7529             cfg.charoff=this.charoff
7530         }
7531         if (this.colspan) {
7532             cfg.colspan=this.colspan
7533         }
7534         if (this.headers) {
7535             cfg.headers=this.headers
7536         }
7537         if (this.height) {
7538             cfg.height=this.height
7539         }
7540         if (this.nowrap) {
7541             cfg.nowrap=this.nowrap
7542         }
7543         if (this.rowspan) {
7544             cfg.rowspan=this.rowspan
7545         }
7546         if (this.scope) {
7547             cfg.scope=this.scope
7548         }
7549         if (this.valign) {
7550             cfg.valign=this.valign
7551         }
7552         if (this.width) {
7553             cfg.width=this.width
7554         }
7555         
7556         
7557         return cfg;
7558     }
7559    
7560 });
7561
7562  
7563
7564  /*
7565  * - LGPL
7566  *
7567  * table row
7568  * 
7569  */
7570
7571 /**
7572  * @class Roo.bootstrap.TableRow
7573  * @extends Roo.bootstrap.Component
7574  * Bootstrap TableRow class
7575  * @cfg {String} cls row class
7576  * @cfg {String} align Aligns the content in a table row
7577  * @cfg {String} bgcolor Specifies a background color for a table row
7578  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7579  * @cfg {String} valign Vertical aligns the content in a table row
7580  * 
7581  * @constructor
7582  * Create a new TableRow
7583  * @param {Object} config The config object
7584  */
7585
7586 Roo.bootstrap.TableRow = function(config){
7587     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7588 };
7589
7590 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7591     
7592     cls: false,
7593     align: false,
7594     bgcolor: false,
7595     charoff: false,
7596     valign: false,
7597     
7598     getAutoCreate : function(){
7599         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7600         
7601         cfg = {
7602             tag: 'tr'
7603         };
7604             
7605         if(this.cls){
7606             cfg.cls = this.cls;
7607         }
7608         if(this.align){
7609             cfg.align = this.align;
7610         }
7611         if(this.bgcolor){
7612             cfg.bgcolor = this.bgcolor;
7613         }
7614         if(this.charoff){
7615             cfg.charoff = this.charoff;
7616         }
7617         if(this.valign){
7618             cfg.valign = this.valign;
7619         }
7620         
7621         return cfg;
7622     }
7623    
7624 });
7625
7626  
7627
7628  /*
7629  * - LGPL
7630  *
7631  * table body
7632  * 
7633  */
7634
7635 /**
7636  * @class Roo.bootstrap.TableBody
7637  * @extends Roo.bootstrap.Component
7638  * Bootstrap TableBody class
7639  * @cfg {String} cls element class
7640  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7641  * @cfg {String} align Aligns the content inside the element
7642  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7643  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7644  * 
7645  * @constructor
7646  * Create a new TableBody
7647  * @param {Object} config The config object
7648  */
7649
7650 Roo.bootstrap.TableBody = function(config){
7651     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7652 };
7653
7654 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7655     
7656     cls: false,
7657     tag: false,
7658     align: false,
7659     charoff: false,
7660     valign: false,
7661     
7662     getAutoCreate : function(){
7663         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7664         
7665         cfg = {
7666             tag: 'tbody'
7667         };
7668             
7669         if (this.cls) {
7670             cfg.cls=this.cls
7671         }
7672         if(this.tag){
7673             cfg.tag = this.tag;
7674         }
7675         
7676         if(this.align){
7677             cfg.align = this.align;
7678         }
7679         if(this.charoff){
7680             cfg.charoff = this.charoff;
7681         }
7682         if(this.valign){
7683             cfg.valign = this.valign;
7684         }
7685         
7686         return cfg;
7687     }
7688     
7689     
7690 //    initEvents : function()
7691 //    {
7692 //        
7693 //        if(!this.store){
7694 //            return;
7695 //        }
7696 //        
7697 //        this.store = Roo.factory(this.store, Roo.data);
7698 //        this.store.on('load', this.onLoad, this);
7699 //        
7700 //        this.store.load();
7701 //        
7702 //    },
7703 //    
7704 //    onLoad: function () 
7705 //    {   
7706 //        this.fireEvent('load', this);
7707 //    }
7708 //    
7709 //   
7710 });
7711
7712  
7713
7714  /*
7715  * Based on:
7716  * Ext JS Library 1.1.1
7717  * Copyright(c) 2006-2007, Ext JS, LLC.
7718  *
7719  * Originally Released Under LGPL - original licence link has changed is not relivant.
7720  *
7721  * Fork - LGPL
7722  * <script type="text/javascript">
7723  */
7724
7725 // as we use this in bootstrap.
7726 Roo.namespace('Roo.form');
7727  /**
7728  * @class Roo.form.Action
7729  * Internal Class used to handle form actions
7730  * @constructor
7731  * @param {Roo.form.BasicForm} el The form element or its id
7732  * @param {Object} config Configuration options
7733  */
7734
7735  
7736  
7737 // define the action interface
7738 Roo.form.Action = function(form, options){
7739     this.form = form;
7740     this.options = options || {};
7741 };
7742 /**
7743  * Client Validation Failed
7744  * @const 
7745  */
7746 Roo.form.Action.CLIENT_INVALID = 'client';
7747 /**
7748  * Server Validation Failed
7749  * @const 
7750  */
7751 Roo.form.Action.SERVER_INVALID = 'server';
7752  /**
7753  * Connect to Server Failed
7754  * @const 
7755  */
7756 Roo.form.Action.CONNECT_FAILURE = 'connect';
7757 /**
7758  * Reading Data from Server Failed
7759  * @const 
7760  */
7761 Roo.form.Action.LOAD_FAILURE = 'load';
7762
7763 Roo.form.Action.prototype = {
7764     type : 'default',
7765     failureType : undefined,
7766     response : undefined,
7767     result : undefined,
7768
7769     // interface method
7770     run : function(options){
7771
7772     },
7773
7774     // interface method
7775     success : function(response){
7776
7777     },
7778
7779     // interface method
7780     handleResponse : function(response){
7781
7782     },
7783
7784     // default connection failure
7785     failure : function(response){
7786         
7787         this.response = response;
7788         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7789         this.form.afterAction(this, false);
7790     },
7791
7792     processResponse : function(response){
7793         this.response = response;
7794         if(!response.responseText){
7795             return true;
7796         }
7797         this.result = this.handleResponse(response);
7798         return this.result;
7799     },
7800
7801     // utility functions used internally
7802     getUrl : function(appendParams){
7803         var url = this.options.url || this.form.url || this.form.el.dom.action;
7804         if(appendParams){
7805             var p = this.getParams();
7806             if(p){
7807                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7808             }
7809         }
7810         return url;
7811     },
7812
7813     getMethod : function(){
7814         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7815     },
7816
7817     getParams : function(){
7818         var bp = this.form.baseParams;
7819         var p = this.options.params;
7820         if(p){
7821             if(typeof p == "object"){
7822                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7823             }else if(typeof p == 'string' && bp){
7824                 p += '&' + Roo.urlEncode(bp);
7825             }
7826         }else if(bp){
7827             p = Roo.urlEncode(bp);
7828         }
7829         return p;
7830     },
7831
7832     createCallback : function(){
7833         return {
7834             success: this.success,
7835             failure: this.failure,
7836             scope: this,
7837             timeout: (this.form.timeout*1000),
7838             upload: this.form.fileUpload ? this.success : undefined
7839         };
7840     }
7841 };
7842
7843 Roo.form.Action.Submit = function(form, options){
7844     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7845 };
7846
7847 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7848     type : 'submit',
7849
7850     haveProgress : false,
7851     uploadComplete : false,
7852     
7853     // uploadProgress indicator.
7854     uploadProgress : function()
7855     {
7856         if (!this.form.progressUrl) {
7857             return;
7858         }
7859         
7860         if (!this.haveProgress) {
7861             Roo.MessageBox.progress("Uploading", "Uploading");
7862         }
7863         if (this.uploadComplete) {
7864            Roo.MessageBox.hide();
7865            return;
7866         }
7867         
7868         this.haveProgress = true;
7869    
7870         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7871         
7872         var c = new Roo.data.Connection();
7873         c.request({
7874             url : this.form.progressUrl,
7875             params: {
7876                 id : uid
7877             },
7878             method: 'GET',
7879             success : function(req){
7880                //console.log(data);
7881                 var rdata = false;
7882                 var edata;
7883                 try  {
7884                    rdata = Roo.decode(req.responseText)
7885                 } catch (e) {
7886                     Roo.log("Invalid data from server..");
7887                     Roo.log(edata);
7888                     return;
7889                 }
7890                 if (!rdata || !rdata.success) {
7891                     Roo.log(rdata);
7892                     Roo.MessageBox.alert(Roo.encode(rdata));
7893                     return;
7894                 }
7895                 var data = rdata.data;
7896                 
7897                 if (this.uploadComplete) {
7898                    Roo.MessageBox.hide();
7899                    return;
7900                 }
7901                    
7902                 if (data){
7903                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7904                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7905                     );
7906                 }
7907                 this.uploadProgress.defer(2000,this);
7908             },
7909        
7910             failure: function(data) {
7911                 Roo.log('progress url failed ');
7912                 Roo.log(data);
7913             },
7914             scope : this
7915         });
7916            
7917     },
7918     
7919     
7920     run : function()
7921     {
7922         // run get Values on the form, so it syncs any secondary forms.
7923         this.form.getValues();
7924         
7925         var o = this.options;
7926         var method = this.getMethod();
7927         var isPost = method == 'POST';
7928         if(o.clientValidation === false || this.form.isValid()){
7929             
7930             if (this.form.progressUrl) {
7931                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7932                     (new Date() * 1) + '' + Math.random());
7933                     
7934             } 
7935             
7936             
7937             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7938                 form:this.form.el.dom,
7939                 url:this.getUrl(!isPost),
7940                 method: method,
7941                 params:isPost ? this.getParams() : null,
7942                 isUpload: this.form.fileUpload,
7943                 formData : this.form.formData
7944             }));
7945             
7946             this.uploadProgress();
7947
7948         }else if (o.clientValidation !== false){ // client validation failed
7949             this.failureType = Roo.form.Action.CLIENT_INVALID;
7950             this.form.afterAction(this, false);
7951         }
7952     },
7953
7954     success : function(response)
7955     {
7956         this.uploadComplete= true;
7957         if (this.haveProgress) {
7958             Roo.MessageBox.hide();
7959         }
7960         
7961         
7962         var result = this.processResponse(response);
7963         if(result === true || result.success){
7964             this.form.afterAction(this, true);
7965             return;
7966         }
7967         if(result.errors){
7968             this.form.markInvalid(result.errors);
7969             this.failureType = Roo.form.Action.SERVER_INVALID;
7970         }
7971         this.form.afterAction(this, false);
7972     },
7973     failure : function(response)
7974     {
7975         this.uploadComplete= true;
7976         if (this.haveProgress) {
7977             Roo.MessageBox.hide();
7978         }
7979         
7980         this.response = response;
7981         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7982         this.form.afterAction(this, false);
7983     },
7984     
7985     handleResponse : function(response){
7986         if(this.form.errorReader){
7987             var rs = this.form.errorReader.read(response);
7988             var errors = [];
7989             if(rs.records){
7990                 for(var i = 0, len = rs.records.length; i < len; i++) {
7991                     var r = rs.records[i];
7992                     errors[i] = r.data;
7993                 }
7994             }
7995             if(errors.length < 1){
7996                 errors = null;
7997             }
7998             return {
7999                 success : rs.success,
8000                 errors : errors
8001             };
8002         }
8003         var ret = false;
8004         try {
8005             ret = Roo.decode(response.responseText);
8006         } catch (e) {
8007             ret = {
8008                 success: false,
8009                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8010                 errors : []
8011             };
8012         }
8013         return ret;
8014         
8015     }
8016 });
8017
8018
8019 Roo.form.Action.Load = function(form, options){
8020     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8021     this.reader = this.form.reader;
8022 };
8023
8024 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8025     type : 'load',
8026
8027     run : function(){
8028         
8029         Roo.Ajax.request(Roo.apply(
8030                 this.createCallback(), {
8031                     method:this.getMethod(),
8032                     url:this.getUrl(false),
8033                     params:this.getParams()
8034         }));
8035     },
8036
8037     success : function(response){
8038         
8039         var result = this.processResponse(response);
8040         if(result === true || !result.success || !result.data){
8041             this.failureType = Roo.form.Action.LOAD_FAILURE;
8042             this.form.afterAction(this, false);
8043             return;
8044         }
8045         this.form.clearInvalid();
8046         this.form.setValues(result.data);
8047         this.form.afterAction(this, true);
8048     },
8049
8050     handleResponse : function(response){
8051         if(this.form.reader){
8052             var rs = this.form.reader.read(response);
8053             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8054             return {
8055                 success : rs.success,
8056                 data : data
8057             };
8058         }
8059         return Roo.decode(response.responseText);
8060     }
8061 });
8062
8063 Roo.form.Action.ACTION_TYPES = {
8064     'load' : Roo.form.Action.Load,
8065     'submit' : Roo.form.Action.Submit
8066 };/*
8067  * - LGPL
8068  *
8069  * form
8070  *
8071  */
8072
8073 /**
8074  * @class Roo.bootstrap.Form
8075  * @extends Roo.bootstrap.Component
8076  * Bootstrap Form class
8077  * @cfg {String} method  GET | POST (default POST)
8078  * @cfg {String} labelAlign top | left (default top)
8079  * @cfg {String} align left  | right - for navbars
8080  * @cfg {Boolean} loadMask load mask when submit (default true)
8081
8082  *
8083  * @constructor
8084  * Create a new Form
8085  * @param {Object} config The config object
8086  */
8087
8088
8089 Roo.bootstrap.Form = function(config){
8090     
8091     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8092     
8093     Roo.bootstrap.Form.popover.apply();
8094     
8095     this.addEvents({
8096         /**
8097          * @event clientvalidation
8098          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8099          * @param {Form} this
8100          * @param {Boolean} valid true if the form has passed client-side validation
8101          */
8102         clientvalidation: true,
8103         /**
8104          * @event beforeaction
8105          * Fires before any action is performed. Return false to cancel the action.
8106          * @param {Form} this
8107          * @param {Action} action The action to be performed
8108          */
8109         beforeaction: true,
8110         /**
8111          * @event actionfailed
8112          * Fires when an action fails.
8113          * @param {Form} this
8114          * @param {Action} action The action that failed
8115          */
8116         actionfailed : true,
8117         /**
8118          * @event actioncomplete
8119          * Fires when an action is completed.
8120          * @param {Form} this
8121          * @param {Action} action The action that completed
8122          */
8123         actioncomplete : true
8124     });
8125 };
8126
8127 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8128
8129      /**
8130      * @cfg {String} method
8131      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8132      */
8133     method : 'POST',
8134     /**
8135      * @cfg {String} url
8136      * The URL to use for form actions if one isn't supplied in the action options.
8137      */
8138     /**
8139      * @cfg {Boolean} fileUpload
8140      * Set to true if this form is a file upload.
8141      */
8142
8143     /**
8144      * @cfg {Object} baseParams
8145      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8146      */
8147
8148     /**
8149      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8150      */
8151     timeout: 30,
8152     /**
8153      * @cfg {Sting} align (left|right) for navbar forms
8154      */
8155     align : 'left',
8156
8157     // private
8158     activeAction : null,
8159
8160     /**
8161      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8162      * element by passing it or its id or mask the form itself by passing in true.
8163      * @type Mixed
8164      */
8165     waitMsgTarget : false,
8166
8167     loadMask : true,
8168     
8169     /**
8170      * @cfg {Boolean} errorMask (true|false) default false
8171      */
8172     errorMask : false,
8173     
8174     /**
8175      * @cfg {Number} maskOffset Default 100
8176      */
8177     maskOffset : 100,
8178     
8179     /**
8180      * @cfg {Boolean} maskBody
8181      */
8182     maskBody : false,
8183
8184     getAutoCreate : function(){
8185
8186         var cfg = {
8187             tag: 'form',
8188             method : this.method || 'POST',
8189             id : this.id || Roo.id(),
8190             cls : ''
8191         };
8192         if (this.parent().xtype.match(/^Nav/)) {
8193             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8194
8195         }
8196
8197         if (this.labelAlign == 'left' ) {
8198             cfg.cls += ' form-horizontal';
8199         }
8200
8201
8202         return cfg;
8203     },
8204     initEvents : function()
8205     {
8206         this.el.on('submit', this.onSubmit, this);
8207         // this was added as random key presses on the form where triggering form submit.
8208         this.el.on('keypress', function(e) {
8209             if (e.getCharCode() != 13) {
8210                 return true;
8211             }
8212             // we might need to allow it for textareas.. and some other items.
8213             // check e.getTarget().
8214
8215             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8216                 return true;
8217             }
8218
8219             Roo.log("keypress blocked");
8220
8221             e.preventDefault();
8222             return false;
8223         });
8224         
8225     },
8226     // private
8227     onSubmit : function(e){
8228         e.stopEvent();
8229     },
8230
8231      /**
8232      * Returns true if client-side validation on the form is successful.
8233      * @return Boolean
8234      */
8235     isValid : function(){
8236         var items = this.getItems();
8237         var valid = true;
8238         var target = false;
8239         
8240         items.each(function(f){
8241             
8242             if(f.validate()){
8243                 return;
8244             }
8245             
8246             Roo.log('invalid field: ' + f.name);
8247             
8248             valid = false;
8249
8250             if(!target && f.el.isVisible(true)){
8251                 target = f;
8252             }
8253            
8254         });
8255         
8256         if(this.errorMask && !valid){
8257             Roo.bootstrap.Form.popover.mask(this, target);
8258         }
8259         
8260         return valid;
8261     },
8262     
8263     /**
8264      * Returns true if any fields in this form have changed since their original load.
8265      * @return Boolean
8266      */
8267     isDirty : function(){
8268         var dirty = false;
8269         var items = this.getItems();
8270         items.each(function(f){
8271            if(f.isDirty()){
8272                dirty = true;
8273                return false;
8274            }
8275            return true;
8276         });
8277         return dirty;
8278     },
8279      /**
8280      * Performs a predefined action (submit or load) or custom actions you define on this form.
8281      * @param {String} actionName The name of the action type
8282      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8283      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8284      * accept other config options):
8285      * <pre>
8286 Property          Type             Description
8287 ----------------  ---------------  ----------------------------------------------------------------------------------
8288 url               String           The url for the action (defaults to the form's url)
8289 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8290 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8291 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8292                                    validate the form on the client (defaults to false)
8293      * </pre>
8294      * @return {BasicForm} this
8295      */
8296     doAction : function(action, options){
8297         if(typeof action == 'string'){
8298             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8299         }
8300         if(this.fireEvent('beforeaction', this, action) !== false){
8301             this.beforeAction(action);
8302             action.run.defer(100, action);
8303         }
8304         return this;
8305     },
8306
8307     // private
8308     beforeAction : function(action){
8309         var o = action.options;
8310         
8311         if(this.loadMask){
8312             
8313             if(this.maskBody){
8314                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8315             } else {
8316                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8317             }
8318         }
8319         // not really supported yet.. ??
8320
8321         //if(this.waitMsgTarget === true){
8322         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8323         //}else if(this.waitMsgTarget){
8324         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8325         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8326         //}else {
8327         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8328        // }
8329
8330     },
8331
8332     // private
8333     afterAction : function(action, success){
8334         this.activeAction = null;
8335         var o = action.options;
8336
8337         if(this.loadMask){
8338             
8339             if(this.maskBody){
8340                 Roo.get(document.body).unmask();
8341             } else {
8342                 this.el.unmask();
8343             }
8344         }
8345         
8346         //if(this.waitMsgTarget === true){
8347 //            this.el.unmask();
8348         //}else if(this.waitMsgTarget){
8349         //    this.waitMsgTarget.unmask();
8350         //}else{
8351         //    Roo.MessageBox.updateProgress(1);
8352         //    Roo.MessageBox.hide();
8353        // }
8354         //
8355         if(success){
8356             if(o.reset){
8357                 this.reset();
8358             }
8359             Roo.callback(o.success, o.scope, [this, action]);
8360             this.fireEvent('actioncomplete', this, action);
8361
8362         }else{
8363
8364             // failure condition..
8365             // we have a scenario where updates need confirming.
8366             // eg. if a locking scenario exists..
8367             // we look for { errors : { needs_confirm : true }} in the response.
8368             if (
8369                 (typeof(action.result) != 'undefined')  &&
8370                 (typeof(action.result.errors) != 'undefined')  &&
8371                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8372            ){
8373                 var _t = this;
8374                 Roo.log("not supported yet");
8375                  /*
8376
8377                 Roo.MessageBox.confirm(
8378                     "Change requires confirmation",
8379                     action.result.errorMsg,
8380                     function(r) {
8381                         if (r != 'yes') {
8382                             return;
8383                         }
8384                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8385                     }
8386
8387                 );
8388                 */
8389
8390
8391                 return;
8392             }
8393
8394             Roo.callback(o.failure, o.scope, [this, action]);
8395             // show an error message if no failed handler is set..
8396             if (!this.hasListener('actionfailed')) {
8397                 Roo.log("need to add dialog support");
8398                 /*
8399                 Roo.MessageBox.alert("Error",
8400                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8401                         action.result.errorMsg :
8402                         "Saving Failed, please check your entries or try again"
8403                 );
8404                 */
8405             }
8406
8407             this.fireEvent('actionfailed', this, action);
8408         }
8409
8410     },
8411     /**
8412      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8413      * @param {String} id The value to search for
8414      * @return Field
8415      */
8416     findField : function(id){
8417         var items = this.getItems();
8418         var field = items.get(id);
8419         if(!field){
8420              items.each(function(f){
8421                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8422                     field = f;
8423                     return false;
8424                 }
8425                 return true;
8426             });
8427         }
8428         return field || null;
8429     },
8430      /**
8431      * Mark fields in this form invalid in bulk.
8432      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8433      * @return {BasicForm} this
8434      */
8435     markInvalid : function(errors){
8436         if(errors instanceof Array){
8437             for(var i = 0, len = errors.length; i < len; i++){
8438                 var fieldError = errors[i];
8439                 var f = this.findField(fieldError.id);
8440                 if(f){
8441                     f.markInvalid(fieldError.msg);
8442                 }
8443             }
8444         }else{
8445             var field, id;
8446             for(id in errors){
8447                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8448                     field.markInvalid(errors[id]);
8449                 }
8450             }
8451         }
8452         //Roo.each(this.childForms || [], function (f) {
8453         //    f.markInvalid(errors);
8454         //});
8455
8456         return this;
8457     },
8458
8459     /**
8460      * Set values for fields in this form in bulk.
8461      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8462      * @return {BasicForm} this
8463      */
8464     setValues : function(values){
8465         if(values instanceof Array){ // array of objects
8466             for(var i = 0, len = values.length; i < len; i++){
8467                 var v = values[i];
8468                 var f = this.findField(v.id);
8469                 if(f){
8470                     f.setValue(v.value);
8471                     if(this.trackResetOnLoad){
8472                         f.originalValue = f.getValue();
8473                     }
8474                 }
8475             }
8476         }else{ // object hash
8477             var field, id;
8478             for(id in values){
8479                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8480
8481                     if (field.setFromData &&
8482                         field.valueField &&
8483                         field.displayField &&
8484                         // combos' with local stores can
8485                         // be queried via setValue()
8486                         // to set their value..
8487                         (field.store && !field.store.isLocal)
8488                         ) {
8489                         // it's a combo
8490                         var sd = { };
8491                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8492                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8493                         field.setFromData(sd);
8494
8495                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8496                         
8497                         field.setFromData(values);
8498                         
8499                     } else {
8500                         field.setValue(values[id]);
8501                     }
8502
8503
8504                     if(this.trackResetOnLoad){
8505                         field.originalValue = field.getValue();
8506                     }
8507                 }
8508             }
8509         }
8510
8511         //Roo.each(this.childForms || [], function (f) {
8512         //    f.setValues(values);
8513         //});
8514
8515         return this;
8516     },
8517
8518     /**
8519      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8520      * they are returned as an array.
8521      * @param {Boolean} asString
8522      * @return {Object}
8523      */
8524     getValues : function(asString){
8525         //if (this.childForms) {
8526             // copy values from the child forms
8527         //    Roo.each(this.childForms, function (f) {
8528         //        this.setValues(f.getValues());
8529         //    }, this);
8530         //}
8531
8532
8533
8534         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8535         if(asString === true){
8536             return fs;
8537         }
8538         return Roo.urlDecode(fs);
8539     },
8540
8541     /**
8542      * Returns the fields in this form as an object with key/value pairs.
8543      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8544      * @return {Object}
8545      */
8546     getFieldValues : function(with_hidden)
8547     {
8548         var items = this.getItems();
8549         var ret = {};
8550         items.each(function(f){
8551             
8552             if (!f.getName()) {
8553                 return;
8554             }
8555             
8556             var v = f.getValue();
8557             
8558             if (f.inputType =='radio') {
8559                 if (typeof(ret[f.getName()]) == 'undefined') {
8560                     ret[f.getName()] = ''; // empty..
8561                 }
8562
8563                 if (!f.el.dom.checked) {
8564                     return;
8565
8566                 }
8567                 v = f.el.dom.value;
8568
8569             }
8570             
8571             if(f.xtype == 'MoneyField'){
8572                 ret[f.currencyName] = f.getCurrency();
8573             }
8574
8575             // not sure if this supported any more..
8576             if ((typeof(v) == 'object') && f.getRawValue) {
8577                 v = f.getRawValue() ; // dates..
8578             }
8579             // combo boxes where name != hiddenName...
8580             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8581                 ret[f.name] = f.getRawValue();
8582             }
8583             ret[f.getName()] = v;
8584         });
8585
8586         return ret;
8587     },
8588
8589     /**
8590      * Clears all invalid messages in this form.
8591      * @return {BasicForm} this
8592      */
8593     clearInvalid : function(){
8594         var items = this.getItems();
8595
8596         items.each(function(f){
8597            f.clearInvalid();
8598         });
8599
8600         return this;
8601     },
8602
8603     /**
8604      * Resets this form.
8605      * @return {BasicForm} this
8606      */
8607     reset : function(){
8608         var items = this.getItems();
8609         items.each(function(f){
8610             f.reset();
8611         });
8612
8613         Roo.each(this.childForms || [], function (f) {
8614             f.reset();
8615         });
8616
8617
8618         return this;
8619     },
8620     
8621     getItems : function()
8622     {
8623         var r=new Roo.util.MixedCollection(false, function(o){
8624             return o.id || (o.id = Roo.id());
8625         });
8626         var iter = function(el) {
8627             if (el.inputEl) {
8628                 r.add(el);
8629             }
8630             if (!el.items) {
8631                 return;
8632             }
8633             Roo.each(el.items,function(e) {
8634                 iter(e);
8635             });
8636         };
8637
8638         iter(this);
8639         return r;
8640     },
8641     
8642     hideFields : function(items)
8643     {
8644         Roo.each(items, function(i){
8645             
8646             var f = this.findField(i);
8647             
8648             if(!f){
8649                 return;
8650             }
8651             
8652             f.hide();
8653             
8654         }, this);
8655     },
8656     
8657     showFields : function(items)
8658     {
8659         Roo.each(items, function(i){
8660             
8661             var f = this.findField(i);
8662             
8663             if(!f){
8664                 return;
8665             }
8666             
8667             f.show();
8668             
8669         }, this);
8670     }
8671
8672 });
8673
8674 Roo.apply(Roo.bootstrap.Form, {
8675     
8676     popover : {
8677         
8678         padding : 5,
8679         
8680         isApplied : false,
8681         
8682         isMasked : false,
8683         
8684         form : false,
8685         
8686         target : false,
8687         
8688         toolTip : false,
8689         
8690         intervalID : false,
8691         
8692         maskEl : false,
8693         
8694         apply : function()
8695         {
8696             if(this.isApplied){
8697                 return;
8698             }
8699             
8700             this.maskEl = {
8701                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8702                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8703                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8704                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8705             };
8706             
8707             this.maskEl.top.enableDisplayMode("block");
8708             this.maskEl.left.enableDisplayMode("block");
8709             this.maskEl.bottom.enableDisplayMode("block");
8710             this.maskEl.right.enableDisplayMode("block");
8711             
8712             this.toolTip = new Roo.bootstrap.Tooltip({
8713                 cls : 'roo-form-error-popover',
8714                 alignment : {
8715                     'left' : ['r-l', [-2,0], 'right'],
8716                     'right' : ['l-r', [2,0], 'left'],
8717                     'bottom' : ['tl-bl', [0,2], 'top'],
8718                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8719                 }
8720             });
8721             
8722             this.toolTip.render(Roo.get(document.body));
8723
8724             this.toolTip.el.enableDisplayMode("block");
8725             
8726             Roo.get(document.body).on('click', function(){
8727                 this.unmask();
8728             }, this);
8729             
8730             Roo.get(document.body).on('touchstart', function(){
8731                 this.unmask();
8732             }, this);
8733             
8734             this.isApplied = true
8735         },
8736         
8737         mask : function(form, target)
8738         {
8739             this.form = form;
8740             
8741             this.target = target;
8742             
8743             if(!this.form.errorMask || !target.el){
8744                 return;
8745             }
8746             
8747             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8748             
8749             Roo.log(scrollable);
8750             
8751             var ot = this.target.el.calcOffsetsTo(scrollable);
8752             
8753             var scrollTo = ot[1] - this.form.maskOffset;
8754             
8755             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8756             
8757             scrollable.scrollTo('top', scrollTo);
8758             
8759             var box = this.target.el.getBox();
8760             Roo.log(box);
8761             var zIndex = Roo.bootstrap.Modal.zIndex++;
8762
8763             
8764             this.maskEl.top.setStyle('position', 'absolute');
8765             this.maskEl.top.setStyle('z-index', zIndex);
8766             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8767             this.maskEl.top.setLeft(0);
8768             this.maskEl.top.setTop(0);
8769             this.maskEl.top.show();
8770             
8771             this.maskEl.left.setStyle('position', 'absolute');
8772             this.maskEl.left.setStyle('z-index', zIndex);
8773             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8774             this.maskEl.left.setLeft(0);
8775             this.maskEl.left.setTop(box.y - this.padding);
8776             this.maskEl.left.show();
8777
8778             this.maskEl.bottom.setStyle('position', 'absolute');
8779             this.maskEl.bottom.setStyle('z-index', zIndex);
8780             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8781             this.maskEl.bottom.setLeft(0);
8782             this.maskEl.bottom.setTop(box.bottom + this.padding);
8783             this.maskEl.bottom.show();
8784
8785             this.maskEl.right.setStyle('position', 'absolute');
8786             this.maskEl.right.setStyle('z-index', zIndex);
8787             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8788             this.maskEl.right.setLeft(box.right + this.padding);
8789             this.maskEl.right.setTop(box.y - this.padding);
8790             this.maskEl.right.show();
8791
8792             this.toolTip.bindEl = this.target.el;
8793
8794             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8795
8796             var tip = this.target.blankText;
8797
8798             if(this.target.getValue() !== '' ) {
8799                 
8800                 if (this.target.invalidText.length) {
8801                     tip = this.target.invalidText;
8802                 } else if (this.target.regexText.length){
8803                     tip = this.target.regexText;
8804                 }
8805             }
8806
8807             this.toolTip.show(tip);
8808
8809             this.intervalID = window.setInterval(function() {
8810                 Roo.bootstrap.Form.popover.unmask();
8811             }, 10000);
8812
8813             window.onwheel = function(){ return false;};
8814             
8815             (function(){ this.isMasked = true; }).defer(500, this);
8816             
8817         },
8818         
8819         unmask : function()
8820         {
8821             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8822                 return;
8823             }
8824             
8825             this.maskEl.top.setStyle('position', 'absolute');
8826             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8827             this.maskEl.top.hide();
8828
8829             this.maskEl.left.setStyle('position', 'absolute');
8830             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8831             this.maskEl.left.hide();
8832
8833             this.maskEl.bottom.setStyle('position', 'absolute');
8834             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8835             this.maskEl.bottom.hide();
8836
8837             this.maskEl.right.setStyle('position', 'absolute');
8838             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8839             this.maskEl.right.hide();
8840             
8841             this.toolTip.hide();
8842             
8843             this.toolTip.el.hide();
8844             
8845             window.onwheel = function(){ return true;};
8846             
8847             if(this.intervalID){
8848                 window.clearInterval(this.intervalID);
8849                 this.intervalID = false;
8850             }
8851             
8852             this.isMasked = false;
8853             
8854         }
8855         
8856     }
8857     
8858 });
8859
8860 /*
8861  * Based on:
8862  * Ext JS Library 1.1.1
8863  * Copyright(c) 2006-2007, Ext JS, LLC.
8864  *
8865  * Originally Released Under LGPL - original licence link has changed is not relivant.
8866  *
8867  * Fork - LGPL
8868  * <script type="text/javascript">
8869  */
8870 /**
8871  * @class Roo.form.VTypes
8872  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8873  * @singleton
8874  */
8875 Roo.form.VTypes = function(){
8876     // closure these in so they are only created once.
8877     var alpha = /^[a-zA-Z_]+$/;
8878     var alphanum = /^[a-zA-Z0-9_]+$/;
8879     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8880     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8881
8882     // All these messages and functions are configurable
8883     return {
8884         /**
8885          * The function used to validate email addresses
8886          * @param {String} value The email address
8887          */
8888         'email' : function(v){
8889             return email.test(v);
8890         },
8891         /**
8892          * The error text to display when the email validation function returns false
8893          * @type String
8894          */
8895         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8896         /**
8897          * The keystroke filter mask to be applied on email input
8898          * @type RegExp
8899          */
8900         'emailMask' : /[a-z0-9_\.\-@]/i,
8901
8902         /**
8903          * The function used to validate URLs
8904          * @param {String} value The URL
8905          */
8906         'url' : function(v){
8907             return url.test(v);
8908         },
8909         /**
8910          * The error text to display when the url validation function returns false
8911          * @type String
8912          */
8913         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8914         
8915         /**
8916          * The function used to validate alpha values
8917          * @param {String} value The value
8918          */
8919         'alpha' : function(v){
8920             return alpha.test(v);
8921         },
8922         /**
8923          * The error text to display when the alpha validation function returns false
8924          * @type String
8925          */
8926         'alphaText' : 'This field should only contain letters and _',
8927         /**
8928          * The keystroke filter mask to be applied on alpha input
8929          * @type RegExp
8930          */
8931         'alphaMask' : /[a-z_]/i,
8932
8933         /**
8934          * The function used to validate alphanumeric values
8935          * @param {String} value The value
8936          */
8937         'alphanum' : function(v){
8938             return alphanum.test(v);
8939         },
8940         /**
8941          * The error text to display when the alphanumeric validation function returns false
8942          * @type String
8943          */
8944         'alphanumText' : 'This field should only contain letters, numbers and _',
8945         /**
8946          * The keystroke filter mask to be applied on alphanumeric input
8947          * @type RegExp
8948          */
8949         'alphanumMask' : /[a-z0-9_]/i
8950     };
8951 }();/*
8952  * - LGPL
8953  *
8954  * Input
8955  * 
8956  */
8957
8958 /**
8959  * @class Roo.bootstrap.Input
8960  * @extends Roo.bootstrap.Component
8961  * Bootstrap Input class
8962  * @cfg {Boolean} disabled is it disabled
8963  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8964  * @cfg {String} name name of the input
8965  * @cfg {string} fieldLabel - the label associated
8966  * @cfg {string} placeholder - placeholder to put in text.
8967  * @cfg {string}  before - input group add on before
8968  * @cfg {string} after - input group add on after
8969  * @cfg {string} size - (lg|sm) or leave empty..
8970  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8971  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8972  * @cfg {Number} md colspan out of 12 for computer-sized screens
8973  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8974  * @cfg {string} value default value of the input
8975  * @cfg {Number} labelWidth set the width of label 
8976  * @cfg {Number} labellg set the width of label (1-12)
8977  * @cfg {Number} labelmd set the width of label (1-12)
8978  * @cfg {Number} labelsm set the width of label (1-12)
8979  * @cfg {Number} labelxs set the width of label (1-12)
8980  * @cfg {String} labelAlign (top|left)
8981  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8982  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8983  * @cfg {String} indicatorpos (left|right) default left
8984  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8985  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8986
8987  * @cfg {String} align (left|center|right) Default left
8988  * @cfg {Boolean} forceFeedback (true|false) Default false
8989  * 
8990  * @constructor
8991  * Create a new Input
8992  * @param {Object} config The config object
8993  */
8994
8995 Roo.bootstrap.Input = function(config){
8996     
8997     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8998     
8999     this.addEvents({
9000         /**
9001          * @event focus
9002          * Fires when this field receives input focus.
9003          * @param {Roo.form.Field} this
9004          */
9005         focus : true,
9006         /**
9007          * @event blur
9008          * Fires when this field loses input focus.
9009          * @param {Roo.form.Field} this
9010          */
9011         blur : true,
9012         /**
9013          * @event specialkey
9014          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9015          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9016          * @param {Roo.form.Field} this
9017          * @param {Roo.EventObject} e The event object
9018          */
9019         specialkey : true,
9020         /**
9021          * @event change
9022          * Fires just before the field blurs if the field value has changed.
9023          * @param {Roo.form.Field} this
9024          * @param {Mixed} newValue The new value
9025          * @param {Mixed} oldValue The original value
9026          */
9027         change : true,
9028         /**
9029          * @event invalid
9030          * Fires after the field has been marked as invalid.
9031          * @param {Roo.form.Field} this
9032          * @param {String} msg The validation message
9033          */
9034         invalid : true,
9035         /**
9036          * @event valid
9037          * Fires after the field has been validated with no errors.
9038          * @param {Roo.form.Field} this
9039          */
9040         valid : true,
9041          /**
9042          * @event keyup
9043          * Fires after the key up
9044          * @param {Roo.form.Field} this
9045          * @param {Roo.EventObject}  e The event Object
9046          */
9047         keyup : true
9048     });
9049 };
9050
9051 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9052      /**
9053      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9054       automatic validation (defaults to "keyup").
9055      */
9056     validationEvent : "keyup",
9057      /**
9058      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9059      */
9060     validateOnBlur : true,
9061     /**
9062      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9063      */
9064     validationDelay : 250,
9065      /**
9066      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9067      */
9068     focusClass : "x-form-focus",  // not needed???
9069     
9070        
9071     /**
9072      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9073      */
9074     invalidClass : "has-warning",
9075     
9076     /**
9077      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9078      */
9079     validClass : "has-success",
9080     
9081     /**
9082      * @cfg {Boolean} hasFeedback (true|false) default true
9083      */
9084     hasFeedback : true,
9085     
9086     /**
9087      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9088      */
9089     invalidFeedbackClass : "glyphicon-warning-sign",
9090     
9091     /**
9092      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9093      */
9094     validFeedbackClass : "glyphicon-ok",
9095     
9096     /**
9097      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9098      */
9099     selectOnFocus : false,
9100     
9101      /**
9102      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9103      */
9104     maskRe : null,
9105        /**
9106      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9107      */
9108     vtype : null,
9109     
9110       /**
9111      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9112      */
9113     disableKeyFilter : false,
9114     
9115        /**
9116      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9117      */
9118     disabled : false,
9119      /**
9120      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9121      */
9122     allowBlank : true,
9123     /**
9124      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9125      */
9126     blankText : "Please complete this mandatory field",
9127     
9128      /**
9129      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9130      */
9131     minLength : 0,
9132     /**
9133      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9134      */
9135     maxLength : Number.MAX_VALUE,
9136     /**
9137      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9138      */
9139     minLengthText : "The minimum length for this field is {0}",
9140     /**
9141      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9142      */
9143     maxLengthText : "The maximum length for this field is {0}",
9144   
9145     
9146     /**
9147      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9148      * If available, this function will be called only after the basic validators all return true, and will be passed the
9149      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9150      */
9151     validator : null,
9152     /**
9153      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9154      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9155      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9156      */
9157     regex : null,
9158     /**
9159      * @cfg {String} regexText -- Depricated - use Invalid Text
9160      */
9161     regexText : "",
9162     
9163     /**
9164      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9165      */
9166     invalidText : "",
9167     
9168     
9169     
9170     autocomplete: false,
9171     
9172     
9173     fieldLabel : '',
9174     inputType : 'text',
9175     
9176     name : false,
9177     placeholder: false,
9178     before : false,
9179     after : false,
9180     size : false,
9181     hasFocus : false,
9182     preventMark: false,
9183     isFormField : true,
9184     value : '',
9185     labelWidth : 2,
9186     labelAlign : false,
9187     readOnly : false,
9188     align : false,
9189     formatedValue : false,
9190     forceFeedback : false,
9191     
9192     indicatorpos : 'left',
9193     
9194     labellg : 0,
9195     labelmd : 0,
9196     labelsm : 0,
9197     labelxs : 0,
9198     
9199     capture : '',
9200     accept : '',
9201     
9202     parentLabelAlign : function()
9203     {
9204         var parent = this;
9205         while (parent.parent()) {
9206             parent = parent.parent();
9207             if (typeof(parent.labelAlign) !='undefined') {
9208                 return parent.labelAlign;
9209             }
9210         }
9211         return 'left';
9212         
9213     },
9214     
9215     getAutoCreate : function()
9216     {
9217         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9218         
9219         var id = Roo.id();
9220         
9221         var cfg = {};
9222         
9223         if(this.inputType != 'hidden'){
9224             cfg.cls = 'form-group' //input-group
9225         }
9226         
9227         var input =  {
9228             tag: 'input',
9229             id : id,
9230             type : this.inputType,
9231             value : this.value,
9232             cls : 'form-control',
9233             placeholder : this.placeholder || '',
9234             autocomplete : this.autocomplete || 'new-password'
9235         };
9236         
9237         if(this.capture.length){
9238             input.capture = this.capture;
9239         }
9240         
9241         if(this.accept.length){
9242             input.accept = this.accept + "/*";
9243         }
9244         
9245         if(this.align){
9246             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9247         }
9248         
9249         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9250             input.maxLength = this.maxLength;
9251         }
9252         
9253         if (this.disabled) {
9254             input.disabled=true;
9255         }
9256         
9257         if (this.readOnly) {
9258             input.readonly=true;
9259         }
9260         
9261         if (this.name) {
9262             input.name = this.name;
9263         }
9264         
9265         if (this.size) {
9266             input.cls += ' input-' + this.size;
9267         }
9268         
9269         var settings=this;
9270         ['xs','sm','md','lg'].map(function(size){
9271             if (settings[size]) {
9272                 cfg.cls += ' col-' + size + '-' + settings[size];
9273             }
9274         });
9275         
9276         var inputblock = input;
9277         
9278         var feedback = {
9279             tag: 'span',
9280             cls: 'glyphicon form-control-feedback'
9281         };
9282             
9283         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9284             
9285             inputblock = {
9286                 cls : 'has-feedback',
9287                 cn :  [
9288                     input,
9289                     feedback
9290                 ] 
9291             };  
9292         }
9293         
9294         if (this.before || this.after) {
9295             
9296             inputblock = {
9297                 cls : 'input-group',
9298                 cn :  [] 
9299             };
9300             
9301             if (this.before && typeof(this.before) == 'string') {
9302                 
9303                 inputblock.cn.push({
9304                     tag :'span',
9305                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9306                     html : this.before
9307                 });
9308             }
9309             if (this.before && typeof(this.before) == 'object') {
9310                 this.before = Roo.factory(this.before);
9311                 
9312                 inputblock.cn.push({
9313                     tag :'span',
9314                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9315                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9316                 });
9317             }
9318             
9319             inputblock.cn.push(input);
9320             
9321             if (this.after && typeof(this.after) == 'string') {
9322                 inputblock.cn.push({
9323                     tag :'span',
9324                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9325                     html : this.after
9326                 });
9327             }
9328             if (this.after && typeof(this.after) == 'object') {
9329                 this.after = Roo.factory(this.after);
9330                 
9331                 inputblock.cn.push({
9332                     tag :'span',
9333                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9334                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9335                 });
9336             }
9337             
9338             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9339                 inputblock.cls += ' has-feedback';
9340                 inputblock.cn.push(feedback);
9341             }
9342         };
9343         var indicator = {
9344             tag : 'i',
9345             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9346             tooltip : 'This field is required'
9347         };
9348         if (Roo.bootstrap.version == 4) {
9349             indicator = {
9350                 tag : 'i',
9351                 style : 'display-none'
9352             };
9353         }
9354         if (align ==='left' && this.fieldLabel.length) {
9355             
9356             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9357             
9358             cfg.cn = [
9359                 indicator,
9360                 {
9361                     tag: 'label',
9362                     'for' :  id,
9363                     cls : 'control-label col-form-label',
9364                     html : this.fieldLabel
9365
9366                 },
9367                 {
9368                     cls : "", 
9369                     cn: [
9370                         inputblock
9371                     ]
9372                 }
9373             ];
9374             
9375             var labelCfg = cfg.cn[1];
9376             var contentCfg = cfg.cn[2];
9377             
9378             if(this.indicatorpos == 'right'){
9379                 cfg.cn = [
9380                     {
9381                         tag: 'label',
9382                         'for' :  id,
9383                         cls : 'control-label col-form-label',
9384                         cn : [
9385                             {
9386                                 tag : 'span',
9387                                 html : this.fieldLabel
9388                             },
9389                             indicator
9390                         ]
9391                     },
9392                     {
9393                         cls : "",
9394                         cn: [
9395                             inputblock
9396                         ]
9397                     }
9398
9399                 ];
9400                 
9401                 labelCfg = cfg.cn[0];
9402                 contentCfg = cfg.cn[1];
9403             
9404             }
9405             
9406             if(this.labelWidth > 12){
9407                 labelCfg.style = "width: " + this.labelWidth + 'px';
9408             }
9409             
9410             if(this.labelWidth < 13 && this.labelmd == 0){
9411                 this.labelmd = this.labelWidth;
9412             }
9413             
9414             if(this.labellg > 0){
9415                 labelCfg.cls += ' col-lg-' + this.labellg;
9416                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9417             }
9418             
9419             if(this.labelmd > 0){
9420                 labelCfg.cls += ' col-md-' + this.labelmd;
9421                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9422             }
9423             
9424             if(this.labelsm > 0){
9425                 labelCfg.cls += ' col-sm-' + this.labelsm;
9426                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9427             }
9428             
9429             if(this.labelxs > 0){
9430                 labelCfg.cls += ' col-xs-' + this.labelxs;
9431                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9432             }
9433             
9434             
9435         } else if ( this.fieldLabel.length) {
9436                 
9437             cfg.cn = [
9438                 {
9439                     tag : 'i',
9440                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9441                     tooltip : 'This field is required'
9442                 },
9443                 {
9444                     tag: 'label',
9445                    //cls : 'input-group-addon',
9446                     html : this.fieldLabel
9447
9448                 },
9449
9450                inputblock
9451
9452            ];
9453            
9454            if(this.indicatorpos == 'right'){
9455                 
9456                 cfg.cn = [
9457                     {
9458                         tag: 'label',
9459                        //cls : 'input-group-addon',
9460                         html : this.fieldLabel
9461
9462                     },
9463                     {
9464                         tag : 'i',
9465                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9466                         tooltip : 'This field is required'
9467                     },
9468
9469                    inputblock
9470
9471                ];
9472
9473             }
9474
9475         } else {
9476             
9477             cfg.cn = [
9478
9479                     inputblock
9480
9481             ];
9482                 
9483                 
9484         };
9485         
9486         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9487            cfg.cls += ' navbar-form';
9488         }
9489         
9490         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9491             // on BS4 we do this only if not form 
9492             cfg.cls += ' navbar-form';
9493             cfg.tag = 'li';
9494         }
9495         
9496         return cfg;
9497         
9498     },
9499     /**
9500      * return the real input element.
9501      */
9502     inputEl: function ()
9503     {
9504         return this.el.select('input.form-control',true).first();
9505     },
9506     
9507     tooltipEl : function()
9508     {
9509         return this.inputEl();
9510     },
9511     
9512     indicatorEl : function()
9513     {
9514         if (Roo.bootstrap.version == 4) {
9515             return false; // not enabled in v4 yet.
9516         }
9517         
9518         var indicator = this.el.select('i.roo-required-indicator',true).first();
9519         
9520         if(!indicator){
9521             return false;
9522         }
9523         
9524         return indicator;
9525         
9526     },
9527     
9528     setDisabled : function(v)
9529     {
9530         var i  = this.inputEl().dom;
9531         if (!v) {
9532             i.removeAttribute('disabled');
9533             return;
9534             
9535         }
9536         i.setAttribute('disabled','true');
9537     },
9538     initEvents : function()
9539     {
9540           
9541         this.inputEl().on("keydown" , this.fireKey,  this);
9542         this.inputEl().on("focus", this.onFocus,  this);
9543         this.inputEl().on("blur", this.onBlur,  this);
9544         
9545         this.inputEl().relayEvent('keyup', this);
9546         
9547         this.indicator = this.indicatorEl();
9548         
9549         if(this.indicator){
9550             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9551         }
9552  
9553         // reference to original value for reset
9554         this.originalValue = this.getValue();
9555         //Roo.form.TextField.superclass.initEvents.call(this);
9556         if(this.validationEvent == 'keyup'){
9557             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9558             this.inputEl().on('keyup', this.filterValidation, this);
9559         }
9560         else if(this.validationEvent !== false){
9561             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9562         }
9563         
9564         if(this.selectOnFocus){
9565             this.on("focus", this.preFocus, this);
9566             
9567         }
9568         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9569             this.inputEl().on("keypress", this.filterKeys, this);
9570         } else {
9571             this.inputEl().relayEvent('keypress', this);
9572         }
9573        /* if(this.grow){
9574             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9575             this.el.on("click", this.autoSize,  this);
9576         }
9577         */
9578         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9579             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9580         }
9581         
9582         if (typeof(this.before) == 'object') {
9583             this.before.render(this.el.select('.roo-input-before',true).first());
9584         }
9585         if (typeof(this.after) == 'object') {
9586             this.after.render(this.el.select('.roo-input-after',true).first());
9587         }
9588         
9589         this.inputEl().on('change', this.onChange, this);
9590         
9591     },
9592     filterValidation : function(e){
9593         if(!e.isNavKeyPress()){
9594             this.validationTask.delay(this.validationDelay);
9595         }
9596     },
9597      /**
9598      * Validates the field value
9599      * @return {Boolean} True if the value is valid, else false
9600      */
9601     validate : function(){
9602         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9603         if(this.disabled || this.validateValue(this.getRawValue())){
9604             this.markValid();
9605             return true;
9606         }
9607         
9608         this.markInvalid();
9609         return false;
9610     },
9611     
9612     
9613     /**
9614      * Validates a value according to the field's validation rules and marks the field as invalid
9615      * if the validation fails
9616      * @param {Mixed} value The value to validate
9617      * @return {Boolean} True if the value is valid, else false
9618      */
9619     validateValue : function(value)
9620     {
9621         if(this.getVisibilityEl().hasClass('hidden')){
9622             return true;
9623         }
9624         
9625         if(value.length < 1)  { // if it's blank
9626             if(this.allowBlank){
9627                 return true;
9628             }
9629             return false;
9630         }
9631         
9632         if(value.length < this.minLength){
9633             return false;
9634         }
9635         if(value.length > this.maxLength){
9636             return false;
9637         }
9638         if(this.vtype){
9639             var vt = Roo.form.VTypes;
9640             if(!vt[this.vtype](value, this)){
9641                 return false;
9642             }
9643         }
9644         if(typeof this.validator == "function"){
9645             var msg = this.validator(value);
9646             if(msg !== true){
9647                 return false;
9648             }
9649             if (typeof(msg) == 'string') {
9650                 this.invalidText = msg;
9651             }
9652         }
9653         
9654         if(this.regex && !this.regex.test(value)){
9655             return false;
9656         }
9657         
9658         return true;
9659     },
9660     
9661      // private
9662     fireKey : function(e){
9663         //Roo.log('field ' + e.getKey());
9664         if(e.isNavKeyPress()){
9665             this.fireEvent("specialkey", this, e);
9666         }
9667     },
9668     focus : function (selectText){
9669         if(this.rendered){
9670             this.inputEl().focus();
9671             if(selectText === true){
9672                 this.inputEl().dom.select();
9673             }
9674         }
9675         return this;
9676     } ,
9677     
9678     onFocus : function(){
9679         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9680            // this.el.addClass(this.focusClass);
9681         }
9682         if(!this.hasFocus){
9683             this.hasFocus = true;
9684             this.startValue = this.getValue();
9685             this.fireEvent("focus", this);
9686         }
9687     },
9688     
9689     beforeBlur : Roo.emptyFn,
9690
9691     
9692     // private
9693     onBlur : function(){
9694         this.beforeBlur();
9695         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9696             //this.el.removeClass(this.focusClass);
9697         }
9698         this.hasFocus = false;
9699         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9700             this.validate();
9701         }
9702         var v = this.getValue();
9703         if(String(v) !== String(this.startValue)){
9704             this.fireEvent('change', this, v, this.startValue);
9705         }
9706         this.fireEvent("blur", this);
9707     },
9708     
9709     onChange : function(e)
9710     {
9711         var v = this.getValue();
9712         if(String(v) !== String(this.startValue)){
9713             this.fireEvent('change', this, v, this.startValue);
9714         }
9715         
9716     },
9717     
9718     /**
9719      * Resets the current field value to the originally loaded value and clears any validation messages
9720      */
9721     reset : function(){
9722         this.setValue(this.originalValue);
9723         this.validate();
9724     },
9725      /**
9726      * Returns the name of the field
9727      * @return {Mixed} name The name field
9728      */
9729     getName: function(){
9730         return this.name;
9731     },
9732      /**
9733      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9734      * @return {Mixed} value The field value
9735      */
9736     getValue : function(){
9737         
9738         var v = this.inputEl().getValue();
9739         
9740         return v;
9741     },
9742     /**
9743      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9744      * @return {Mixed} value The field value
9745      */
9746     getRawValue : function(){
9747         var v = this.inputEl().getValue();
9748         
9749         return v;
9750     },
9751     
9752     /**
9753      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9754      * @param {Mixed} value The value to set
9755      */
9756     setRawValue : function(v){
9757         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9758     },
9759     
9760     selectText : function(start, end){
9761         var v = this.getRawValue();
9762         if(v.length > 0){
9763             start = start === undefined ? 0 : start;
9764             end = end === undefined ? v.length : end;
9765             var d = this.inputEl().dom;
9766             if(d.setSelectionRange){
9767                 d.setSelectionRange(start, end);
9768             }else if(d.createTextRange){
9769                 var range = d.createTextRange();
9770                 range.moveStart("character", start);
9771                 range.moveEnd("character", v.length-end);
9772                 range.select();
9773             }
9774         }
9775     },
9776     
9777     /**
9778      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9779      * @param {Mixed} value The value to set
9780      */
9781     setValue : function(v){
9782         this.value = v;
9783         if(this.rendered){
9784             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9785             this.validate();
9786         }
9787     },
9788     
9789     /*
9790     processValue : function(value){
9791         if(this.stripCharsRe){
9792             var newValue = value.replace(this.stripCharsRe, '');
9793             if(newValue !== value){
9794                 this.setRawValue(newValue);
9795                 return newValue;
9796             }
9797         }
9798         return value;
9799     },
9800   */
9801     preFocus : function(){
9802         
9803         if(this.selectOnFocus){
9804             this.inputEl().dom.select();
9805         }
9806     },
9807     filterKeys : function(e){
9808         var k = e.getKey();
9809         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9810             return;
9811         }
9812         var c = e.getCharCode(), cc = String.fromCharCode(c);
9813         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9814             return;
9815         }
9816         if(!this.maskRe.test(cc)){
9817             e.stopEvent();
9818         }
9819     },
9820      /**
9821      * Clear any invalid styles/messages for this field
9822      */
9823     clearInvalid : function(){
9824         
9825         if(!this.el || this.preventMark){ // not rendered
9826             return;
9827         }
9828         
9829         
9830         this.el.removeClass([this.invalidClass, 'is-invalid']);
9831         
9832         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9833             
9834             var feedback = this.el.select('.form-control-feedback', true).first();
9835             
9836             if(feedback){
9837                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9838             }
9839             
9840         }
9841         
9842         if(this.indicator){
9843             this.indicator.removeClass('visible');
9844             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9845         }
9846         
9847         this.fireEvent('valid', this);
9848     },
9849     
9850      /**
9851      * Mark this field as valid
9852      */
9853     markValid : function()
9854     {
9855         if(!this.el  || this.preventMark){ // not rendered...
9856             return;
9857         }
9858         
9859         this.el.removeClass([this.invalidClass, this.validClass]);
9860         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9861
9862         var feedback = this.el.select('.form-control-feedback', true).first();
9863             
9864         if(feedback){
9865             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9866         }
9867         
9868         if(this.indicator){
9869             this.indicator.removeClass('visible');
9870             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9871         }
9872         
9873         if(this.disabled){
9874             return;
9875         }
9876         
9877         if(this.allowBlank && !this.getRawValue().length){
9878             return;
9879         }
9880         if (Roo.bootstrap.version == 3) {
9881             this.el.addClass(this.validClass);
9882         } else {
9883             this.inputEl().addClass('is-valid');
9884         }
9885
9886         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9887             
9888             var feedback = this.el.select('.form-control-feedback', true).first();
9889             
9890             if(feedback){
9891                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9892                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9893             }
9894             
9895         }
9896         
9897         this.fireEvent('valid', this);
9898     },
9899     
9900      /**
9901      * Mark this field as invalid
9902      * @param {String} msg The validation message
9903      */
9904     markInvalid : function(msg)
9905     {
9906         if(!this.el  || this.preventMark){ // not rendered
9907             return;
9908         }
9909         
9910         this.el.removeClass([this.invalidClass, this.validClass]);
9911         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9912         
9913         var feedback = this.el.select('.form-control-feedback', true).first();
9914             
9915         if(feedback){
9916             this.el.select('.form-control-feedback', true).first().removeClass(
9917                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9918         }
9919
9920         if(this.disabled){
9921             return;
9922         }
9923         
9924         if(this.allowBlank && !this.getRawValue().length){
9925             return;
9926         }
9927         
9928         if(this.indicator){
9929             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9930             this.indicator.addClass('visible');
9931         }
9932         if (Roo.bootstrap.version == 3) {
9933             this.el.addClass(this.invalidClass);
9934         } else {
9935             this.inputEl().addClass('is-invalid');
9936         }
9937         
9938         
9939         
9940         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9941             
9942             var feedback = this.el.select('.form-control-feedback', true).first();
9943             
9944             if(feedback){
9945                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9946                 
9947                 if(this.getValue().length || this.forceFeedback){
9948                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9949                 }
9950                 
9951             }
9952             
9953         }
9954         
9955         this.fireEvent('invalid', this, msg);
9956     },
9957     // private
9958     SafariOnKeyDown : function(event)
9959     {
9960         // this is a workaround for a password hang bug on chrome/ webkit.
9961         if (this.inputEl().dom.type != 'password') {
9962             return;
9963         }
9964         
9965         var isSelectAll = false;
9966         
9967         if(this.inputEl().dom.selectionEnd > 0){
9968             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9969         }
9970         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9971             event.preventDefault();
9972             this.setValue('');
9973             return;
9974         }
9975         
9976         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9977             
9978             event.preventDefault();
9979             // this is very hacky as keydown always get's upper case.
9980             //
9981             var cc = String.fromCharCode(event.getCharCode());
9982             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9983             
9984         }
9985     },
9986     adjustWidth : function(tag, w){
9987         tag = tag.toLowerCase();
9988         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9989             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9990                 if(tag == 'input'){
9991                     return w + 2;
9992                 }
9993                 if(tag == 'textarea'){
9994                     return w-2;
9995                 }
9996             }else if(Roo.isOpera){
9997                 if(tag == 'input'){
9998                     return w + 2;
9999                 }
10000                 if(tag == 'textarea'){
10001                     return w-2;
10002                 }
10003             }
10004         }
10005         return w;
10006     },
10007     
10008     setFieldLabel : function(v)
10009     {
10010         if(!this.rendered){
10011             return;
10012         }
10013         
10014         if(this.indicatorEl()){
10015             var ar = this.el.select('label > span',true);
10016             
10017             if (ar.elements.length) {
10018                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10019                 this.fieldLabel = v;
10020                 return;
10021             }
10022             
10023             var br = this.el.select('label',true);
10024             
10025             if(br.elements.length) {
10026                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10027                 this.fieldLabel = v;
10028                 return;
10029             }
10030             
10031             Roo.log('Cannot Found any of label > span || label in input');
10032             return;
10033         }
10034         
10035         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10036         this.fieldLabel = v;
10037         
10038         
10039     }
10040 });
10041
10042  
10043 /*
10044  * - LGPL
10045  *
10046  * Input
10047  * 
10048  */
10049
10050 /**
10051  * @class Roo.bootstrap.TextArea
10052  * @extends Roo.bootstrap.Input
10053  * Bootstrap TextArea class
10054  * @cfg {Number} cols Specifies the visible width of a text area
10055  * @cfg {Number} rows Specifies the visible number of lines in a text area
10056  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10057  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10058  * @cfg {string} html text
10059  * 
10060  * @constructor
10061  * Create a new TextArea
10062  * @param {Object} config The config object
10063  */
10064
10065 Roo.bootstrap.TextArea = function(config){
10066     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10067    
10068 };
10069
10070 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10071      
10072     cols : false,
10073     rows : 5,
10074     readOnly : false,
10075     warp : 'soft',
10076     resize : false,
10077     value: false,
10078     html: false,
10079     
10080     getAutoCreate : function(){
10081         
10082         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10083         
10084         var id = Roo.id();
10085         
10086         var cfg = {};
10087         
10088         if(this.inputType != 'hidden'){
10089             cfg.cls = 'form-group' //input-group
10090         }
10091         
10092         var input =  {
10093             tag: 'textarea',
10094             id : id,
10095             warp : this.warp,
10096             rows : this.rows,
10097             value : this.value || '',
10098             html: this.html || '',
10099             cls : 'form-control',
10100             placeholder : this.placeholder || '' 
10101             
10102         };
10103         
10104         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10105             input.maxLength = this.maxLength;
10106         }
10107         
10108         if(this.resize){
10109             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10110         }
10111         
10112         if(this.cols){
10113             input.cols = this.cols;
10114         }
10115         
10116         if (this.readOnly) {
10117             input.readonly = true;
10118         }
10119         
10120         if (this.name) {
10121             input.name = this.name;
10122         }
10123         
10124         if (this.size) {
10125             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10126         }
10127         
10128         var settings=this;
10129         ['xs','sm','md','lg'].map(function(size){
10130             if (settings[size]) {
10131                 cfg.cls += ' col-' + size + '-' + settings[size];
10132             }
10133         });
10134         
10135         var inputblock = input;
10136         
10137         if(this.hasFeedback && !this.allowBlank){
10138             
10139             var feedback = {
10140                 tag: 'span',
10141                 cls: 'glyphicon form-control-feedback'
10142             };
10143
10144             inputblock = {
10145                 cls : 'has-feedback',
10146                 cn :  [
10147                     input,
10148                     feedback
10149                 ] 
10150             };  
10151         }
10152         
10153         
10154         if (this.before || this.after) {
10155             
10156             inputblock = {
10157                 cls : 'input-group',
10158                 cn :  [] 
10159             };
10160             if (this.before) {
10161                 inputblock.cn.push({
10162                     tag :'span',
10163                     cls : 'input-group-addon',
10164                     html : this.before
10165                 });
10166             }
10167             
10168             inputblock.cn.push(input);
10169             
10170             if(this.hasFeedback && !this.allowBlank){
10171                 inputblock.cls += ' has-feedback';
10172                 inputblock.cn.push(feedback);
10173             }
10174             
10175             if (this.after) {
10176                 inputblock.cn.push({
10177                     tag :'span',
10178                     cls : 'input-group-addon',
10179                     html : this.after
10180                 });
10181             }
10182             
10183         }
10184         
10185         if (align ==='left' && this.fieldLabel.length) {
10186             cfg.cn = [
10187                 {
10188                     tag: 'label',
10189                     'for' :  id,
10190                     cls : 'control-label',
10191                     html : this.fieldLabel
10192                 },
10193                 {
10194                     cls : "",
10195                     cn: [
10196                         inputblock
10197                     ]
10198                 }
10199
10200             ];
10201             
10202             if(this.labelWidth > 12){
10203                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10204             }
10205
10206             if(this.labelWidth < 13 && this.labelmd == 0){
10207                 this.labelmd = this.labelWidth;
10208             }
10209
10210             if(this.labellg > 0){
10211                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10212                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10213             }
10214
10215             if(this.labelmd > 0){
10216                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10217                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10218             }
10219
10220             if(this.labelsm > 0){
10221                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10222                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10223             }
10224
10225             if(this.labelxs > 0){
10226                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10227                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10228             }
10229             
10230         } else if ( this.fieldLabel.length) {
10231             cfg.cn = [
10232
10233                {
10234                    tag: 'label',
10235                    //cls : 'input-group-addon',
10236                    html : this.fieldLabel
10237
10238                },
10239
10240                inputblock
10241
10242            ];
10243
10244         } else {
10245
10246             cfg.cn = [
10247
10248                 inputblock
10249
10250             ];
10251                 
10252         }
10253         
10254         if (this.disabled) {
10255             input.disabled=true;
10256         }
10257         
10258         return cfg;
10259         
10260     },
10261     /**
10262      * return the real textarea element.
10263      */
10264     inputEl: function ()
10265     {
10266         return this.el.select('textarea.form-control',true).first();
10267     },
10268     
10269     /**
10270      * Clear any invalid styles/messages for this field
10271      */
10272     clearInvalid : function()
10273     {
10274         
10275         if(!this.el || this.preventMark){ // not rendered
10276             return;
10277         }
10278         
10279         var label = this.el.select('label', true).first();
10280         var icon = this.el.select('i.fa-star', true).first();
10281         
10282         if(label && icon){
10283             icon.remove();
10284         }
10285         this.el.removeClass( this.validClass);
10286         this.inputEl().removeClass('is-invalid');
10287          
10288         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10289             
10290             var feedback = this.el.select('.form-control-feedback', true).first();
10291             
10292             if(feedback){
10293                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10294             }
10295             
10296         }
10297         
10298         this.fireEvent('valid', this);
10299     },
10300     
10301      /**
10302      * Mark this field as valid
10303      */
10304     markValid : function()
10305     {
10306         if(!this.el  || this.preventMark){ // not rendered
10307             return;
10308         }
10309         
10310         this.el.removeClass([this.invalidClass, this.validClass]);
10311         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10312         
10313         var feedback = this.el.select('.form-control-feedback', true).first();
10314             
10315         if(feedback){
10316             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10317         }
10318
10319         if(this.disabled || this.allowBlank){
10320             return;
10321         }
10322         
10323         var label = this.el.select('label', true).first();
10324         var icon = this.el.select('i.fa-star', true).first();
10325         
10326         if(label && icon){
10327             icon.remove();
10328         }
10329         if (Roo.bootstrap.version == 3) {
10330             this.el.addClass(this.validClass);
10331         } else {
10332             this.inputEl().addClass('is-valid');
10333         }
10334         
10335         
10336         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10337             
10338             var feedback = this.el.select('.form-control-feedback', true).first();
10339             
10340             if(feedback){
10341                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10342                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10343             }
10344             
10345         }
10346         
10347         this.fireEvent('valid', this);
10348     },
10349     
10350      /**
10351      * Mark this field as invalid
10352      * @param {String} msg The validation message
10353      */
10354     markInvalid : function(msg)
10355     {
10356         if(!this.el  || this.preventMark){ // not rendered
10357             return;
10358         }
10359         
10360         this.el.removeClass([this.invalidClass, this.validClass]);
10361         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10362         
10363         var feedback = this.el.select('.form-control-feedback', true).first();
10364             
10365         if(feedback){
10366             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10367         }
10368
10369         if(this.disabled || this.allowBlank){
10370             return;
10371         }
10372         
10373         var label = this.el.select('label', true).first();
10374         var icon = this.el.select('i.fa-star', true).first();
10375         
10376         if(!this.getValue().length && label && !icon){
10377             this.el.createChild({
10378                 tag : 'i',
10379                 cls : 'text-danger fa fa-lg fa-star',
10380                 tooltip : 'This field is required',
10381                 style : 'margin-right:5px;'
10382             }, label, true);
10383         }
10384         
10385         if (Roo.bootstrap.version == 3) {
10386             this.el.addClass(this.invalidClass);
10387         } else {
10388             this.inputEl().addClass('is-invalid');
10389         }
10390         
10391         // fixme ... this may be depricated need to test..
10392         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10393             
10394             var feedback = this.el.select('.form-control-feedback', true).first();
10395             
10396             if(feedback){
10397                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10398                 
10399                 if(this.getValue().length || this.forceFeedback){
10400                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10401                 }
10402                 
10403             }
10404             
10405         }
10406         
10407         this.fireEvent('invalid', this, msg);
10408     }
10409 });
10410
10411  
10412 /*
10413  * - LGPL
10414  *
10415  * trigger field - base class for combo..
10416  * 
10417  */
10418  
10419 /**
10420  * @class Roo.bootstrap.TriggerField
10421  * @extends Roo.bootstrap.Input
10422  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10423  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10424  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10425  * for which you can provide a custom implementation.  For example:
10426  * <pre><code>
10427 var trigger = new Roo.bootstrap.TriggerField();
10428 trigger.onTriggerClick = myTriggerFn;
10429 trigger.applyTo('my-field');
10430 </code></pre>
10431  *
10432  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10433  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10434  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10435  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10436  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10437
10438  * @constructor
10439  * Create a new TriggerField.
10440  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10441  * to the base TextField)
10442  */
10443 Roo.bootstrap.TriggerField = function(config){
10444     this.mimicing = false;
10445     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10446 };
10447
10448 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10449     /**
10450      * @cfg {String} triggerClass A CSS class to apply to the trigger
10451      */
10452      /**
10453      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10454      */
10455     hideTrigger:false,
10456
10457     /**
10458      * @cfg {Boolean} removable (true|false) special filter default false
10459      */
10460     removable : false,
10461     
10462     /** @cfg {Boolean} grow @hide */
10463     /** @cfg {Number} growMin @hide */
10464     /** @cfg {Number} growMax @hide */
10465
10466     /**
10467      * @hide 
10468      * @method
10469      */
10470     autoSize: Roo.emptyFn,
10471     // private
10472     monitorTab : true,
10473     // private
10474     deferHeight : true,
10475
10476     
10477     actionMode : 'wrap',
10478     
10479     caret : false,
10480     
10481     
10482     getAutoCreate : function(){
10483        
10484         var align = this.labelAlign || this.parentLabelAlign();
10485         
10486         var id = Roo.id();
10487         
10488         var cfg = {
10489             cls: 'form-group' //input-group
10490         };
10491         
10492         
10493         var input =  {
10494             tag: 'input',
10495             id : id,
10496             type : this.inputType,
10497             cls : 'form-control',
10498             autocomplete: 'new-password',
10499             placeholder : this.placeholder || '' 
10500             
10501         };
10502         if (this.name) {
10503             input.name = this.name;
10504         }
10505         if (this.size) {
10506             input.cls += ' input-' + this.size;
10507         }
10508         
10509         if (this.disabled) {
10510             input.disabled=true;
10511         }
10512         
10513         var inputblock = input;
10514         
10515         if(this.hasFeedback && !this.allowBlank){
10516             
10517             var feedback = {
10518                 tag: 'span',
10519                 cls: 'glyphicon form-control-feedback'
10520             };
10521             
10522             if(this.removable && !this.editable && !this.tickable){
10523                 inputblock = {
10524                     cls : 'has-feedback',
10525                     cn :  [
10526                         inputblock,
10527                         {
10528                             tag: 'button',
10529                             html : 'x',
10530                             cls : 'roo-combo-removable-btn close'
10531                         },
10532                         feedback
10533                     ] 
10534                 };
10535             } else {
10536                 inputblock = {
10537                     cls : 'has-feedback',
10538                     cn :  [
10539                         inputblock,
10540                         feedback
10541                     ] 
10542                 };
10543             }
10544
10545         } else {
10546             if(this.removable && !this.editable && !this.tickable){
10547                 inputblock = {
10548                     cls : 'roo-removable',
10549                     cn :  [
10550                         inputblock,
10551                         {
10552                             tag: 'button',
10553                             html : 'x',
10554                             cls : 'roo-combo-removable-btn close'
10555                         }
10556                     ] 
10557                 };
10558             }
10559         }
10560         
10561         if (this.before || this.after) {
10562             
10563             inputblock = {
10564                 cls : 'input-group',
10565                 cn :  [] 
10566             };
10567             if (this.before) {
10568                 inputblock.cn.push({
10569                     tag :'span',
10570                     cls : 'input-group-addon input-group-prepend input-group-text',
10571                     html : this.before
10572                 });
10573             }
10574             
10575             inputblock.cn.push(input);
10576             
10577             if(this.hasFeedback && !this.allowBlank){
10578                 inputblock.cls += ' has-feedback';
10579                 inputblock.cn.push(feedback);
10580             }
10581             
10582             if (this.after) {
10583                 inputblock.cn.push({
10584                     tag :'span',
10585                     cls : 'input-group-addon input-group-append input-group-text',
10586                     html : this.after
10587                 });
10588             }
10589             
10590         };
10591         
10592       
10593         
10594         var ibwrap = inputblock;
10595         
10596         if(this.multiple){
10597             ibwrap = {
10598                 tag: 'ul',
10599                 cls: 'roo-select2-choices',
10600                 cn:[
10601                     {
10602                         tag: 'li',
10603                         cls: 'roo-select2-search-field',
10604                         cn: [
10605
10606                             inputblock
10607                         ]
10608                     }
10609                 ]
10610             };
10611                 
10612         }
10613         
10614         var combobox = {
10615             cls: 'roo-select2-container input-group',
10616             cn: [
10617                  {
10618                     tag: 'input',
10619                     type : 'hidden',
10620                     cls: 'form-hidden-field'
10621                 },
10622                 ibwrap
10623             ]
10624         };
10625         
10626         if(!this.multiple && this.showToggleBtn){
10627             
10628             var caret = {
10629                         tag: 'span',
10630                         cls: 'caret'
10631              };
10632             if (this.caret != false) {
10633                 caret = {
10634                      tag: 'i',
10635                      cls: 'fa fa-' + this.caret
10636                 };
10637                 
10638             }
10639             
10640             combobox.cn.push({
10641                 tag :'span',
10642                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10643                 cn : [
10644                     caret,
10645                     {
10646                         tag: 'span',
10647                         cls: 'combobox-clear',
10648                         cn  : [
10649                             {
10650                                 tag : 'i',
10651                                 cls: 'icon-remove'
10652                             }
10653                         ]
10654                     }
10655                 ]
10656
10657             })
10658         }
10659         
10660         if(this.multiple){
10661             combobox.cls += ' roo-select2-container-multi';
10662         }
10663          var indicator = {
10664             tag : 'i',
10665             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10666             tooltip : 'This field is required'
10667         };
10668         if (Roo.bootstrap.version == 4) {
10669             indicator = {
10670                 tag : 'i',
10671                 style : 'display:none'
10672             };
10673         }
10674         
10675         
10676         if (align ==='left' && this.fieldLabel.length) {
10677             
10678             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10679
10680             cfg.cn = [
10681                 indicator,
10682                 {
10683                     tag: 'label',
10684                     'for' :  id,
10685                     cls : 'control-label',
10686                     html : this.fieldLabel
10687
10688                 },
10689                 {
10690                     cls : "", 
10691                     cn: [
10692                         combobox
10693                     ]
10694                 }
10695
10696             ];
10697             
10698             var labelCfg = cfg.cn[1];
10699             var contentCfg = cfg.cn[2];
10700             
10701             if(this.indicatorpos == 'right'){
10702                 cfg.cn = [
10703                     {
10704                         tag: 'label',
10705                         'for' :  id,
10706                         cls : 'control-label',
10707                         cn : [
10708                             {
10709                                 tag : 'span',
10710                                 html : this.fieldLabel
10711                             },
10712                             indicator
10713                         ]
10714                     },
10715                     {
10716                         cls : "", 
10717                         cn: [
10718                             combobox
10719                         ]
10720                     }
10721
10722                 ];
10723                 
10724                 labelCfg = cfg.cn[0];
10725                 contentCfg = cfg.cn[1];
10726             }
10727             
10728             if(this.labelWidth > 12){
10729                 labelCfg.style = "width: " + this.labelWidth + 'px';
10730             }
10731             
10732             if(this.labelWidth < 13 && this.labelmd == 0){
10733                 this.labelmd = this.labelWidth;
10734             }
10735             
10736             if(this.labellg > 0){
10737                 labelCfg.cls += ' col-lg-' + this.labellg;
10738                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10739             }
10740             
10741             if(this.labelmd > 0){
10742                 labelCfg.cls += ' col-md-' + this.labelmd;
10743                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10744             }
10745             
10746             if(this.labelsm > 0){
10747                 labelCfg.cls += ' col-sm-' + this.labelsm;
10748                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10749             }
10750             
10751             if(this.labelxs > 0){
10752                 labelCfg.cls += ' col-xs-' + this.labelxs;
10753                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10754             }
10755             
10756         } else if ( this.fieldLabel.length) {
10757 //                Roo.log(" label");
10758             cfg.cn = [
10759                 indicator,
10760                {
10761                    tag: 'label',
10762                    //cls : 'input-group-addon',
10763                    html : this.fieldLabel
10764
10765                },
10766
10767                combobox
10768
10769             ];
10770             
10771             if(this.indicatorpos == 'right'){
10772                 
10773                 cfg.cn = [
10774                     {
10775                        tag: 'label',
10776                        cn : [
10777                            {
10778                                tag : 'span',
10779                                html : this.fieldLabel
10780                            },
10781                            indicator
10782                        ]
10783
10784                     },
10785                     combobox
10786
10787                 ];
10788
10789             }
10790
10791         } else {
10792             
10793 //                Roo.log(" no label && no align");
10794                 cfg = combobox
10795                      
10796                 
10797         }
10798         
10799         var settings=this;
10800         ['xs','sm','md','lg'].map(function(size){
10801             if (settings[size]) {
10802                 cfg.cls += ' col-' + size + '-' + settings[size];
10803             }
10804         });
10805         
10806         return cfg;
10807         
10808     },
10809     
10810     
10811     
10812     // private
10813     onResize : function(w, h){
10814 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10815 //        if(typeof w == 'number'){
10816 //            var x = w - this.trigger.getWidth();
10817 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10818 //            this.trigger.setStyle('left', x+'px');
10819 //        }
10820     },
10821
10822     // private
10823     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10824
10825     // private
10826     getResizeEl : function(){
10827         return this.inputEl();
10828     },
10829
10830     // private
10831     getPositionEl : function(){
10832         return this.inputEl();
10833     },
10834
10835     // private
10836     alignErrorIcon : function(){
10837         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10838     },
10839
10840     // private
10841     initEvents : function(){
10842         
10843         this.createList();
10844         
10845         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10846         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10847         if(!this.multiple && this.showToggleBtn){
10848             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10849             if(this.hideTrigger){
10850                 this.trigger.setDisplayed(false);
10851             }
10852             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10853         }
10854         
10855         if(this.multiple){
10856             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10857         }
10858         
10859         if(this.removable && !this.editable && !this.tickable){
10860             var close = this.closeTriggerEl();
10861             
10862             if(close){
10863                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10864                 close.on('click', this.removeBtnClick, this, close);
10865             }
10866         }
10867         
10868         //this.trigger.addClassOnOver('x-form-trigger-over');
10869         //this.trigger.addClassOnClick('x-form-trigger-click');
10870         
10871         //if(!this.width){
10872         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10873         //}
10874     },
10875     
10876     closeTriggerEl : function()
10877     {
10878         var close = this.el.select('.roo-combo-removable-btn', true).first();
10879         return close ? close : false;
10880     },
10881     
10882     removeBtnClick : function(e, h, el)
10883     {
10884         e.preventDefault();
10885         
10886         if(this.fireEvent("remove", this) !== false){
10887             this.reset();
10888             this.fireEvent("afterremove", this)
10889         }
10890     },
10891     
10892     createList : function()
10893     {
10894         this.list = Roo.get(document.body).createChild({
10895             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10896             cls: 'typeahead typeahead-long dropdown-menu',
10897             style: 'display:none'
10898         });
10899         
10900         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10901         
10902     },
10903
10904     // private
10905     initTrigger : function(){
10906        
10907     },
10908
10909     // private
10910     onDestroy : function(){
10911         if(this.trigger){
10912             this.trigger.removeAllListeners();
10913           //  this.trigger.remove();
10914         }
10915         //if(this.wrap){
10916         //    this.wrap.remove();
10917         //}
10918         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10919     },
10920
10921     // private
10922     onFocus : function(){
10923         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10924         /*
10925         if(!this.mimicing){
10926             this.wrap.addClass('x-trigger-wrap-focus');
10927             this.mimicing = true;
10928             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10929             if(this.monitorTab){
10930                 this.el.on("keydown", this.checkTab, this);
10931             }
10932         }
10933         */
10934     },
10935
10936     // private
10937     checkTab : function(e){
10938         if(e.getKey() == e.TAB){
10939             this.triggerBlur();
10940         }
10941     },
10942
10943     // private
10944     onBlur : function(){
10945         // do nothing
10946     },
10947
10948     // private
10949     mimicBlur : function(e, t){
10950         /*
10951         if(!this.wrap.contains(t) && this.validateBlur()){
10952             this.triggerBlur();
10953         }
10954         */
10955     },
10956
10957     // private
10958     triggerBlur : function(){
10959         this.mimicing = false;
10960         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10961         if(this.monitorTab){
10962             this.el.un("keydown", this.checkTab, this);
10963         }
10964         //this.wrap.removeClass('x-trigger-wrap-focus');
10965         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10966     },
10967
10968     // private
10969     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10970     validateBlur : function(e, t){
10971         return true;
10972     },
10973
10974     // private
10975     onDisable : function(){
10976         this.inputEl().dom.disabled = true;
10977         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10978         //if(this.wrap){
10979         //    this.wrap.addClass('x-item-disabled');
10980         //}
10981     },
10982
10983     // private
10984     onEnable : function(){
10985         this.inputEl().dom.disabled = false;
10986         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10987         //if(this.wrap){
10988         //    this.el.removeClass('x-item-disabled');
10989         //}
10990     },
10991
10992     // private
10993     onShow : function(){
10994         var ae = this.getActionEl();
10995         
10996         if(ae){
10997             ae.dom.style.display = '';
10998             ae.dom.style.visibility = 'visible';
10999         }
11000     },
11001
11002     // private
11003     
11004     onHide : function(){
11005         var ae = this.getActionEl();
11006         ae.dom.style.display = 'none';
11007     },
11008
11009     /**
11010      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11011      * by an implementing function.
11012      * @method
11013      * @param {EventObject} e
11014      */
11015     onTriggerClick : Roo.emptyFn
11016 });
11017  /*
11018  * Based on:
11019  * Ext JS Library 1.1.1
11020  * Copyright(c) 2006-2007, Ext JS, LLC.
11021  *
11022  * Originally Released Under LGPL - original licence link has changed is not relivant.
11023  *
11024  * Fork - LGPL
11025  * <script type="text/javascript">
11026  */
11027
11028
11029 /**
11030  * @class Roo.data.SortTypes
11031  * @singleton
11032  * Defines the default sorting (casting?) comparison functions used when sorting data.
11033  */
11034 Roo.data.SortTypes = {
11035     /**
11036      * Default sort that does nothing
11037      * @param {Mixed} s The value being converted
11038      * @return {Mixed} The comparison value
11039      */
11040     none : function(s){
11041         return s;
11042     },
11043     
11044     /**
11045      * The regular expression used to strip tags
11046      * @type {RegExp}
11047      * @property
11048      */
11049     stripTagsRE : /<\/?[^>]+>/gi,
11050     
11051     /**
11052      * Strips all HTML tags to sort on text only
11053      * @param {Mixed} s The value being converted
11054      * @return {String} The comparison value
11055      */
11056     asText : function(s){
11057         return String(s).replace(this.stripTagsRE, "");
11058     },
11059     
11060     /**
11061      * Strips all HTML tags to sort on text only - Case insensitive
11062      * @param {Mixed} s The value being converted
11063      * @return {String} The comparison value
11064      */
11065     asUCText : function(s){
11066         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11067     },
11068     
11069     /**
11070      * Case insensitive string
11071      * @param {Mixed} s The value being converted
11072      * @return {String} The comparison value
11073      */
11074     asUCString : function(s) {
11075         return String(s).toUpperCase();
11076     },
11077     
11078     /**
11079      * Date sorting
11080      * @param {Mixed} s The value being converted
11081      * @return {Number} The comparison value
11082      */
11083     asDate : function(s) {
11084         if(!s){
11085             return 0;
11086         }
11087         if(s instanceof Date){
11088             return s.getTime();
11089         }
11090         return Date.parse(String(s));
11091     },
11092     
11093     /**
11094      * Float sorting
11095      * @param {Mixed} s The value being converted
11096      * @return {Float} The comparison value
11097      */
11098     asFloat : function(s) {
11099         var val = parseFloat(String(s).replace(/,/g, ""));
11100         if(isNaN(val)) {
11101             val = 0;
11102         }
11103         return val;
11104     },
11105     
11106     /**
11107      * Integer sorting
11108      * @param {Mixed} s The value being converted
11109      * @return {Number} The comparison value
11110      */
11111     asInt : function(s) {
11112         var val = parseInt(String(s).replace(/,/g, ""));
11113         if(isNaN(val)) {
11114             val = 0;
11115         }
11116         return val;
11117     }
11118 };/*
11119  * Based on:
11120  * Ext JS Library 1.1.1
11121  * Copyright(c) 2006-2007, Ext JS, LLC.
11122  *
11123  * Originally Released Under LGPL - original licence link has changed is not relivant.
11124  *
11125  * Fork - LGPL
11126  * <script type="text/javascript">
11127  */
11128
11129 /**
11130 * @class Roo.data.Record
11131  * Instances of this class encapsulate both record <em>definition</em> information, and record
11132  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11133  * to access Records cached in an {@link Roo.data.Store} object.<br>
11134  * <p>
11135  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11136  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11137  * objects.<br>
11138  * <p>
11139  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11140  * @constructor
11141  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11142  * {@link #create}. The parameters are the same.
11143  * @param {Array} data An associative Array of data values keyed by the field name.
11144  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11145  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11146  * not specified an integer id is generated.
11147  */
11148 Roo.data.Record = function(data, id){
11149     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11150     this.data = data;
11151 };
11152
11153 /**
11154  * Generate a constructor for a specific record layout.
11155  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11156  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11157  * Each field definition object may contain the following properties: <ul>
11158  * <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,
11159  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11160  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11161  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11162  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11163  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11164  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11165  * this may be omitted.</p></li>
11166  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11167  * <ul><li>auto (Default, implies no conversion)</li>
11168  * <li>string</li>
11169  * <li>int</li>
11170  * <li>float</li>
11171  * <li>boolean</li>
11172  * <li>date</li></ul></p></li>
11173  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11174  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11175  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11176  * by the Reader into an object that will be stored in the Record. It is passed the
11177  * following parameters:<ul>
11178  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11179  * </ul></p></li>
11180  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11181  * </ul>
11182  * <br>usage:<br><pre><code>
11183 var TopicRecord = Roo.data.Record.create(
11184     {name: 'title', mapping: 'topic_title'},
11185     {name: 'author', mapping: 'username'},
11186     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11187     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11188     {name: 'lastPoster', mapping: 'user2'},
11189     {name: 'excerpt', mapping: 'post_text'}
11190 );
11191
11192 var myNewRecord = new TopicRecord({
11193     title: 'Do my job please',
11194     author: 'noobie',
11195     totalPosts: 1,
11196     lastPost: new Date(),
11197     lastPoster: 'Animal',
11198     excerpt: 'No way dude!'
11199 });
11200 myStore.add(myNewRecord);
11201 </code></pre>
11202  * @method create
11203  * @static
11204  */
11205 Roo.data.Record.create = function(o){
11206     var f = function(){
11207         f.superclass.constructor.apply(this, arguments);
11208     };
11209     Roo.extend(f, Roo.data.Record);
11210     var p = f.prototype;
11211     p.fields = new Roo.util.MixedCollection(false, function(field){
11212         return field.name;
11213     });
11214     for(var i = 0, len = o.length; i < len; i++){
11215         p.fields.add(new Roo.data.Field(o[i]));
11216     }
11217     f.getField = function(name){
11218         return p.fields.get(name);  
11219     };
11220     return f;
11221 };
11222
11223 Roo.data.Record.AUTO_ID = 1000;
11224 Roo.data.Record.EDIT = 'edit';
11225 Roo.data.Record.REJECT = 'reject';
11226 Roo.data.Record.COMMIT = 'commit';
11227
11228 Roo.data.Record.prototype = {
11229     /**
11230      * Readonly flag - true if this record has been modified.
11231      * @type Boolean
11232      */
11233     dirty : false,
11234     editing : false,
11235     error: null,
11236     modified: null,
11237
11238     // private
11239     join : function(store){
11240         this.store = store;
11241     },
11242
11243     /**
11244      * Set the named field to the specified value.
11245      * @param {String} name The name of the field to set.
11246      * @param {Object} value The value to set the field to.
11247      */
11248     set : function(name, value){
11249         if(this.data[name] == value){
11250             return;
11251         }
11252         this.dirty = true;
11253         if(!this.modified){
11254             this.modified = {};
11255         }
11256         if(typeof this.modified[name] == 'undefined'){
11257             this.modified[name] = this.data[name];
11258         }
11259         this.data[name] = value;
11260         if(!this.editing && this.store){
11261             this.store.afterEdit(this);
11262         }       
11263     },
11264
11265     /**
11266      * Get the value of the named field.
11267      * @param {String} name The name of the field to get the value of.
11268      * @return {Object} The value of the field.
11269      */
11270     get : function(name){
11271         return this.data[name]; 
11272     },
11273
11274     // private
11275     beginEdit : function(){
11276         this.editing = true;
11277         this.modified = {}; 
11278     },
11279
11280     // private
11281     cancelEdit : function(){
11282         this.editing = false;
11283         delete this.modified;
11284     },
11285
11286     // private
11287     endEdit : function(){
11288         this.editing = false;
11289         if(this.dirty && this.store){
11290             this.store.afterEdit(this);
11291         }
11292     },
11293
11294     /**
11295      * Usually called by the {@link Roo.data.Store} which owns the Record.
11296      * Rejects all changes made to the Record since either creation, or the last commit operation.
11297      * Modified fields are reverted to their original values.
11298      * <p>
11299      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11300      * of reject operations.
11301      */
11302     reject : function(){
11303         var m = this.modified;
11304         for(var n in m){
11305             if(typeof m[n] != "function"){
11306                 this.data[n] = m[n];
11307             }
11308         }
11309         this.dirty = false;
11310         delete this.modified;
11311         this.editing = false;
11312         if(this.store){
11313             this.store.afterReject(this);
11314         }
11315     },
11316
11317     /**
11318      * Usually called by the {@link Roo.data.Store} which owns the Record.
11319      * Commits all changes made to the Record since either creation, or the last commit operation.
11320      * <p>
11321      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11322      * of commit operations.
11323      */
11324     commit : function(){
11325         this.dirty = false;
11326         delete this.modified;
11327         this.editing = false;
11328         if(this.store){
11329             this.store.afterCommit(this);
11330         }
11331     },
11332
11333     // private
11334     hasError : function(){
11335         return this.error != null;
11336     },
11337
11338     // private
11339     clearError : function(){
11340         this.error = null;
11341     },
11342
11343     /**
11344      * Creates a copy of this record.
11345      * @param {String} id (optional) A new record id if you don't want to use this record's id
11346      * @return {Record}
11347      */
11348     copy : function(newId) {
11349         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11350     }
11351 };/*
11352  * Based on:
11353  * Ext JS Library 1.1.1
11354  * Copyright(c) 2006-2007, Ext JS, LLC.
11355  *
11356  * Originally Released Under LGPL - original licence link has changed is not relivant.
11357  *
11358  * Fork - LGPL
11359  * <script type="text/javascript">
11360  */
11361
11362
11363
11364 /**
11365  * @class Roo.data.Store
11366  * @extends Roo.util.Observable
11367  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11368  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11369  * <p>
11370  * 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
11371  * has no knowledge of the format of the data returned by the Proxy.<br>
11372  * <p>
11373  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11374  * instances from the data object. These records are cached and made available through accessor functions.
11375  * @constructor
11376  * Creates a new Store.
11377  * @param {Object} config A config object containing the objects needed for the Store to access data,
11378  * and read the data into Records.
11379  */
11380 Roo.data.Store = function(config){
11381     this.data = new Roo.util.MixedCollection(false);
11382     this.data.getKey = function(o){
11383         return o.id;
11384     };
11385     this.baseParams = {};
11386     // private
11387     this.paramNames = {
11388         "start" : "start",
11389         "limit" : "limit",
11390         "sort" : "sort",
11391         "dir" : "dir",
11392         "multisort" : "_multisort"
11393     };
11394
11395     if(config && config.data){
11396         this.inlineData = config.data;
11397         delete config.data;
11398     }
11399
11400     Roo.apply(this, config);
11401     
11402     if(this.reader){ // reader passed
11403         this.reader = Roo.factory(this.reader, Roo.data);
11404         this.reader.xmodule = this.xmodule || false;
11405         if(!this.recordType){
11406             this.recordType = this.reader.recordType;
11407         }
11408         if(this.reader.onMetaChange){
11409             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11410         }
11411     }
11412
11413     if(this.recordType){
11414         this.fields = this.recordType.prototype.fields;
11415     }
11416     this.modified = [];
11417
11418     this.addEvents({
11419         /**
11420          * @event datachanged
11421          * Fires when the data cache has changed, and a widget which is using this Store
11422          * as a Record cache should refresh its view.
11423          * @param {Store} this
11424          */
11425         datachanged : true,
11426         /**
11427          * @event metachange
11428          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11429          * @param {Store} this
11430          * @param {Object} meta The JSON metadata
11431          */
11432         metachange : true,
11433         /**
11434          * @event add
11435          * Fires when Records have been added to the Store
11436          * @param {Store} this
11437          * @param {Roo.data.Record[]} records The array of Records added
11438          * @param {Number} index The index at which the record(s) were added
11439          */
11440         add : true,
11441         /**
11442          * @event remove
11443          * Fires when a Record has been removed from the Store
11444          * @param {Store} this
11445          * @param {Roo.data.Record} record The Record that was removed
11446          * @param {Number} index The index at which the record was removed
11447          */
11448         remove : true,
11449         /**
11450          * @event update
11451          * Fires when a Record has been updated
11452          * @param {Store} this
11453          * @param {Roo.data.Record} record The Record that was updated
11454          * @param {String} operation The update operation being performed.  Value may be one of:
11455          * <pre><code>
11456  Roo.data.Record.EDIT
11457  Roo.data.Record.REJECT
11458  Roo.data.Record.COMMIT
11459          * </code></pre>
11460          */
11461         update : true,
11462         /**
11463          * @event clear
11464          * Fires when the data cache has been cleared.
11465          * @param {Store} this
11466          */
11467         clear : true,
11468         /**
11469          * @event beforeload
11470          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11471          * the load action will be canceled.
11472          * @param {Store} this
11473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11474          */
11475         beforeload : true,
11476         /**
11477          * @event beforeloadadd
11478          * Fires after a new set of Records has been loaded.
11479          * @param {Store} this
11480          * @param {Roo.data.Record[]} records The Records that were loaded
11481          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11482          */
11483         beforeloadadd : true,
11484         /**
11485          * @event load
11486          * Fires after a new set of Records has been loaded, before they are added to the store.
11487          * @param {Store} this
11488          * @param {Roo.data.Record[]} records The Records that were loaded
11489          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11490          * @params {Object} return from reader
11491          */
11492         load : true,
11493         /**
11494          * @event loadexception
11495          * Fires if an exception occurs in the Proxy during loading.
11496          * Called with the signature of the Proxy's "loadexception" event.
11497          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11498          * 
11499          * @param {Proxy} 
11500          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11501          * @param {Object} load options 
11502          * @param {Object} jsonData from your request (normally this contains the Exception)
11503          */
11504         loadexception : true
11505     });
11506     
11507     if(this.proxy){
11508         this.proxy = Roo.factory(this.proxy, Roo.data);
11509         this.proxy.xmodule = this.xmodule || false;
11510         this.relayEvents(this.proxy,  ["loadexception"]);
11511     }
11512     this.sortToggle = {};
11513     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11514
11515     Roo.data.Store.superclass.constructor.call(this);
11516
11517     if(this.inlineData){
11518         this.loadData(this.inlineData);
11519         delete this.inlineData;
11520     }
11521 };
11522
11523 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11524      /**
11525     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11526     * without a remote query - used by combo/forms at present.
11527     */
11528     
11529     /**
11530     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11531     */
11532     /**
11533     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11534     */
11535     /**
11536     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11537     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11538     */
11539     /**
11540     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11541     * on any HTTP request
11542     */
11543     /**
11544     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11545     */
11546     /**
11547     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11548     */
11549     multiSort: false,
11550     /**
11551     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11552     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11553     */
11554     remoteSort : false,
11555
11556     /**
11557     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11558      * loaded or when a record is removed. (defaults to false).
11559     */
11560     pruneModifiedRecords : false,
11561
11562     // private
11563     lastOptions : null,
11564
11565     /**
11566      * Add Records to the Store and fires the add event.
11567      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11568      */
11569     add : function(records){
11570         records = [].concat(records);
11571         for(var i = 0, len = records.length; i < len; i++){
11572             records[i].join(this);
11573         }
11574         var index = this.data.length;
11575         this.data.addAll(records);
11576         this.fireEvent("add", this, records, index);
11577     },
11578
11579     /**
11580      * Remove a Record from the Store and fires the remove event.
11581      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11582      */
11583     remove : function(record){
11584         var index = this.data.indexOf(record);
11585         this.data.removeAt(index);
11586  
11587         if(this.pruneModifiedRecords){
11588             this.modified.remove(record);
11589         }
11590         this.fireEvent("remove", this, record, index);
11591     },
11592
11593     /**
11594      * Remove all Records from the Store and fires the clear event.
11595      */
11596     removeAll : function(){
11597         this.data.clear();
11598         if(this.pruneModifiedRecords){
11599             this.modified = [];
11600         }
11601         this.fireEvent("clear", this);
11602     },
11603
11604     /**
11605      * Inserts Records to the Store at the given index and fires the add event.
11606      * @param {Number} index The start index at which to insert the passed Records.
11607      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11608      */
11609     insert : function(index, records){
11610         records = [].concat(records);
11611         for(var i = 0, len = records.length; i < len; i++){
11612             this.data.insert(index, records[i]);
11613             records[i].join(this);
11614         }
11615         this.fireEvent("add", this, records, index);
11616     },
11617
11618     /**
11619      * Get the index within the cache of the passed Record.
11620      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11621      * @return {Number} The index of the passed Record. Returns -1 if not found.
11622      */
11623     indexOf : function(record){
11624         return this.data.indexOf(record);
11625     },
11626
11627     /**
11628      * Get the index within the cache of the Record with the passed id.
11629      * @param {String} id The id of the Record to find.
11630      * @return {Number} The index of the Record. Returns -1 if not found.
11631      */
11632     indexOfId : function(id){
11633         return this.data.indexOfKey(id);
11634     },
11635
11636     /**
11637      * Get the Record with the specified id.
11638      * @param {String} id The id of the Record to find.
11639      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11640      */
11641     getById : function(id){
11642         return this.data.key(id);
11643     },
11644
11645     /**
11646      * Get the Record at the specified index.
11647      * @param {Number} index The index of the Record to find.
11648      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11649      */
11650     getAt : function(index){
11651         return this.data.itemAt(index);
11652     },
11653
11654     /**
11655      * Returns a range of Records between specified indices.
11656      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11657      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11658      * @return {Roo.data.Record[]} An array of Records
11659      */
11660     getRange : function(start, end){
11661         return this.data.getRange(start, end);
11662     },
11663
11664     // private
11665     storeOptions : function(o){
11666         o = Roo.apply({}, o);
11667         delete o.callback;
11668         delete o.scope;
11669         this.lastOptions = o;
11670     },
11671
11672     /**
11673      * Loads the Record cache from the configured Proxy using the configured Reader.
11674      * <p>
11675      * If using remote paging, then the first load call must specify the <em>start</em>
11676      * and <em>limit</em> properties in the options.params property to establish the initial
11677      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11678      * <p>
11679      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11680      * and this call will return before the new data has been loaded. Perform any post-processing
11681      * in a callback function, or in a "load" event handler.</strong>
11682      * <p>
11683      * @param {Object} options An object containing properties which control loading options:<ul>
11684      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11685      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11686      * passed the following arguments:<ul>
11687      * <li>r : Roo.data.Record[]</li>
11688      * <li>options: Options object from the load call</li>
11689      * <li>success: Boolean success indicator</li></ul></li>
11690      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11691      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11692      * </ul>
11693      */
11694     load : function(options){
11695         options = options || {};
11696         if(this.fireEvent("beforeload", this, options) !== false){
11697             this.storeOptions(options);
11698             var p = Roo.apply(options.params || {}, this.baseParams);
11699             // if meta was not loaded from remote source.. try requesting it.
11700             if (!this.reader.metaFromRemote) {
11701                 p._requestMeta = 1;
11702             }
11703             if(this.sortInfo && this.remoteSort){
11704                 var pn = this.paramNames;
11705                 p[pn["sort"]] = this.sortInfo.field;
11706                 p[pn["dir"]] = this.sortInfo.direction;
11707             }
11708             if (this.multiSort) {
11709                 var pn = this.paramNames;
11710                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11711             }
11712             
11713             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11714         }
11715     },
11716
11717     /**
11718      * Reloads the Record cache from the configured Proxy using the configured Reader and
11719      * the options from the last load operation performed.
11720      * @param {Object} options (optional) An object containing properties which may override the options
11721      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11722      * the most recently used options are reused).
11723      */
11724     reload : function(options){
11725         this.load(Roo.applyIf(options||{}, this.lastOptions));
11726     },
11727
11728     // private
11729     // Called as a callback by the Reader during a load operation.
11730     loadRecords : function(o, options, success){
11731         if(!o || success === false){
11732             if(success !== false){
11733                 this.fireEvent("load", this, [], options, o);
11734             }
11735             if(options.callback){
11736                 options.callback.call(options.scope || this, [], options, false);
11737             }
11738             return;
11739         }
11740         // if data returned failure - throw an exception.
11741         if (o.success === false) {
11742             // show a message if no listener is registered.
11743             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11744                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11745             }
11746             // loadmask wil be hooked into this..
11747             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11748             return;
11749         }
11750         var r = o.records, t = o.totalRecords || r.length;
11751         
11752         this.fireEvent("beforeloadadd", this, r, options, o);
11753         
11754         if(!options || options.add !== true){
11755             if(this.pruneModifiedRecords){
11756                 this.modified = [];
11757             }
11758             for(var i = 0, len = r.length; i < len; i++){
11759                 r[i].join(this);
11760             }
11761             if(this.snapshot){
11762                 this.data = this.snapshot;
11763                 delete this.snapshot;
11764             }
11765             this.data.clear();
11766             this.data.addAll(r);
11767             this.totalLength = t;
11768             this.applySort();
11769             this.fireEvent("datachanged", this);
11770         }else{
11771             this.totalLength = Math.max(t, this.data.length+r.length);
11772             this.add(r);
11773         }
11774         
11775         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11776                 
11777             var e = new Roo.data.Record({});
11778
11779             e.set(this.parent.displayField, this.parent.emptyTitle);
11780             e.set(this.parent.valueField, '');
11781
11782             this.insert(0, e);
11783         }
11784             
11785         this.fireEvent("load", this, r, options, o);
11786         if(options.callback){
11787             options.callback.call(options.scope || this, r, options, true);
11788         }
11789     },
11790
11791
11792     /**
11793      * Loads data from a passed data block. A Reader which understands the format of the data
11794      * must have been configured in the constructor.
11795      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11796      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11797      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11798      */
11799     loadData : function(o, append){
11800         var r = this.reader.readRecords(o);
11801         this.loadRecords(r, {add: append}, true);
11802     },
11803
11804     /**
11805      * Gets the number of cached records.
11806      * <p>
11807      * <em>If using paging, this may not be the total size of the dataset. If the data object
11808      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11809      * the data set size</em>
11810      */
11811     getCount : function(){
11812         return this.data.length || 0;
11813     },
11814
11815     /**
11816      * Gets the total number of records in the dataset as returned by the server.
11817      * <p>
11818      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11819      * the dataset size</em>
11820      */
11821     getTotalCount : function(){
11822         return this.totalLength || 0;
11823     },
11824
11825     /**
11826      * Returns the sort state of the Store as an object with two properties:
11827      * <pre><code>
11828  field {String} The name of the field by which the Records are sorted
11829  direction {String} The sort order, "ASC" or "DESC"
11830      * </code></pre>
11831      */
11832     getSortState : function(){
11833         return this.sortInfo;
11834     },
11835
11836     // private
11837     applySort : function(){
11838         if(this.sortInfo && !this.remoteSort){
11839             var s = this.sortInfo, f = s.field;
11840             var st = this.fields.get(f).sortType;
11841             var fn = function(r1, r2){
11842                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11843                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11844             };
11845             this.data.sort(s.direction, fn);
11846             if(this.snapshot && this.snapshot != this.data){
11847                 this.snapshot.sort(s.direction, fn);
11848             }
11849         }
11850     },
11851
11852     /**
11853      * Sets the default sort column and order to be used by the next load operation.
11854      * @param {String} fieldName The name of the field to sort by.
11855      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11856      */
11857     setDefaultSort : function(field, dir){
11858         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11859     },
11860
11861     /**
11862      * Sort the Records.
11863      * If remote sorting is used, the sort is performed on the server, and the cache is
11864      * reloaded. If local sorting is used, the cache is sorted internally.
11865      * @param {String} fieldName The name of the field to sort by.
11866      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11867      */
11868     sort : function(fieldName, dir){
11869         var f = this.fields.get(fieldName);
11870         if(!dir){
11871             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11872             
11873             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11874                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11875             }else{
11876                 dir = f.sortDir;
11877             }
11878         }
11879         this.sortToggle[f.name] = dir;
11880         this.sortInfo = {field: f.name, direction: dir};
11881         if(!this.remoteSort){
11882             this.applySort();
11883             this.fireEvent("datachanged", this);
11884         }else{
11885             this.load(this.lastOptions);
11886         }
11887     },
11888
11889     /**
11890      * Calls the specified function for each of the Records in the cache.
11891      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11892      * Returning <em>false</em> aborts and exits the iteration.
11893      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11894      */
11895     each : function(fn, scope){
11896         this.data.each(fn, scope);
11897     },
11898
11899     /**
11900      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11901      * (e.g., during paging).
11902      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11903      */
11904     getModifiedRecords : function(){
11905         return this.modified;
11906     },
11907
11908     // private
11909     createFilterFn : function(property, value, anyMatch){
11910         if(!value.exec){ // not a regex
11911             value = String(value);
11912             if(value.length == 0){
11913                 return false;
11914             }
11915             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11916         }
11917         return function(r){
11918             return value.test(r.data[property]);
11919         };
11920     },
11921
11922     /**
11923      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11924      * @param {String} property A field on your records
11925      * @param {Number} start The record index to start at (defaults to 0)
11926      * @param {Number} end The last record index to include (defaults to length - 1)
11927      * @return {Number} The sum
11928      */
11929     sum : function(property, start, end){
11930         var rs = this.data.items, v = 0;
11931         start = start || 0;
11932         end = (end || end === 0) ? end : rs.length-1;
11933
11934         for(var i = start; i <= end; i++){
11935             v += (rs[i].data[property] || 0);
11936         }
11937         return v;
11938     },
11939
11940     /**
11941      * Filter the records by a specified property.
11942      * @param {String} field A field on your records
11943      * @param {String/RegExp} value Either a string that the field
11944      * should start with or a RegExp to test against the field
11945      * @param {Boolean} anyMatch True to match any part not just the beginning
11946      */
11947     filter : function(property, value, anyMatch){
11948         var fn = this.createFilterFn(property, value, anyMatch);
11949         return fn ? this.filterBy(fn) : this.clearFilter();
11950     },
11951
11952     /**
11953      * Filter by a function. The specified function will be called with each
11954      * record in this data source. If the function returns true the record is included,
11955      * otherwise it is filtered.
11956      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11957      * @param {Object} scope (optional) The scope of the function (defaults to this)
11958      */
11959     filterBy : function(fn, scope){
11960         this.snapshot = this.snapshot || this.data;
11961         this.data = this.queryBy(fn, scope||this);
11962         this.fireEvent("datachanged", this);
11963     },
11964
11965     /**
11966      * Query the records by a specified property.
11967      * @param {String} field A field on your records
11968      * @param {String/RegExp} value Either a string that the field
11969      * should start with or a RegExp to test against the field
11970      * @param {Boolean} anyMatch True to match any part not just the beginning
11971      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11972      */
11973     query : function(property, value, anyMatch){
11974         var fn = this.createFilterFn(property, value, anyMatch);
11975         return fn ? this.queryBy(fn) : this.data.clone();
11976     },
11977
11978     /**
11979      * Query by a function. The specified function will be called with each
11980      * record in this data source. If the function returns true the record is included
11981      * in the results.
11982      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11983      * @param {Object} scope (optional) The scope of the function (defaults to this)
11984       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11985      **/
11986     queryBy : function(fn, scope){
11987         var data = this.snapshot || this.data;
11988         return data.filterBy(fn, scope||this);
11989     },
11990
11991     /**
11992      * Collects unique values for a particular dataIndex from this store.
11993      * @param {String} dataIndex The property to collect
11994      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11995      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11996      * @return {Array} An array of the unique values
11997      **/
11998     collect : function(dataIndex, allowNull, bypassFilter){
11999         var d = (bypassFilter === true && this.snapshot) ?
12000                 this.snapshot.items : this.data.items;
12001         var v, sv, r = [], l = {};
12002         for(var i = 0, len = d.length; i < len; i++){
12003             v = d[i].data[dataIndex];
12004             sv = String(v);
12005             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12006                 l[sv] = true;
12007                 r[r.length] = v;
12008             }
12009         }
12010         return r;
12011     },
12012
12013     /**
12014      * Revert to a view of the Record cache with no filtering applied.
12015      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12016      */
12017     clearFilter : function(suppressEvent){
12018         if(this.snapshot && this.snapshot != this.data){
12019             this.data = this.snapshot;
12020             delete this.snapshot;
12021             if(suppressEvent !== true){
12022                 this.fireEvent("datachanged", this);
12023             }
12024         }
12025     },
12026
12027     // private
12028     afterEdit : function(record){
12029         if(this.modified.indexOf(record) == -1){
12030             this.modified.push(record);
12031         }
12032         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12033     },
12034     
12035     // private
12036     afterReject : function(record){
12037         this.modified.remove(record);
12038         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12039     },
12040
12041     // private
12042     afterCommit : function(record){
12043         this.modified.remove(record);
12044         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12045     },
12046
12047     /**
12048      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12049      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12050      */
12051     commitChanges : function(){
12052         var m = this.modified.slice(0);
12053         this.modified = [];
12054         for(var i = 0, len = m.length; i < len; i++){
12055             m[i].commit();
12056         }
12057     },
12058
12059     /**
12060      * Cancel outstanding changes on all changed records.
12061      */
12062     rejectChanges : function(){
12063         var m = this.modified.slice(0);
12064         this.modified = [];
12065         for(var i = 0, len = m.length; i < len; i++){
12066             m[i].reject();
12067         }
12068     },
12069
12070     onMetaChange : function(meta, rtype, o){
12071         this.recordType = rtype;
12072         this.fields = rtype.prototype.fields;
12073         delete this.snapshot;
12074         this.sortInfo = meta.sortInfo || this.sortInfo;
12075         this.modified = [];
12076         this.fireEvent('metachange', this, this.reader.meta);
12077     },
12078     
12079     moveIndex : function(data, type)
12080     {
12081         var index = this.indexOf(data);
12082         
12083         var newIndex = index + type;
12084         
12085         this.remove(data);
12086         
12087         this.insert(newIndex, data);
12088         
12089     }
12090 });/*
12091  * Based on:
12092  * Ext JS Library 1.1.1
12093  * Copyright(c) 2006-2007, Ext JS, LLC.
12094  *
12095  * Originally Released Under LGPL - original licence link has changed is not relivant.
12096  *
12097  * Fork - LGPL
12098  * <script type="text/javascript">
12099  */
12100
12101 /**
12102  * @class Roo.data.SimpleStore
12103  * @extends Roo.data.Store
12104  * Small helper class to make creating Stores from Array data easier.
12105  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12106  * @cfg {Array} fields An array of field definition objects, or field name strings.
12107  * @cfg {Array} data The multi-dimensional array of data
12108  * @constructor
12109  * @param {Object} config
12110  */
12111 Roo.data.SimpleStore = function(config){
12112     Roo.data.SimpleStore.superclass.constructor.call(this, {
12113         isLocal : true,
12114         reader: new Roo.data.ArrayReader({
12115                 id: config.id
12116             },
12117             Roo.data.Record.create(config.fields)
12118         ),
12119         proxy : new Roo.data.MemoryProxy(config.data)
12120     });
12121     this.load();
12122 };
12123 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12124  * Based on:
12125  * Ext JS Library 1.1.1
12126  * Copyright(c) 2006-2007, Ext JS, LLC.
12127  *
12128  * Originally Released Under LGPL - original licence link has changed is not relivant.
12129  *
12130  * Fork - LGPL
12131  * <script type="text/javascript">
12132  */
12133
12134 /**
12135 /**
12136  * @extends Roo.data.Store
12137  * @class Roo.data.JsonStore
12138  * Small helper class to make creating Stores for JSON data easier. <br/>
12139 <pre><code>
12140 var store = new Roo.data.JsonStore({
12141     url: 'get-images.php',
12142     root: 'images',
12143     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12144 });
12145 </code></pre>
12146  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12147  * JsonReader and HttpProxy (unless inline data is provided).</b>
12148  * @cfg {Array} fields An array of field definition objects, or field name strings.
12149  * @constructor
12150  * @param {Object} config
12151  */
12152 Roo.data.JsonStore = function(c){
12153     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12154         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12155         reader: new Roo.data.JsonReader(c, c.fields)
12156     }));
12157 };
12158 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12159  * Based on:
12160  * Ext JS Library 1.1.1
12161  * Copyright(c) 2006-2007, Ext JS, LLC.
12162  *
12163  * Originally Released Under LGPL - original licence link has changed is not relivant.
12164  *
12165  * Fork - LGPL
12166  * <script type="text/javascript">
12167  */
12168
12169  
12170 Roo.data.Field = function(config){
12171     if(typeof config == "string"){
12172         config = {name: config};
12173     }
12174     Roo.apply(this, config);
12175     
12176     if(!this.type){
12177         this.type = "auto";
12178     }
12179     
12180     var st = Roo.data.SortTypes;
12181     // named sortTypes are supported, here we look them up
12182     if(typeof this.sortType == "string"){
12183         this.sortType = st[this.sortType];
12184     }
12185     
12186     // set default sortType for strings and dates
12187     if(!this.sortType){
12188         switch(this.type){
12189             case "string":
12190                 this.sortType = st.asUCString;
12191                 break;
12192             case "date":
12193                 this.sortType = st.asDate;
12194                 break;
12195             default:
12196                 this.sortType = st.none;
12197         }
12198     }
12199
12200     // define once
12201     var stripRe = /[\$,%]/g;
12202
12203     // prebuilt conversion function for this field, instead of
12204     // switching every time we're reading a value
12205     if(!this.convert){
12206         var cv, dateFormat = this.dateFormat;
12207         switch(this.type){
12208             case "":
12209             case "auto":
12210             case undefined:
12211                 cv = function(v){ return v; };
12212                 break;
12213             case "string":
12214                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12215                 break;
12216             case "int":
12217                 cv = function(v){
12218                     return v !== undefined && v !== null && v !== '' ?
12219                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12220                     };
12221                 break;
12222             case "float":
12223                 cv = function(v){
12224                     return v !== undefined && v !== null && v !== '' ?
12225                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12226                     };
12227                 break;
12228             case "bool":
12229             case "boolean":
12230                 cv = function(v){ return v === true || v === "true" || v == 1; };
12231                 break;
12232             case "date":
12233                 cv = function(v){
12234                     if(!v){
12235                         return '';
12236                     }
12237                     if(v instanceof Date){
12238                         return v;
12239                     }
12240                     if(dateFormat){
12241                         if(dateFormat == "timestamp"){
12242                             return new Date(v*1000);
12243                         }
12244                         return Date.parseDate(v, dateFormat);
12245                     }
12246                     var parsed = Date.parse(v);
12247                     return parsed ? new Date(parsed) : null;
12248                 };
12249              break;
12250             
12251         }
12252         this.convert = cv;
12253     }
12254 };
12255
12256 Roo.data.Field.prototype = {
12257     dateFormat: null,
12258     defaultValue: "",
12259     mapping: null,
12260     sortType : null,
12261     sortDir : "ASC"
12262 };/*
12263  * Based on:
12264  * Ext JS Library 1.1.1
12265  * Copyright(c) 2006-2007, Ext JS, LLC.
12266  *
12267  * Originally Released Under LGPL - original licence link has changed is not relivant.
12268  *
12269  * Fork - LGPL
12270  * <script type="text/javascript">
12271  */
12272  
12273 // Base class for reading structured data from a data source.  This class is intended to be
12274 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12275
12276 /**
12277  * @class Roo.data.DataReader
12278  * Base class for reading structured data from a data source.  This class is intended to be
12279  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12280  */
12281
12282 Roo.data.DataReader = function(meta, recordType){
12283     
12284     this.meta = meta;
12285     
12286     this.recordType = recordType instanceof Array ? 
12287         Roo.data.Record.create(recordType) : recordType;
12288 };
12289
12290 Roo.data.DataReader.prototype = {
12291      /**
12292      * Create an empty record
12293      * @param {Object} data (optional) - overlay some values
12294      * @return {Roo.data.Record} record created.
12295      */
12296     newRow :  function(d) {
12297         var da =  {};
12298         this.recordType.prototype.fields.each(function(c) {
12299             switch( c.type) {
12300                 case 'int' : da[c.name] = 0; break;
12301                 case 'date' : da[c.name] = new Date(); break;
12302                 case 'float' : da[c.name] = 0.0; break;
12303                 case 'boolean' : da[c.name] = false; break;
12304                 default : da[c.name] = ""; break;
12305             }
12306             
12307         });
12308         return new this.recordType(Roo.apply(da, d));
12309     }
12310     
12311 };/*
12312  * Based on:
12313  * Ext JS Library 1.1.1
12314  * Copyright(c) 2006-2007, Ext JS, LLC.
12315  *
12316  * Originally Released Under LGPL - original licence link has changed is not relivant.
12317  *
12318  * Fork - LGPL
12319  * <script type="text/javascript">
12320  */
12321
12322 /**
12323  * @class Roo.data.DataProxy
12324  * @extends Roo.data.Observable
12325  * This class is an abstract base class for implementations which provide retrieval of
12326  * unformatted data objects.<br>
12327  * <p>
12328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12329  * (of the appropriate type which knows how to parse the data object) to provide a block of
12330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12331  * <p>
12332  * Custom implementations must implement the load method as described in
12333  * {@link Roo.data.HttpProxy#load}.
12334  */
12335 Roo.data.DataProxy = function(){
12336     this.addEvents({
12337         /**
12338          * @event beforeload
12339          * Fires before a network request is made to retrieve a data object.
12340          * @param {Object} This DataProxy object.
12341          * @param {Object} params The params parameter to the load function.
12342          */
12343         beforeload : true,
12344         /**
12345          * @event load
12346          * Fires before the load method's callback is called.
12347          * @param {Object} This DataProxy object.
12348          * @param {Object} o The data object.
12349          * @param {Object} arg The callback argument object passed to the load function.
12350          */
12351         load : true,
12352         /**
12353          * @event loadexception
12354          * Fires if an Exception occurs during data retrieval.
12355          * @param {Object} This DataProxy object.
12356          * @param {Object} o The data object.
12357          * @param {Object} arg The callback argument object passed to the load function.
12358          * @param {Object} e The Exception.
12359          */
12360         loadexception : true
12361     });
12362     Roo.data.DataProxy.superclass.constructor.call(this);
12363 };
12364
12365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12366
12367     /**
12368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12369      */
12370 /*
12371  * Based on:
12372  * Ext JS Library 1.1.1
12373  * Copyright(c) 2006-2007, Ext JS, LLC.
12374  *
12375  * Originally Released Under LGPL - original licence link has changed is not relivant.
12376  *
12377  * Fork - LGPL
12378  * <script type="text/javascript">
12379  */
12380 /**
12381  * @class Roo.data.MemoryProxy
12382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12383  * to the Reader when its load method is called.
12384  * @constructor
12385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12386  */
12387 Roo.data.MemoryProxy = function(data){
12388     if (data.data) {
12389         data = data.data;
12390     }
12391     Roo.data.MemoryProxy.superclass.constructor.call(this);
12392     this.data = data;
12393 };
12394
12395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12396     
12397     /**
12398      * Load data from the requested source (in this case an in-memory
12399      * data object passed to the constructor), read the data object into
12400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12401      * process that block using the passed callback.
12402      * @param {Object} params This parameter is not used by the MemoryProxy class.
12403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12404      * object into a block of Roo.data.Records.
12405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12406      * The function must be passed <ul>
12407      * <li>The Record block object</li>
12408      * <li>The "arg" argument from the load function</li>
12409      * <li>A boolean success indicator</li>
12410      * </ul>
12411      * @param {Object} scope The scope in which to call the callback
12412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12413      */
12414     load : function(params, reader, callback, scope, arg){
12415         params = params || {};
12416         var result;
12417         try {
12418             result = reader.readRecords(params.data ? params.data :this.data);
12419         }catch(e){
12420             this.fireEvent("loadexception", this, arg, null, e);
12421             callback.call(scope, null, arg, false);
12422             return;
12423         }
12424         callback.call(scope, result, arg, true);
12425     },
12426     
12427     // private
12428     update : function(params, records){
12429         
12430     }
12431 });/*
12432  * Based on:
12433  * Ext JS Library 1.1.1
12434  * Copyright(c) 2006-2007, Ext JS, LLC.
12435  *
12436  * Originally Released Under LGPL - original licence link has changed is not relivant.
12437  *
12438  * Fork - LGPL
12439  * <script type="text/javascript">
12440  */
12441 /**
12442  * @class Roo.data.HttpProxy
12443  * @extends Roo.data.DataProxy
12444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12445  * configured to reference a certain URL.<br><br>
12446  * <p>
12447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12448  * from which the running page was served.<br><br>
12449  * <p>
12450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12451  * <p>
12452  * Be aware that to enable the browser to parse an XML document, the server must set
12453  * the Content-Type header in the HTTP response to "text/xml".
12454  * @constructor
12455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12457  * will be used to make the request.
12458  */
12459 Roo.data.HttpProxy = function(conn){
12460     Roo.data.HttpProxy.superclass.constructor.call(this);
12461     // is conn a conn config or a real conn?
12462     this.conn = conn;
12463     this.useAjax = !conn || !conn.events;
12464   
12465 };
12466
12467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12468     // thse are take from connection...
12469     
12470     /**
12471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12472      */
12473     /**
12474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12475      * extra parameters to each request made by this object. (defaults to undefined)
12476      */
12477     /**
12478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12479      *  to each request made by this object. (defaults to undefined)
12480      */
12481     /**
12482      * @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)
12483      */
12484     /**
12485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12486      */
12487      /**
12488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12489      * @type Boolean
12490      */
12491   
12492
12493     /**
12494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12495      * @type Boolean
12496      */
12497     /**
12498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12500      * a finer-grained basis than the DataProxy events.
12501      */
12502     getConnection : function(){
12503         return this.useAjax ? Roo.Ajax : this.conn;
12504     },
12505
12506     /**
12507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12509      * process that block using the passed callback.
12510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12511      * for the request to the remote server.
12512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12513      * object into a block of Roo.data.Records.
12514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12515      * The function must be passed <ul>
12516      * <li>The Record block object</li>
12517      * <li>The "arg" argument from the load function</li>
12518      * <li>A boolean success indicator</li>
12519      * </ul>
12520      * @param {Object} scope The scope in which to call the callback
12521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12522      */
12523     load : function(params, reader, callback, scope, arg){
12524         if(this.fireEvent("beforeload", this, params) !== false){
12525             var  o = {
12526                 params : params || {},
12527                 request: {
12528                     callback : callback,
12529                     scope : scope,
12530                     arg : arg
12531                 },
12532                 reader: reader,
12533                 callback : this.loadResponse,
12534                 scope: this
12535             };
12536             if(this.useAjax){
12537                 Roo.applyIf(o, this.conn);
12538                 if(this.activeRequest){
12539                     Roo.Ajax.abort(this.activeRequest);
12540                 }
12541                 this.activeRequest = Roo.Ajax.request(o);
12542             }else{
12543                 this.conn.request(o);
12544             }
12545         }else{
12546             callback.call(scope||this, null, arg, false);
12547         }
12548     },
12549
12550     // private
12551     loadResponse : function(o, success, response){
12552         delete this.activeRequest;
12553         if(!success){
12554             this.fireEvent("loadexception", this, o, response);
12555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12556             return;
12557         }
12558         var result;
12559         try {
12560             result = o.reader.read(response);
12561         }catch(e){
12562             this.fireEvent("loadexception", this, o, response, e);
12563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12564             return;
12565         }
12566         
12567         this.fireEvent("load", this, o, o.request.arg);
12568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12569     },
12570
12571     // private
12572     update : function(dataSet){
12573
12574     },
12575
12576     // private
12577     updateResponse : function(dataSet){
12578
12579     }
12580 });/*
12581  * Based on:
12582  * Ext JS Library 1.1.1
12583  * Copyright(c) 2006-2007, Ext JS, LLC.
12584  *
12585  * Originally Released Under LGPL - original licence link has changed is not relivant.
12586  *
12587  * Fork - LGPL
12588  * <script type="text/javascript">
12589  */
12590
12591 /**
12592  * @class Roo.data.ScriptTagProxy
12593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12594  * other than the originating domain of the running page.<br><br>
12595  * <p>
12596  * <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
12597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12598  * <p>
12599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12600  * source code that is used as the source inside a &lt;script> tag.<br><br>
12601  * <p>
12602  * In order for the browser to process the returned data, the server must wrap the data object
12603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12605  * depending on whether the callback name was passed:
12606  * <p>
12607  * <pre><code>
12608 boolean scriptTag = false;
12609 String cb = request.getParameter("callback");
12610 if (cb != null) {
12611     scriptTag = true;
12612     response.setContentType("text/javascript");
12613 } else {
12614     response.setContentType("application/x-json");
12615 }
12616 Writer out = response.getWriter();
12617 if (scriptTag) {
12618     out.write(cb + "(");
12619 }
12620 out.print(dataBlock.toJsonString());
12621 if (scriptTag) {
12622     out.write(");");
12623 }
12624 </pre></code>
12625  *
12626  * @constructor
12627  * @param {Object} config A configuration object.
12628  */
12629 Roo.data.ScriptTagProxy = function(config){
12630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12631     Roo.apply(this, config);
12632     this.head = document.getElementsByTagName("head")[0];
12633 };
12634
12635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12636
12637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12638     /**
12639      * @cfg {String} url The URL from which to request the data object.
12640      */
12641     /**
12642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12643      */
12644     timeout : 30000,
12645     /**
12646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12647      * the server the name of the callback function set up by the load call to process the returned data object.
12648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12649      * javascript output which calls this named function passing the data object as its only parameter.
12650      */
12651     callbackParam : "callback",
12652     /**
12653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12654      * name to the request.
12655      */
12656     nocache : true,
12657
12658     /**
12659      * Load data from the configured URL, read the data object into
12660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12661      * process that block using the passed callback.
12662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12663      * for the request to the remote server.
12664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12665      * object into a block of Roo.data.Records.
12666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12667      * The function must be passed <ul>
12668      * <li>The Record block object</li>
12669      * <li>The "arg" argument from the load function</li>
12670      * <li>A boolean success indicator</li>
12671      * </ul>
12672      * @param {Object} scope The scope in which to call the callback
12673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12674      */
12675     load : function(params, reader, callback, scope, arg){
12676         if(this.fireEvent("beforeload", this, params) !== false){
12677
12678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12679
12680             var url = this.url;
12681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12682             if(this.nocache){
12683                 url += "&_dc=" + (new Date().getTime());
12684             }
12685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12686             var trans = {
12687                 id : transId,
12688                 cb : "stcCallback"+transId,
12689                 scriptId : "stcScript"+transId,
12690                 params : params,
12691                 arg : arg,
12692                 url : url,
12693                 callback : callback,
12694                 scope : scope,
12695                 reader : reader
12696             };
12697             var conn = this;
12698
12699             window[trans.cb] = function(o){
12700                 conn.handleResponse(o, trans);
12701             };
12702
12703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12704
12705             if(this.autoAbort !== false){
12706                 this.abort();
12707             }
12708
12709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12710
12711             var script = document.createElement("script");
12712             script.setAttribute("src", url);
12713             script.setAttribute("type", "text/javascript");
12714             script.setAttribute("id", trans.scriptId);
12715             this.head.appendChild(script);
12716
12717             this.trans = trans;
12718         }else{
12719             callback.call(scope||this, null, arg, false);
12720         }
12721     },
12722
12723     // private
12724     isLoading : function(){
12725         return this.trans ? true : false;
12726     },
12727
12728     /**
12729      * Abort the current server request.
12730      */
12731     abort : function(){
12732         if(this.isLoading()){
12733             this.destroyTrans(this.trans);
12734         }
12735     },
12736
12737     // private
12738     destroyTrans : function(trans, isLoaded){
12739         this.head.removeChild(document.getElementById(trans.scriptId));
12740         clearTimeout(trans.timeoutId);
12741         if(isLoaded){
12742             window[trans.cb] = undefined;
12743             try{
12744                 delete window[trans.cb];
12745             }catch(e){}
12746         }else{
12747             // if hasn't been loaded, wait for load to remove it to prevent script error
12748             window[trans.cb] = function(){
12749                 window[trans.cb] = undefined;
12750                 try{
12751                     delete window[trans.cb];
12752                 }catch(e){}
12753             };
12754         }
12755     },
12756
12757     // private
12758     handleResponse : function(o, trans){
12759         this.trans = false;
12760         this.destroyTrans(trans, true);
12761         var result;
12762         try {
12763             result = trans.reader.readRecords(o);
12764         }catch(e){
12765             this.fireEvent("loadexception", this, o, trans.arg, e);
12766             trans.callback.call(trans.scope||window, null, trans.arg, false);
12767             return;
12768         }
12769         this.fireEvent("load", this, o, trans.arg);
12770         trans.callback.call(trans.scope||window, result, trans.arg, true);
12771     },
12772
12773     // private
12774     handleFailure : function(trans){
12775         this.trans = false;
12776         this.destroyTrans(trans, false);
12777         this.fireEvent("loadexception", this, null, trans.arg);
12778         trans.callback.call(trans.scope||window, null, trans.arg, false);
12779     }
12780 });/*
12781  * Based on:
12782  * Ext JS Library 1.1.1
12783  * Copyright(c) 2006-2007, Ext JS, LLC.
12784  *
12785  * Originally Released Under LGPL - original licence link has changed is not relivant.
12786  *
12787  * Fork - LGPL
12788  * <script type="text/javascript">
12789  */
12790
12791 /**
12792  * @class Roo.data.JsonReader
12793  * @extends Roo.data.DataReader
12794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12795  * based on mappings in a provided Roo.data.Record constructor.
12796  * 
12797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12798  * in the reply previously. 
12799  * 
12800  * <p>
12801  * Example code:
12802  * <pre><code>
12803 var RecordDef = Roo.data.Record.create([
12804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12806 ]);
12807 var myReader = new Roo.data.JsonReader({
12808     totalProperty: "results",    // The property which contains the total dataset size (optional)
12809     root: "rows",                // The property which contains an Array of row objects
12810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12811 }, RecordDef);
12812 </code></pre>
12813  * <p>
12814  * This would consume a JSON file like this:
12815  * <pre><code>
12816 { 'results': 2, 'rows': [
12817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12819 }
12820 </code></pre>
12821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12823  * paged from the remote server.
12824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12825  * @cfg {String} root name of the property which contains the Array of row objects.
12826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12827  * @cfg {Array} fields Array of field definition objects
12828  * @constructor
12829  * Create a new JsonReader
12830  * @param {Object} meta Metadata configuration options
12831  * @param {Object} recordType Either an Array of field definition objects,
12832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12833  */
12834 Roo.data.JsonReader = function(meta, recordType){
12835     
12836     meta = meta || {};
12837     // set some defaults:
12838     Roo.applyIf(meta, {
12839         totalProperty: 'total',
12840         successProperty : 'success',
12841         root : 'data',
12842         id : 'id'
12843     });
12844     
12845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12846 };
12847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12848     
12849     /**
12850      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12851      * Used by Store query builder to append _requestMeta to params.
12852      * 
12853      */
12854     metaFromRemote : false,
12855     /**
12856      * This method is only used by a DataProxy which has retrieved data from a remote server.
12857      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12858      * @return {Object} data A data block which is used by an Roo.data.Store object as
12859      * a cache of Roo.data.Records.
12860      */
12861     read : function(response){
12862         var json = response.responseText;
12863        
12864         var o = /* eval:var:o */ eval("("+json+")");
12865         if(!o) {
12866             throw {message: "JsonReader.read: Json object not found"};
12867         }
12868         
12869         if(o.metaData){
12870             
12871             delete this.ef;
12872             this.metaFromRemote = true;
12873             this.meta = o.metaData;
12874             this.recordType = Roo.data.Record.create(o.metaData.fields);
12875             this.onMetaChange(this.meta, this.recordType, o);
12876         }
12877         return this.readRecords(o);
12878     },
12879
12880     // private function a store will implement
12881     onMetaChange : function(meta, recordType, o){
12882
12883     },
12884
12885     /**
12886          * @ignore
12887          */
12888     simpleAccess: function(obj, subsc) {
12889         return obj[subsc];
12890     },
12891
12892         /**
12893          * @ignore
12894          */
12895     getJsonAccessor: function(){
12896         var re = /[\[\.]/;
12897         return function(expr) {
12898             try {
12899                 return(re.test(expr))
12900                     ? new Function("obj", "return obj." + expr)
12901                     : function(obj){
12902                         return obj[expr];
12903                     };
12904             } catch(e){}
12905             return Roo.emptyFn;
12906         };
12907     }(),
12908
12909     /**
12910      * Create a data block containing Roo.data.Records from an XML document.
12911      * @param {Object} o An object which contains an Array of row objects in the property specified
12912      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12913      * which contains the total size of the dataset.
12914      * @return {Object} data A data block which is used by an Roo.data.Store object as
12915      * a cache of Roo.data.Records.
12916      */
12917     readRecords : function(o){
12918         /**
12919          * After any data loads, the raw JSON data is available for further custom processing.
12920          * @type Object
12921          */
12922         this.o = o;
12923         var s = this.meta, Record = this.recordType,
12924             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12925
12926 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12927         if (!this.ef) {
12928             if(s.totalProperty) {
12929                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12930                 }
12931                 if(s.successProperty) {
12932                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12933                 }
12934                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12935                 if (s.id) {
12936                         var g = this.getJsonAccessor(s.id);
12937                         this.getId = function(rec) {
12938                                 var r = g(rec);  
12939                                 return (r === undefined || r === "") ? null : r;
12940                         };
12941                 } else {
12942                         this.getId = function(){return null;};
12943                 }
12944             this.ef = [];
12945             for(var jj = 0; jj < fl; jj++){
12946                 f = fi[jj];
12947                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12948                 this.ef[jj] = this.getJsonAccessor(map);
12949             }
12950         }
12951
12952         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12953         if(s.totalProperty){
12954             var vt = parseInt(this.getTotal(o), 10);
12955             if(!isNaN(vt)){
12956                 totalRecords = vt;
12957             }
12958         }
12959         if(s.successProperty){
12960             var vs = this.getSuccess(o);
12961             if(vs === false || vs === 'false'){
12962                 success = false;
12963             }
12964         }
12965         var records = [];
12966         for(var i = 0; i < c; i++){
12967                 var n = root[i];
12968             var values = {};
12969             var id = this.getId(n);
12970             for(var j = 0; j < fl; j++){
12971                 f = fi[j];
12972             var v = this.ef[j](n);
12973             if (!f.convert) {
12974                 Roo.log('missing convert for ' + f.name);
12975                 Roo.log(f);
12976                 continue;
12977             }
12978             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12979             }
12980             var record = new Record(values, id);
12981             record.json = n;
12982             records[i] = record;
12983         }
12984         return {
12985             raw : o,
12986             success : success,
12987             records : records,
12988             totalRecords : totalRecords
12989         };
12990     }
12991 });/*
12992  * Based on:
12993  * Ext JS Library 1.1.1
12994  * Copyright(c) 2006-2007, Ext JS, LLC.
12995  *
12996  * Originally Released Under LGPL - original licence link has changed is not relivant.
12997  *
12998  * Fork - LGPL
12999  * <script type="text/javascript">
13000  */
13001
13002 /**
13003  * @class Roo.data.ArrayReader
13004  * @extends Roo.data.DataReader
13005  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13006  * Each element of that Array represents a row of data fields. The
13007  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13008  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13009  * <p>
13010  * Example code:.
13011  * <pre><code>
13012 var RecordDef = Roo.data.Record.create([
13013     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13014     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13015 ]);
13016 var myReader = new Roo.data.ArrayReader({
13017     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13018 }, RecordDef);
13019 </code></pre>
13020  * <p>
13021  * This would consume an Array like this:
13022  * <pre><code>
13023 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13024   </code></pre>
13025  
13026  * @constructor
13027  * Create a new JsonReader
13028  * @param {Object} meta Metadata configuration options.
13029  * @param {Object|Array} recordType Either an Array of field definition objects
13030  * 
13031  * @cfg {Array} fields Array of field definition objects
13032  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13033  * as specified to {@link Roo.data.Record#create},
13034  * or an {@link Roo.data.Record} object
13035  *
13036  * 
13037  * created using {@link Roo.data.Record#create}.
13038  */
13039 Roo.data.ArrayReader = function(meta, recordType){
13040     
13041      
13042     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13043 };
13044
13045 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13046     /**
13047      * Create a data block containing Roo.data.Records from an XML document.
13048      * @param {Object} o An Array of row objects which represents the dataset.
13049      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13050      * a cache of Roo.data.Records.
13051      */
13052     readRecords : function(o){
13053         var sid = this.meta ? this.meta.id : null;
13054         var recordType = this.recordType, fields = recordType.prototype.fields;
13055         var records = [];
13056         var root = o;
13057             for(var i = 0; i < root.length; i++){
13058                     var n = root[i];
13059                 var values = {};
13060                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13061                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13062                 var f = fields.items[j];
13063                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13064                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13065                 v = f.convert(v);
13066                 values[f.name] = v;
13067             }
13068                 var record = new recordType(values, id);
13069                 record.json = n;
13070                 records[records.length] = record;
13071             }
13072             return {
13073                 records : records,
13074                 totalRecords : records.length
13075             };
13076     }
13077 });/*
13078  * - LGPL
13079  * * 
13080  */
13081
13082 /**
13083  * @class Roo.bootstrap.ComboBox
13084  * @extends Roo.bootstrap.TriggerField
13085  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13086  * @cfg {Boolean} append (true|false) default false
13087  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13088  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13089  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13090  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13091  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13092  * @cfg {Boolean} animate default true
13093  * @cfg {Boolean} emptyResultText only for touch device
13094  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13095  * @cfg {String} emptyTitle default ''
13096  * @constructor
13097  * Create a new ComboBox.
13098  * @param {Object} config Configuration options
13099  */
13100 Roo.bootstrap.ComboBox = function(config){
13101     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13102     this.addEvents({
13103         /**
13104          * @event expand
13105          * Fires when the dropdown list is expanded
13106         * @param {Roo.bootstrap.ComboBox} combo This combo box
13107         */
13108         'expand' : true,
13109         /**
13110          * @event collapse
13111          * Fires when the dropdown list is collapsed
13112         * @param {Roo.bootstrap.ComboBox} combo This combo box
13113         */
13114         'collapse' : true,
13115         /**
13116          * @event beforeselect
13117          * Fires before a list item is selected. Return false to cancel the selection.
13118         * @param {Roo.bootstrap.ComboBox} combo This combo box
13119         * @param {Roo.data.Record} record The data record returned from the underlying store
13120         * @param {Number} index The index of the selected item in the dropdown list
13121         */
13122         'beforeselect' : true,
13123         /**
13124          * @event select
13125          * Fires when a list item is selected
13126         * @param {Roo.bootstrap.ComboBox} combo This combo box
13127         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13128         * @param {Number} index The index of the selected item in the dropdown list
13129         */
13130         'select' : true,
13131         /**
13132          * @event beforequery
13133          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13134          * The event object passed has these properties:
13135         * @param {Roo.bootstrap.ComboBox} combo This combo box
13136         * @param {String} query The query
13137         * @param {Boolean} forceAll true to force "all" query
13138         * @param {Boolean} cancel true to cancel the query
13139         * @param {Object} e The query event object
13140         */
13141         'beforequery': true,
13142          /**
13143          * @event add
13144          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13145         * @param {Roo.bootstrap.ComboBox} combo This combo box
13146         */
13147         'add' : true,
13148         /**
13149          * @event edit
13150          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13151         * @param {Roo.bootstrap.ComboBox} combo This combo box
13152         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13153         */
13154         'edit' : true,
13155         /**
13156          * @event remove
13157          * Fires when the remove value from the combobox array
13158         * @param {Roo.bootstrap.ComboBox} combo This combo box
13159         */
13160         'remove' : true,
13161         /**
13162          * @event afterremove
13163          * Fires when the remove value from the combobox array
13164         * @param {Roo.bootstrap.ComboBox} combo This combo box
13165         */
13166         'afterremove' : true,
13167         /**
13168          * @event specialfilter
13169          * Fires when specialfilter
13170             * @param {Roo.bootstrap.ComboBox} combo This combo box
13171             */
13172         'specialfilter' : true,
13173         /**
13174          * @event tick
13175          * Fires when tick the element
13176             * @param {Roo.bootstrap.ComboBox} combo This combo box
13177             */
13178         'tick' : true,
13179         /**
13180          * @event touchviewdisplay
13181          * Fires when touch view require special display (default is using displayField)
13182             * @param {Roo.bootstrap.ComboBox} combo This combo box
13183             * @param {Object} cfg set html .
13184             */
13185         'touchviewdisplay' : true
13186         
13187     });
13188     
13189     this.item = [];
13190     this.tickItems = [];
13191     
13192     this.selectedIndex = -1;
13193     if(this.mode == 'local'){
13194         if(config.queryDelay === undefined){
13195             this.queryDelay = 10;
13196         }
13197         if(config.minChars === undefined){
13198             this.minChars = 0;
13199         }
13200     }
13201 };
13202
13203 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13204      
13205     /**
13206      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13207      * rendering into an Roo.Editor, defaults to false)
13208      */
13209     /**
13210      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13211      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13212      */
13213     /**
13214      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13215      */
13216     /**
13217      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13218      * the dropdown list (defaults to undefined, with no header element)
13219      */
13220
13221      /**
13222      * @cfg {String/Roo.Template} tpl The template to use to render the output
13223      */
13224      
13225      /**
13226      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13227      */
13228     listWidth: undefined,
13229     /**
13230      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13231      * mode = 'remote' or 'text' if mode = 'local')
13232      */
13233     displayField: undefined,
13234     
13235     /**
13236      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13237      * mode = 'remote' or 'value' if mode = 'local'). 
13238      * Note: use of a valueField requires the user make a selection
13239      * in order for a value to be mapped.
13240      */
13241     valueField: undefined,
13242     /**
13243      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13244      */
13245     modalTitle : '',
13246     
13247     /**
13248      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13249      * field's data value (defaults to the underlying DOM element's name)
13250      */
13251     hiddenName: undefined,
13252     /**
13253      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13254      */
13255     listClass: '',
13256     /**
13257      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13258      */
13259     selectedClass: 'active',
13260     
13261     /**
13262      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13263      */
13264     shadow:'sides',
13265     /**
13266      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13267      * anchor positions (defaults to 'tl-bl')
13268      */
13269     listAlign: 'tl-bl?',
13270     /**
13271      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13272      */
13273     maxHeight: 300,
13274     /**
13275      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13276      * query specified by the allQuery config option (defaults to 'query')
13277      */
13278     triggerAction: 'query',
13279     /**
13280      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13281      * (defaults to 4, does not apply if editable = false)
13282      */
13283     minChars : 4,
13284     /**
13285      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13286      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13287      */
13288     typeAhead: false,
13289     /**
13290      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13291      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13292      */
13293     queryDelay: 500,
13294     /**
13295      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13296      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13297      */
13298     pageSize: 0,
13299     /**
13300      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13301      * when editable = true (defaults to false)
13302      */
13303     selectOnFocus:false,
13304     /**
13305      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13306      */
13307     queryParam: 'query',
13308     /**
13309      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13310      * when mode = 'remote' (defaults to 'Loading...')
13311      */
13312     loadingText: 'Loading...',
13313     /**
13314      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13315      */
13316     resizable: false,
13317     /**
13318      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13319      */
13320     handleHeight : 8,
13321     /**
13322      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13323      * traditional select (defaults to true)
13324      */
13325     editable: true,
13326     /**
13327      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13328      */
13329     allQuery: '',
13330     /**
13331      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13332      */
13333     mode: 'remote',
13334     /**
13335      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13336      * listWidth has a higher value)
13337      */
13338     minListWidth : 70,
13339     /**
13340      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13341      * allow the user to set arbitrary text into the field (defaults to false)
13342      */
13343     forceSelection:false,
13344     /**
13345      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13346      * if typeAhead = true (defaults to 250)
13347      */
13348     typeAheadDelay : 250,
13349     /**
13350      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13351      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13352      */
13353     valueNotFoundText : undefined,
13354     /**
13355      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13356      */
13357     blockFocus : false,
13358     
13359     /**
13360      * @cfg {Boolean} disableClear Disable showing of clear button.
13361      */
13362     disableClear : false,
13363     /**
13364      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13365      */
13366     alwaysQuery : false,
13367     
13368     /**
13369      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13370      */
13371     multiple : false,
13372     
13373     /**
13374      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13375      */
13376     invalidClass : "has-warning",
13377     
13378     /**
13379      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13380      */
13381     validClass : "has-success",
13382     
13383     /**
13384      * @cfg {Boolean} specialFilter (true|false) special filter default false
13385      */
13386     specialFilter : false,
13387     
13388     /**
13389      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13390      */
13391     mobileTouchView : true,
13392     
13393     /**
13394      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13395      */
13396     useNativeIOS : false,
13397     
13398     /**
13399      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13400      */
13401     mobile_restrict_height : false,
13402     
13403     ios_options : false,
13404     
13405     //private
13406     addicon : false,
13407     editicon: false,
13408     
13409     page: 0,
13410     hasQuery: false,
13411     append: false,
13412     loadNext: false,
13413     autoFocus : true,
13414     tickable : false,
13415     btnPosition : 'right',
13416     triggerList : true,
13417     showToggleBtn : true,
13418     animate : true,
13419     emptyResultText: 'Empty',
13420     triggerText : 'Select',
13421     emptyTitle : '',
13422     
13423     // element that contains real text value.. (when hidden is used..)
13424     
13425     getAutoCreate : function()
13426     {   
13427         var cfg = false;
13428         //render
13429         /*
13430          * Render classic select for iso
13431          */
13432         
13433         if(Roo.isIOS && this.useNativeIOS){
13434             cfg = this.getAutoCreateNativeIOS();
13435             return cfg;
13436         }
13437         
13438         /*
13439          * Touch Devices
13440          */
13441         
13442         if(Roo.isTouch && this.mobileTouchView){
13443             cfg = this.getAutoCreateTouchView();
13444             return cfg;;
13445         }
13446         
13447         /*
13448          *  Normal ComboBox
13449          */
13450         if(!this.tickable){
13451             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13452             return cfg;
13453         }
13454         
13455         /*
13456          *  ComboBox with tickable selections
13457          */
13458              
13459         var align = this.labelAlign || this.parentLabelAlign();
13460         
13461         cfg = {
13462             cls : 'form-group roo-combobox-tickable' //input-group
13463         };
13464         
13465         var btn_text_select = '';
13466         var btn_text_done = '';
13467         var btn_text_cancel = '';
13468         
13469         if (this.btn_text_show) {
13470             btn_text_select = 'Select';
13471             btn_text_done = 'Done';
13472             btn_text_cancel = 'Cancel'; 
13473         }
13474         
13475         var buttons = {
13476             tag : 'div',
13477             cls : 'tickable-buttons',
13478             cn : [
13479                 {
13480                     tag : 'button',
13481                     type : 'button',
13482                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13483                     //html : this.triggerText
13484                     html: btn_text_select
13485                 },
13486                 {
13487                     tag : 'button',
13488                     type : 'button',
13489                     name : 'ok',
13490                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13491                     //html : 'Done'
13492                     html: btn_text_done
13493                 },
13494                 {
13495                     tag : 'button',
13496                     type : 'button',
13497                     name : 'cancel',
13498                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13499                     //html : 'Cancel'
13500                     html: btn_text_cancel
13501                 }
13502             ]
13503         };
13504         
13505         if(this.editable){
13506             buttons.cn.unshift({
13507                 tag: 'input',
13508                 cls: 'roo-select2-search-field-input'
13509             });
13510         }
13511         
13512         var _this = this;
13513         
13514         Roo.each(buttons.cn, function(c){
13515             if (_this.size) {
13516                 c.cls += ' btn-' + _this.size;
13517             }
13518
13519             if (_this.disabled) {
13520                 c.disabled = true;
13521             }
13522         });
13523         
13524         var box = {
13525             tag: 'div',
13526             style : 'display: contents',
13527             cn: [
13528                 {
13529                     tag: 'input',
13530                     type : 'hidden',
13531                     cls: 'form-hidden-field'
13532                 },
13533                 {
13534                     tag: 'ul',
13535                     cls: 'roo-select2-choices',
13536                     cn:[
13537                         {
13538                             tag: 'li',
13539                             cls: 'roo-select2-search-field',
13540                             cn: [
13541                                 buttons
13542                             ]
13543                         }
13544                     ]
13545                 }
13546             ]
13547         };
13548         
13549         var combobox = {
13550             cls: 'roo-select2-container input-group roo-select2-container-multi',
13551             cn: [
13552                 
13553                 box
13554 //                {
13555 //                    tag: 'ul',
13556 //                    cls: 'typeahead typeahead-long dropdown-menu',
13557 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13558 //                }
13559             ]
13560         };
13561         
13562         if(this.hasFeedback && !this.allowBlank){
13563             
13564             var feedback = {
13565                 tag: 'span',
13566                 cls: 'glyphicon form-control-feedback'
13567             };
13568
13569             combobox.cn.push(feedback);
13570         }
13571         
13572         var indicator = {
13573             tag : 'i',
13574             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13575             tooltip : 'This field is required'
13576         };
13577         if (Roo.bootstrap.version == 4) {
13578             indicator = {
13579                 tag : 'i',
13580                 style : 'display:none'
13581             };
13582         }
13583         if (align ==='left' && this.fieldLabel.length) {
13584             
13585             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13586             
13587             cfg.cn = [
13588                 indicator,
13589                 {
13590                     tag: 'label',
13591                     'for' :  id,
13592                     cls : 'control-label col-form-label',
13593                     html : this.fieldLabel
13594
13595                 },
13596                 {
13597                     cls : "", 
13598                     cn: [
13599                         combobox
13600                     ]
13601                 }
13602
13603             ];
13604             
13605             var labelCfg = cfg.cn[1];
13606             var contentCfg = cfg.cn[2];
13607             
13608
13609             if(this.indicatorpos == 'right'){
13610                 
13611                 cfg.cn = [
13612                     {
13613                         tag: 'label',
13614                         'for' :  id,
13615                         cls : 'control-label col-form-label',
13616                         cn : [
13617                             {
13618                                 tag : 'span',
13619                                 html : this.fieldLabel
13620                             },
13621                             indicator
13622                         ]
13623                     },
13624                     {
13625                         cls : "",
13626                         cn: [
13627                             combobox
13628                         ]
13629                     }
13630
13631                 ];
13632                 
13633                 
13634                 
13635                 labelCfg = cfg.cn[0];
13636                 contentCfg = cfg.cn[1];
13637             
13638             }
13639             
13640             if(this.labelWidth > 12){
13641                 labelCfg.style = "width: " + this.labelWidth + 'px';
13642             }
13643             
13644             if(this.labelWidth < 13 && this.labelmd == 0){
13645                 this.labelmd = this.labelWidth;
13646             }
13647             
13648             if(this.labellg > 0){
13649                 labelCfg.cls += ' col-lg-' + this.labellg;
13650                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13651             }
13652             
13653             if(this.labelmd > 0){
13654                 labelCfg.cls += ' col-md-' + this.labelmd;
13655                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13656             }
13657             
13658             if(this.labelsm > 0){
13659                 labelCfg.cls += ' col-sm-' + this.labelsm;
13660                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13661             }
13662             
13663             if(this.labelxs > 0){
13664                 labelCfg.cls += ' col-xs-' + this.labelxs;
13665                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13666             }
13667                 
13668                 
13669         } else if ( this.fieldLabel.length) {
13670 //                Roo.log(" label");
13671                  cfg.cn = [
13672                    indicator,
13673                     {
13674                         tag: 'label',
13675                         //cls : 'input-group-addon',
13676                         html : this.fieldLabel
13677                     },
13678                     combobox
13679                 ];
13680                 
13681                 if(this.indicatorpos == 'right'){
13682                     cfg.cn = [
13683                         {
13684                             tag: 'label',
13685                             //cls : 'input-group-addon',
13686                             html : this.fieldLabel
13687                         },
13688                         indicator,
13689                         combobox
13690                     ];
13691                     
13692                 }
13693
13694         } else {
13695             
13696 //                Roo.log(" no label && no align");
13697                 cfg = combobox
13698                      
13699                 
13700         }
13701          
13702         var settings=this;
13703         ['xs','sm','md','lg'].map(function(size){
13704             if (settings[size]) {
13705                 cfg.cls += ' col-' + size + '-' + settings[size];
13706             }
13707         });
13708         
13709         return cfg;
13710         
13711     },
13712     
13713     _initEventsCalled : false,
13714     
13715     // private
13716     initEvents: function()
13717     {   
13718         if (this._initEventsCalled) { // as we call render... prevent looping...
13719             return;
13720         }
13721         this._initEventsCalled = true;
13722         
13723         if (!this.store) {
13724             throw "can not find store for combo";
13725         }
13726         
13727         this.indicator = this.indicatorEl();
13728         
13729         this.store = Roo.factory(this.store, Roo.data);
13730         this.store.parent = this;
13731         
13732         // if we are building from html. then this element is so complex, that we can not really
13733         // use the rendered HTML.
13734         // so we have to trash and replace the previous code.
13735         if (Roo.XComponent.build_from_html) {
13736             // remove this element....
13737             var e = this.el.dom, k=0;
13738             while (e ) { e = e.previousSibling;  ++k;}
13739
13740             this.el.remove();
13741             
13742             this.el=false;
13743             this.rendered = false;
13744             
13745             this.render(this.parent().getChildContainer(true), k);
13746         }
13747         
13748         if(Roo.isIOS && this.useNativeIOS){
13749             this.initIOSView();
13750             return;
13751         }
13752         
13753         /*
13754          * Touch Devices
13755          */
13756         
13757         if(Roo.isTouch && this.mobileTouchView){
13758             this.initTouchView();
13759             return;
13760         }
13761         
13762         if(this.tickable){
13763             this.initTickableEvents();
13764             return;
13765         }
13766         
13767         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13768         
13769         if(this.hiddenName){
13770             
13771             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13772             
13773             this.hiddenField.dom.value =
13774                 this.hiddenValue !== undefined ? this.hiddenValue :
13775                 this.value !== undefined ? this.value : '';
13776
13777             // prevent input submission
13778             this.el.dom.removeAttribute('name');
13779             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13780              
13781              
13782         }
13783         //if(Roo.isGecko){
13784         //    this.el.dom.setAttribute('autocomplete', 'off');
13785         //}
13786         
13787         var cls = 'x-combo-list';
13788         
13789         //this.list = new Roo.Layer({
13790         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13791         //});
13792         
13793         var _this = this;
13794         
13795         (function(){
13796             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13797             _this.list.setWidth(lw);
13798         }).defer(100);
13799         
13800         this.list.on('mouseover', this.onViewOver, this);
13801         this.list.on('mousemove', this.onViewMove, this);
13802         this.list.on('scroll', this.onViewScroll, this);
13803         
13804         /*
13805         this.list.swallowEvent('mousewheel');
13806         this.assetHeight = 0;
13807
13808         if(this.title){
13809             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13810             this.assetHeight += this.header.getHeight();
13811         }
13812
13813         this.innerList = this.list.createChild({cls:cls+'-inner'});
13814         this.innerList.on('mouseover', this.onViewOver, this);
13815         this.innerList.on('mousemove', this.onViewMove, this);
13816         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13817         
13818         if(this.allowBlank && !this.pageSize && !this.disableClear){
13819             this.footer = this.list.createChild({cls:cls+'-ft'});
13820             this.pageTb = new Roo.Toolbar(this.footer);
13821            
13822         }
13823         if(this.pageSize){
13824             this.footer = this.list.createChild({cls:cls+'-ft'});
13825             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13826                     {pageSize: this.pageSize});
13827             
13828         }
13829         
13830         if (this.pageTb && this.allowBlank && !this.disableClear) {
13831             var _this = this;
13832             this.pageTb.add(new Roo.Toolbar.Fill(), {
13833                 cls: 'x-btn-icon x-btn-clear',
13834                 text: '&#160;',
13835                 handler: function()
13836                 {
13837                     _this.collapse();
13838                     _this.clearValue();
13839                     _this.onSelect(false, -1);
13840                 }
13841             });
13842         }
13843         if (this.footer) {
13844             this.assetHeight += this.footer.getHeight();
13845         }
13846         */
13847             
13848         if(!this.tpl){
13849             this.tpl = Roo.bootstrap.version == 4 ?
13850                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13851                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13852         }
13853
13854         this.view = new Roo.View(this.list, this.tpl, {
13855             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13856         });
13857         //this.view.wrapEl.setDisplayed(false);
13858         this.view.on('click', this.onViewClick, this);
13859         
13860         
13861         this.store.on('beforeload', this.onBeforeLoad, this);
13862         this.store.on('load', this.onLoad, this);
13863         this.store.on('loadexception', this.onLoadException, this);
13864         /*
13865         if(this.resizable){
13866             this.resizer = new Roo.Resizable(this.list,  {
13867                pinned:true, handles:'se'
13868             });
13869             this.resizer.on('resize', function(r, w, h){
13870                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13871                 this.listWidth = w;
13872                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13873                 this.restrictHeight();
13874             }, this);
13875             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13876         }
13877         */
13878         if(!this.editable){
13879             this.editable = true;
13880             this.setEditable(false);
13881         }
13882         
13883         /*
13884         
13885         if (typeof(this.events.add.listeners) != 'undefined') {
13886             
13887             this.addicon = this.wrap.createChild(
13888                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13889        
13890             this.addicon.on('click', function(e) {
13891                 this.fireEvent('add', this);
13892             }, this);
13893         }
13894         if (typeof(this.events.edit.listeners) != 'undefined') {
13895             
13896             this.editicon = this.wrap.createChild(
13897                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13898             if (this.addicon) {
13899                 this.editicon.setStyle('margin-left', '40px');
13900             }
13901             this.editicon.on('click', function(e) {
13902                 
13903                 // we fire even  if inothing is selected..
13904                 this.fireEvent('edit', this, this.lastData );
13905                 
13906             }, this);
13907         }
13908         */
13909         
13910         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13911             "up" : function(e){
13912                 this.inKeyMode = true;
13913                 this.selectPrev();
13914             },
13915
13916             "down" : function(e){
13917                 if(!this.isExpanded()){
13918                     this.onTriggerClick();
13919                 }else{
13920                     this.inKeyMode = true;
13921                     this.selectNext();
13922                 }
13923             },
13924
13925             "enter" : function(e){
13926 //                this.onViewClick();
13927                 //return true;
13928                 this.collapse();
13929                 
13930                 if(this.fireEvent("specialkey", this, e)){
13931                     this.onViewClick(false);
13932                 }
13933                 
13934                 return true;
13935             },
13936
13937             "esc" : function(e){
13938                 this.collapse();
13939             },
13940
13941             "tab" : function(e){
13942                 this.collapse();
13943                 
13944                 if(this.fireEvent("specialkey", this, e)){
13945                     this.onViewClick(false);
13946                 }
13947                 
13948                 return true;
13949             },
13950
13951             scope : this,
13952
13953             doRelay : function(foo, bar, hname){
13954                 if(hname == 'down' || this.scope.isExpanded()){
13955                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13956                 }
13957                 return true;
13958             },
13959
13960             forceKeyDown: true
13961         });
13962         
13963         
13964         this.queryDelay = Math.max(this.queryDelay || 10,
13965                 this.mode == 'local' ? 10 : 250);
13966         
13967         
13968         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13969         
13970         if(this.typeAhead){
13971             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13972         }
13973         if(this.editable !== false){
13974             this.inputEl().on("keyup", this.onKeyUp, this);
13975         }
13976         if(this.forceSelection){
13977             this.inputEl().on('blur', this.doForce, this);
13978         }
13979         
13980         if(this.multiple){
13981             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13982             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13983         }
13984     },
13985     
13986     initTickableEvents: function()
13987     {   
13988         this.createList();
13989         
13990         if(this.hiddenName){
13991             
13992             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13993             
13994             this.hiddenField.dom.value =
13995                 this.hiddenValue !== undefined ? this.hiddenValue :
13996                 this.value !== undefined ? this.value : '';
13997
13998             // prevent input submission
13999             this.el.dom.removeAttribute('name');
14000             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14001              
14002              
14003         }
14004         
14005 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14006         
14007         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14008         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14009         if(this.triggerList){
14010             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14011         }
14012          
14013         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14014         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14015         
14016         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14017         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14018         
14019         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14020         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14021         
14022         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14023         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14024         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14025         
14026         this.okBtn.hide();
14027         this.cancelBtn.hide();
14028         
14029         var _this = this;
14030         
14031         (function(){
14032             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14033             _this.list.setWidth(lw);
14034         }).defer(100);
14035         
14036         this.list.on('mouseover', this.onViewOver, this);
14037         this.list.on('mousemove', this.onViewMove, this);
14038         
14039         this.list.on('scroll', this.onViewScroll, this);
14040         
14041         if(!this.tpl){
14042             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14043                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14044         }
14045
14046         this.view = new Roo.View(this.list, this.tpl, {
14047             singleSelect:true,
14048             tickable:true,
14049             parent:this,
14050             store: this.store,
14051             selectedClass: this.selectedClass
14052         });
14053         
14054         //this.view.wrapEl.setDisplayed(false);
14055         this.view.on('click', this.onViewClick, this);
14056         
14057         
14058         
14059         this.store.on('beforeload', this.onBeforeLoad, this);
14060         this.store.on('load', this.onLoad, this);
14061         this.store.on('loadexception', this.onLoadException, this);
14062         
14063         if(this.editable){
14064             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14065                 "up" : function(e){
14066                     this.inKeyMode = true;
14067                     this.selectPrev();
14068                 },
14069
14070                 "down" : function(e){
14071                     this.inKeyMode = true;
14072                     this.selectNext();
14073                 },
14074
14075                 "enter" : function(e){
14076                     if(this.fireEvent("specialkey", this, e)){
14077                         this.onViewClick(false);
14078                     }
14079                     
14080                     return true;
14081                 },
14082
14083                 "esc" : function(e){
14084                     this.onTickableFooterButtonClick(e, false, false);
14085                 },
14086
14087                 "tab" : function(e){
14088                     this.fireEvent("specialkey", this, e);
14089                     
14090                     this.onTickableFooterButtonClick(e, false, false);
14091                     
14092                     return true;
14093                 },
14094
14095                 scope : this,
14096
14097                 doRelay : function(e, fn, key){
14098                     if(this.scope.isExpanded()){
14099                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14100                     }
14101                     return true;
14102                 },
14103
14104                 forceKeyDown: true
14105             });
14106         }
14107         
14108         this.queryDelay = Math.max(this.queryDelay || 10,
14109                 this.mode == 'local' ? 10 : 250);
14110         
14111         
14112         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14113         
14114         if(this.typeAhead){
14115             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14116         }
14117         
14118         if(this.editable !== false){
14119             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14120         }
14121         
14122         this.indicator = this.indicatorEl();
14123         
14124         if(this.indicator){
14125             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14126             this.indicator.hide();
14127         }
14128         
14129     },
14130
14131     onDestroy : function(){
14132         if(this.view){
14133             this.view.setStore(null);
14134             this.view.el.removeAllListeners();
14135             this.view.el.remove();
14136             this.view.purgeListeners();
14137         }
14138         if(this.list){
14139             this.list.dom.innerHTML  = '';
14140         }
14141         
14142         if(this.store){
14143             this.store.un('beforeload', this.onBeforeLoad, this);
14144             this.store.un('load', this.onLoad, this);
14145             this.store.un('loadexception', this.onLoadException, this);
14146         }
14147         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14148     },
14149
14150     // private
14151     fireKey : function(e){
14152         if(e.isNavKeyPress() && !this.list.isVisible()){
14153             this.fireEvent("specialkey", this, e);
14154         }
14155     },
14156
14157     // private
14158     onResize: function(w, h){
14159 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14160 //        
14161 //        if(typeof w != 'number'){
14162 //            // we do not handle it!?!?
14163 //            return;
14164 //        }
14165 //        var tw = this.trigger.getWidth();
14166 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14167 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14168 //        var x = w - tw;
14169 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14170 //            
14171 //        //this.trigger.setStyle('left', x+'px');
14172 //        
14173 //        if(this.list && this.listWidth === undefined){
14174 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14175 //            this.list.setWidth(lw);
14176 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14177 //        }
14178         
14179     
14180         
14181     },
14182
14183     /**
14184      * Allow or prevent the user from directly editing the field text.  If false is passed,
14185      * the user will only be able to select from the items defined in the dropdown list.  This method
14186      * is the runtime equivalent of setting the 'editable' config option at config time.
14187      * @param {Boolean} value True to allow the user to directly edit the field text
14188      */
14189     setEditable : function(value){
14190         if(value == this.editable){
14191             return;
14192         }
14193         this.editable = value;
14194         if(!value){
14195             this.inputEl().dom.setAttribute('readOnly', true);
14196             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14197             this.inputEl().addClass('x-combo-noedit');
14198         }else{
14199             this.inputEl().dom.setAttribute('readOnly', false);
14200             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14201             this.inputEl().removeClass('x-combo-noedit');
14202         }
14203     },
14204
14205     // private
14206     
14207     onBeforeLoad : function(combo,opts){
14208         if(!this.hasFocus){
14209             return;
14210         }
14211          if (!opts.add) {
14212             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14213          }
14214         this.restrictHeight();
14215         this.selectedIndex = -1;
14216     },
14217
14218     // private
14219     onLoad : function(){
14220         
14221         this.hasQuery = false;
14222         
14223         if(!this.hasFocus){
14224             return;
14225         }
14226         
14227         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14228             this.loading.hide();
14229         }
14230         
14231         if(this.store.getCount() > 0){
14232             
14233             this.expand();
14234             this.restrictHeight();
14235             if(this.lastQuery == this.allQuery){
14236                 if(this.editable && !this.tickable){
14237                     this.inputEl().dom.select();
14238                 }
14239                 
14240                 if(
14241                     !this.selectByValue(this.value, true) &&
14242                     this.autoFocus && 
14243                     (
14244                         !this.store.lastOptions ||
14245                         typeof(this.store.lastOptions.add) == 'undefined' || 
14246                         this.store.lastOptions.add != true
14247                     )
14248                 ){
14249                     this.select(0, true);
14250                 }
14251             }else{
14252                 if(this.autoFocus){
14253                     this.selectNext();
14254                 }
14255                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14256                     this.taTask.delay(this.typeAheadDelay);
14257                 }
14258             }
14259         }else{
14260             this.onEmptyResults();
14261         }
14262         
14263         //this.el.focus();
14264     },
14265     // private
14266     onLoadException : function()
14267     {
14268         this.hasQuery = false;
14269         
14270         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14271             this.loading.hide();
14272         }
14273         
14274         if(this.tickable && this.editable){
14275             return;
14276         }
14277         
14278         this.collapse();
14279         // only causes errors at present
14280         //Roo.log(this.store.reader.jsonData);
14281         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14282             // fixme
14283             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14284         //}
14285         
14286         
14287     },
14288     // private
14289     onTypeAhead : function(){
14290         if(this.store.getCount() > 0){
14291             var r = this.store.getAt(0);
14292             var newValue = r.data[this.displayField];
14293             var len = newValue.length;
14294             var selStart = this.getRawValue().length;
14295             
14296             if(selStart != len){
14297                 this.setRawValue(newValue);
14298                 this.selectText(selStart, newValue.length);
14299             }
14300         }
14301     },
14302
14303     // private
14304     onSelect : function(record, index){
14305         
14306         if(this.fireEvent('beforeselect', this, record, index) !== false){
14307         
14308             this.setFromData(index > -1 ? record.data : false);
14309             
14310             this.collapse();
14311             this.fireEvent('select', this, record, index);
14312         }
14313     },
14314
14315     /**
14316      * Returns the currently selected field value or empty string if no value is set.
14317      * @return {String} value The selected value
14318      */
14319     getValue : function()
14320     {
14321         if(Roo.isIOS && this.useNativeIOS){
14322             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14323         }
14324         
14325         if(this.multiple){
14326             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14327         }
14328         
14329         if(this.valueField){
14330             return typeof this.value != 'undefined' ? this.value : '';
14331         }else{
14332             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14333         }
14334     },
14335     
14336     getRawValue : function()
14337     {
14338         if(Roo.isIOS && this.useNativeIOS){
14339             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14340         }
14341         
14342         var v = this.inputEl().getValue();
14343         
14344         return v;
14345     },
14346
14347     /**
14348      * Clears any text/value currently set in the field
14349      */
14350     clearValue : function(){
14351         
14352         if(this.hiddenField){
14353             this.hiddenField.dom.value = '';
14354         }
14355         this.value = '';
14356         this.setRawValue('');
14357         this.lastSelectionText = '';
14358         this.lastData = false;
14359         
14360         var close = this.closeTriggerEl();
14361         
14362         if(close){
14363             close.hide();
14364         }
14365         
14366         this.validate();
14367         
14368     },
14369
14370     /**
14371      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14372      * will be displayed in the field.  If the value does not match the data value of an existing item,
14373      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14374      * Otherwise the field will be blank (although the value will still be set).
14375      * @param {String} value The value to match
14376      */
14377     setValue : function(v)
14378     {
14379         if(Roo.isIOS && this.useNativeIOS){
14380             this.setIOSValue(v);
14381             return;
14382         }
14383         
14384         if(this.multiple){
14385             this.syncValue();
14386             return;
14387         }
14388         
14389         var text = v;
14390         if(this.valueField){
14391             var r = this.findRecord(this.valueField, v);
14392             if(r){
14393                 text = r.data[this.displayField];
14394             }else if(this.valueNotFoundText !== undefined){
14395                 text = this.valueNotFoundText;
14396             }
14397         }
14398         this.lastSelectionText = text;
14399         if(this.hiddenField){
14400             this.hiddenField.dom.value = v;
14401         }
14402         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14403         this.value = v;
14404         
14405         var close = this.closeTriggerEl();
14406         
14407         if(close){
14408             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14409         }
14410         
14411         this.validate();
14412     },
14413     /**
14414      * @property {Object} the last set data for the element
14415      */
14416     
14417     lastData : false,
14418     /**
14419      * Sets the value of the field based on a object which is related to the record format for the store.
14420      * @param {Object} value the value to set as. or false on reset?
14421      */
14422     setFromData : function(o){
14423         
14424         if(this.multiple){
14425             this.addItem(o);
14426             return;
14427         }
14428             
14429         var dv = ''; // display value
14430         var vv = ''; // value value..
14431         this.lastData = o;
14432         if (this.displayField) {
14433             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14434         } else {
14435             // this is an error condition!!!
14436             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14437         }
14438         
14439         if(this.valueField){
14440             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14441         }
14442         
14443         var close = this.closeTriggerEl();
14444         
14445         if(close){
14446             if(dv.length || vv * 1 > 0){
14447                 close.show() ;
14448                 this.blockFocus=true;
14449             } else {
14450                 close.hide();
14451             }             
14452         }
14453         
14454         if(this.hiddenField){
14455             this.hiddenField.dom.value = vv;
14456             
14457             this.lastSelectionText = dv;
14458             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14459             this.value = vv;
14460             return;
14461         }
14462         // no hidden field.. - we store the value in 'value', but still display
14463         // display field!!!!
14464         this.lastSelectionText = dv;
14465         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14466         this.value = vv;
14467         
14468         
14469         
14470     },
14471     // private
14472     reset : function(){
14473         // overridden so that last data is reset..
14474         
14475         if(this.multiple){
14476             this.clearItem();
14477             return;
14478         }
14479         
14480         this.setValue(this.originalValue);
14481         //this.clearInvalid();
14482         this.lastData = false;
14483         if (this.view) {
14484             this.view.clearSelections();
14485         }
14486         
14487         this.validate();
14488     },
14489     // private
14490     findRecord : function(prop, value){
14491         var record;
14492         if(this.store.getCount() > 0){
14493             this.store.each(function(r){
14494                 if(r.data[prop] == value){
14495                     record = r;
14496                     return false;
14497                 }
14498                 return true;
14499             });
14500         }
14501         return record;
14502     },
14503     
14504     getName: function()
14505     {
14506         // returns hidden if it's set..
14507         if (!this.rendered) {return ''};
14508         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14509         
14510     },
14511     // private
14512     onViewMove : function(e, t){
14513         this.inKeyMode = false;
14514     },
14515
14516     // private
14517     onViewOver : function(e, t){
14518         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14519             return;
14520         }
14521         var item = this.view.findItemFromChild(t);
14522         
14523         if(item){
14524             var index = this.view.indexOf(item);
14525             this.select(index, false);
14526         }
14527     },
14528
14529     // private
14530     onViewClick : function(view, doFocus, el, e)
14531     {
14532         var index = this.view.getSelectedIndexes()[0];
14533         
14534         var r = this.store.getAt(index);
14535         
14536         if(this.tickable){
14537             
14538             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14539                 return;
14540             }
14541             
14542             var rm = false;
14543             var _this = this;
14544             
14545             Roo.each(this.tickItems, function(v,k){
14546                 
14547                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14548                     Roo.log(v);
14549                     _this.tickItems.splice(k, 1);
14550                     
14551                     if(typeof(e) == 'undefined' && view == false){
14552                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14553                     }
14554                     
14555                     rm = true;
14556                     return;
14557                 }
14558             });
14559             
14560             if(rm){
14561                 return;
14562             }
14563             
14564             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14565                 this.tickItems.push(r.data);
14566             }
14567             
14568             if(typeof(e) == 'undefined' && view == false){
14569                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14570             }
14571                     
14572             return;
14573         }
14574         
14575         if(r){
14576             this.onSelect(r, index);
14577         }
14578         if(doFocus !== false && !this.blockFocus){
14579             this.inputEl().focus();
14580         }
14581     },
14582
14583     // private
14584     restrictHeight : function(){
14585         //this.innerList.dom.style.height = '';
14586         //var inner = this.innerList.dom;
14587         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14588         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14589         //this.list.beginUpdate();
14590         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14591         this.list.alignTo(this.inputEl(), this.listAlign);
14592         this.list.alignTo(this.inputEl(), this.listAlign);
14593         //this.list.endUpdate();
14594     },
14595
14596     // private
14597     onEmptyResults : function(){
14598         
14599         if(this.tickable && this.editable){
14600             this.hasFocus = false;
14601             this.restrictHeight();
14602             return;
14603         }
14604         
14605         this.collapse();
14606     },
14607
14608     /**
14609      * Returns true if the dropdown list is expanded, else false.
14610      */
14611     isExpanded : function(){
14612         return this.list.isVisible();
14613     },
14614
14615     /**
14616      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14617      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14618      * @param {String} value The data value of the item to select
14619      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14620      * selected item if it is not currently in view (defaults to true)
14621      * @return {Boolean} True if the value matched an item in the list, else false
14622      */
14623     selectByValue : function(v, scrollIntoView){
14624         if(v !== undefined && v !== null){
14625             var r = this.findRecord(this.valueField || this.displayField, v);
14626             if(r){
14627                 this.select(this.store.indexOf(r), scrollIntoView);
14628                 return true;
14629             }
14630         }
14631         return false;
14632     },
14633
14634     /**
14635      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14636      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14637      * @param {Number} index The zero-based index of the list item to select
14638      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14639      * selected item if it is not currently in view (defaults to true)
14640      */
14641     select : function(index, scrollIntoView){
14642         this.selectedIndex = index;
14643         this.view.select(index);
14644         if(scrollIntoView !== false){
14645             var el = this.view.getNode(index);
14646             /*
14647              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14648              */
14649             if(el){
14650                 this.list.scrollChildIntoView(el, false);
14651             }
14652         }
14653     },
14654
14655     // private
14656     selectNext : function(){
14657         var ct = this.store.getCount();
14658         if(ct > 0){
14659             if(this.selectedIndex == -1){
14660                 this.select(0);
14661             }else if(this.selectedIndex < ct-1){
14662                 this.select(this.selectedIndex+1);
14663             }
14664         }
14665     },
14666
14667     // private
14668     selectPrev : function(){
14669         var ct = this.store.getCount();
14670         if(ct > 0){
14671             if(this.selectedIndex == -1){
14672                 this.select(0);
14673             }else if(this.selectedIndex != 0){
14674                 this.select(this.selectedIndex-1);
14675             }
14676         }
14677     },
14678
14679     // private
14680     onKeyUp : function(e){
14681         if(this.editable !== false && !e.isSpecialKey()){
14682             this.lastKey = e.getKey();
14683             this.dqTask.delay(this.queryDelay);
14684         }
14685     },
14686
14687     // private
14688     validateBlur : function(){
14689         return !this.list || !this.list.isVisible();   
14690     },
14691
14692     // private
14693     initQuery : function(){
14694         
14695         var v = this.getRawValue();
14696         
14697         if(this.tickable && this.editable){
14698             v = this.tickableInputEl().getValue();
14699         }
14700         
14701         this.doQuery(v);
14702     },
14703
14704     // private
14705     doForce : function(){
14706         if(this.inputEl().dom.value.length > 0){
14707             this.inputEl().dom.value =
14708                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14709              
14710         }
14711     },
14712
14713     /**
14714      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14715      * query allowing the query action to be canceled if needed.
14716      * @param {String} query The SQL query to execute
14717      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14718      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14719      * saved in the current store (defaults to false)
14720      */
14721     doQuery : function(q, forceAll){
14722         
14723         if(q === undefined || q === null){
14724             q = '';
14725         }
14726         var qe = {
14727             query: q,
14728             forceAll: forceAll,
14729             combo: this,
14730             cancel:false
14731         };
14732         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14733             return false;
14734         }
14735         q = qe.query;
14736         
14737         forceAll = qe.forceAll;
14738         if(forceAll === true || (q.length >= this.minChars)){
14739             
14740             this.hasQuery = true;
14741             
14742             if(this.lastQuery != q || this.alwaysQuery){
14743                 this.lastQuery = q;
14744                 if(this.mode == 'local'){
14745                     this.selectedIndex = -1;
14746                     if(forceAll){
14747                         this.store.clearFilter();
14748                     }else{
14749                         
14750                         if(this.specialFilter){
14751                             this.fireEvent('specialfilter', this);
14752                             this.onLoad();
14753                             return;
14754                         }
14755                         
14756                         this.store.filter(this.displayField, q);
14757                     }
14758                     
14759                     this.store.fireEvent("datachanged", this.store);
14760                     
14761                     this.onLoad();
14762                     
14763                     
14764                 }else{
14765                     
14766                     this.store.baseParams[this.queryParam] = q;
14767                     
14768                     var options = {params : this.getParams(q)};
14769                     
14770                     if(this.loadNext){
14771                         options.add = true;
14772                         options.params.start = this.page * this.pageSize;
14773                     }
14774                     
14775                     this.store.load(options);
14776                     
14777                     /*
14778                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14779                      *  we should expand the list on onLoad
14780                      *  so command out it
14781                      */
14782 //                    this.expand();
14783                 }
14784             }else{
14785                 this.selectedIndex = -1;
14786                 this.onLoad();   
14787             }
14788         }
14789         
14790         this.loadNext = false;
14791     },
14792     
14793     // private
14794     getParams : function(q){
14795         var p = {};
14796         //p[this.queryParam] = q;
14797         
14798         if(this.pageSize){
14799             p.start = 0;
14800             p.limit = this.pageSize;
14801         }
14802         return p;
14803     },
14804
14805     /**
14806      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14807      */
14808     collapse : function(){
14809         if(!this.isExpanded()){
14810             return;
14811         }
14812         
14813         this.list.hide();
14814         
14815         this.hasFocus = false;
14816         
14817         if(this.tickable){
14818             this.okBtn.hide();
14819             this.cancelBtn.hide();
14820             this.trigger.show();
14821             
14822             if(this.editable){
14823                 this.tickableInputEl().dom.value = '';
14824                 this.tickableInputEl().blur();
14825             }
14826             
14827         }
14828         
14829         Roo.get(document).un('mousedown', this.collapseIf, this);
14830         Roo.get(document).un('mousewheel', this.collapseIf, this);
14831         if (!this.editable) {
14832             Roo.get(document).un('keydown', this.listKeyPress, this);
14833         }
14834         this.fireEvent('collapse', this);
14835         
14836         this.validate();
14837     },
14838
14839     // private
14840     collapseIf : function(e){
14841         var in_combo  = e.within(this.el);
14842         var in_list =  e.within(this.list);
14843         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14844         
14845         if (in_combo || in_list || is_list) {
14846             //e.stopPropagation();
14847             return;
14848         }
14849         
14850         if(this.tickable){
14851             this.onTickableFooterButtonClick(e, false, false);
14852         }
14853
14854         this.collapse();
14855         
14856     },
14857
14858     /**
14859      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14860      */
14861     expand : function(){
14862        
14863         if(this.isExpanded() || !this.hasFocus){
14864             return;
14865         }
14866         
14867         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14868         this.list.setWidth(lw);
14869         
14870         Roo.log('expand');
14871         
14872         this.list.show();
14873         
14874         this.restrictHeight();
14875         
14876         if(this.tickable){
14877             
14878             this.tickItems = Roo.apply([], this.item);
14879             
14880             this.okBtn.show();
14881             this.cancelBtn.show();
14882             this.trigger.hide();
14883             
14884             if(this.editable){
14885                 this.tickableInputEl().focus();
14886             }
14887             
14888         }
14889         
14890         Roo.get(document).on('mousedown', this.collapseIf, this);
14891         Roo.get(document).on('mousewheel', this.collapseIf, this);
14892         if (!this.editable) {
14893             Roo.get(document).on('keydown', this.listKeyPress, this);
14894         }
14895         
14896         this.fireEvent('expand', this);
14897     },
14898
14899     // private
14900     // Implements the default empty TriggerField.onTriggerClick function
14901     onTriggerClick : function(e)
14902     {
14903         Roo.log('trigger click');
14904         
14905         if(this.disabled || !this.triggerList){
14906             return;
14907         }
14908         
14909         this.page = 0;
14910         this.loadNext = false;
14911         
14912         if(this.isExpanded()){
14913             this.collapse();
14914             if (!this.blockFocus) {
14915                 this.inputEl().focus();
14916             }
14917             
14918         }else {
14919             this.hasFocus = true;
14920             if(this.triggerAction == 'all') {
14921                 this.doQuery(this.allQuery, true);
14922             } else {
14923                 this.doQuery(this.getRawValue());
14924             }
14925             if (!this.blockFocus) {
14926                 this.inputEl().focus();
14927             }
14928         }
14929     },
14930     
14931     onTickableTriggerClick : function(e)
14932     {
14933         if(this.disabled){
14934             return;
14935         }
14936         
14937         this.page = 0;
14938         this.loadNext = false;
14939         this.hasFocus = true;
14940         
14941         if(this.triggerAction == 'all') {
14942             this.doQuery(this.allQuery, true);
14943         } else {
14944             this.doQuery(this.getRawValue());
14945         }
14946     },
14947     
14948     onSearchFieldClick : function(e)
14949     {
14950         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14951             this.onTickableFooterButtonClick(e, false, false);
14952             return;
14953         }
14954         
14955         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14956             return;
14957         }
14958         
14959         this.page = 0;
14960         this.loadNext = false;
14961         this.hasFocus = true;
14962         
14963         if(this.triggerAction == 'all') {
14964             this.doQuery(this.allQuery, true);
14965         } else {
14966             this.doQuery(this.getRawValue());
14967         }
14968     },
14969     
14970     listKeyPress : function(e)
14971     {
14972         //Roo.log('listkeypress');
14973         // scroll to first matching element based on key pres..
14974         if (e.isSpecialKey()) {
14975             return false;
14976         }
14977         var k = String.fromCharCode(e.getKey()).toUpperCase();
14978         //Roo.log(k);
14979         var match  = false;
14980         var csel = this.view.getSelectedNodes();
14981         var cselitem = false;
14982         if (csel.length) {
14983             var ix = this.view.indexOf(csel[0]);
14984             cselitem  = this.store.getAt(ix);
14985             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14986                 cselitem = false;
14987             }
14988             
14989         }
14990         
14991         this.store.each(function(v) { 
14992             if (cselitem) {
14993                 // start at existing selection.
14994                 if (cselitem.id == v.id) {
14995                     cselitem = false;
14996                 }
14997                 return true;
14998             }
14999                 
15000             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15001                 match = this.store.indexOf(v);
15002                 return false;
15003             }
15004             return true;
15005         }, this);
15006         
15007         if (match === false) {
15008             return true; // no more action?
15009         }
15010         // scroll to?
15011         this.view.select(match);
15012         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15013         sn.scrollIntoView(sn.dom.parentNode, false);
15014     },
15015     
15016     onViewScroll : function(e, t){
15017         
15018         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){
15019             return;
15020         }
15021         
15022         this.hasQuery = true;
15023         
15024         this.loading = this.list.select('.loading', true).first();
15025         
15026         if(this.loading === null){
15027             this.list.createChild({
15028                 tag: 'div',
15029                 cls: 'loading roo-select2-more-results roo-select2-active',
15030                 html: 'Loading more results...'
15031             });
15032             
15033             this.loading = this.list.select('.loading', true).first();
15034             
15035             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15036             
15037             this.loading.hide();
15038         }
15039         
15040         this.loading.show();
15041         
15042         var _combo = this;
15043         
15044         this.page++;
15045         this.loadNext = true;
15046         
15047         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15048         
15049         return;
15050     },
15051     
15052     addItem : function(o)
15053     {   
15054         var dv = ''; // display value
15055         
15056         if (this.displayField) {
15057             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15058         } else {
15059             // this is an error condition!!!
15060             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15061         }
15062         
15063         if(!dv.length){
15064             return;
15065         }
15066         
15067         var choice = this.choices.createChild({
15068             tag: 'li',
15069             cls: 'roo-select2-search-choice',
15070             cn: [
15071                 {
15072                     tag: 'div',
15073                     html: dv
15074                 },
15075                 {
15076                     tag: 'a',
15077                     href: '#',
15078                     cls: 'roo-select2-search-choice-close fa fa-times',
15079                     tabindex: '-1'
15080                 }
15081             ]
15082             
15083         }, this.searchField);
15084         
15085         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15086         
15087         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15088         
15089         this.item.push(o);
15090         
15091         this.lastData = o;
15092         
15093         this.syncValue();
15094         
15095         this.inputEl().dom.value = '';
15096         
15097         this.validate();
15098     },
15099     
15100     onRemoveItem : function(e, _self, o)
15101     {
15102         e.preventDefault();
15103         
15104         this.lastItem = Roo.apply([], this.item);
15105         
15106         var index = this.item.indexOf(o.data) * 1;
15107         
15108         if( index < 0){
15109             Roo.log('not this item?!');
15110             return;
15111         }
15112         
15113         this.item.splice(index, 1);
15114         o.item.remove();
15115         
15116         this.syncValue();
15117         
15118         this.fireEvent('remove', this, e);
15119         
15120         this.validate();
15121         
15122     },
15123     
15124     syncValue : function()
15125     {
15126         if(!this.item.length){
15127             this.clearValue();
15128             return;
15129         }
15130             
15131         var value = [];
15132         var _this = this;
15133         Roo.each(this.item, function(i){
15134             if(_this.valueField){
15135                 value.push(i[_this.valueField]);
15136                 return;
15137             }
15138
15139             value.push(i);
15140         });
15141
15142         this.value = value.join(',');
15143
15144         if(this.hiddenField){
15145             this.hiddenField.dom.value = this.value;
15146         }
15147         
15148         this.store.fireEvent("datachanged", this.store);
15149         
15150         this.validate();
15151     },
15152     
15153     clearItem : function()
15154     {
15155         if(!this.multiple){
15156             return;
15157         }
15158         
15159         this.item = [];
15160         
15161         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15162            c.remove();
15163         });
15164         
15165         this.syncValue();
15166         
15167         this.validate();
15168         
15169         if(this.tickable && !Roo.isTouch){
15170             this.view.refresh();
15171         }
15172     },
15173     
15174     inputEl: function ()
15175     {
15176         if(Roo.isIOS && this.useNativeIOS){
15177             return this.el.select('select.roo-ios-select', true).first();
15178         }
15179         
15180         if(Roo.isTouch && this.mobileTouchView){
15181             return this.el.select('input.form-control',true).first();
15182         }
15183         
15184         if(this.tickable){
15185             return this.searchField;
15186         }
15187         
15188         return this.el.select('input.form-control',true).first();
15189     },
15190     
15191     onTickableFooterButtonClick : function(e, btn, el)
15192     {
15193         e.preventDefault();
15194         
15195         this.lastItem = Roo.apply([], this.item);
15196         
15197         if(btn && btn.name == 'cancel'){
15198             this.tickItems = Roo.apply([], this.item);
15199             this.collapse();
15200             return;
15201         }
15202         
15203         this.clearItem();
15204         
15205         var _this = this;
15206         
15207         Roo.each(this.tickItems, function(o){
15208             _this.addItem(o);
15209         });
15210         
15211         this.collapse();
15212         
15213     },
15214     
15215     validate : function()
15216     {
15217         if(this.getVisibilityEl().hasClass('hidden')){
15218             return true;
15219         }
15220         
15221         var v = this.getRawValue();
15222         
15223         if(this.multiple){
15224             v = this.getValue();
15225         }
15226         
15227         if(this.disabled || this.allowBlank || v.length){
15228             this.markValid();
15229             return true;
15230         }
15231         
15232         this.markInvalid();
15233         return false;
15234     },
15235     
15236     tickableInputEl : function()
15237     {
15238         if(!this.tickable || !this.editable){
15239             return this.inputEl();
15240         }
15241         
15242         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15243     },
15244     
15245     
15246     getAutoCreateTouchView : function()
15247     {
15248         var id = Roo.id();
15249         
15250         var cfg = {
15251             cls: 'form-group' //input-group
15252         };
15253         
15254         var input =  {
15255             tag: 'input',
15256             id : id,
15257             type : this.inputType,
15258             cls : 'form-control x-combo-noedit',
15259             autocomplete: 'new-password',
15260             placeholder : this.placeholder || '',
15261             readonly : true
15262         };
15263         
15264         if (this.name) {
15265             input.name = this.name;
15266         }
15267         
15268         if (this.size) {
15269             input.cls += ' input-' + this.size;
15270         }
15271         
15272         if (this.disabled) {
15273             input.disabled = true;
15274         }
15275         
15276         var inputblock = {
15277             cls : '',
15278             cn : [
15279                 input
15280             ]
15281         };
15282         
15283         if(this.before){
15284             inputblock.cls += ' input-group';
15285             
15286             inputblock.cn.unshift({
15287                 tag :'span',
15288                 cls : 'input-group-addon input-group-prepend input-group-text',
15289                 html : this.before
15290             });
15291         }
15292         
15293         if(this.removable && !this.multiple){
15294             inputblock.cls += ' roo-removable';
15295             
15296             inputblock.cn.push({
15297                 tag: 'button',
15298                 html : 'x',
15299                 cls : 'roo-combo-removable-btn close'
15300             });
15301         }
15302
15303         if(this.hasFeedback && !this.allowBlank){
15304             
15305             inputblock.cls += ' has-feedback';
15306             
15307             inputblock.cn.push({
15308                 tag: 'span',
15309                 cls: 'glyphicon form-control-feedback'
15310             });
15311             
15312         }
15313         
15314         if (this.after) {
15315             
15316             inputblock.cls += (this.before) ? '' : ' input-group';
15317             
15318             inputblock.cn.push({
15319                 tag :'span',
15320                 cls : 'input-group-addon input-group-append input-group-text',
15321                 html : this.after
15322             });
15323         }
15324
15325         
15326         var ibwrap = inputblock;
15327         
15328         if(this.multiple){
15329             ibwrap = {
15330                 tag: 'ul',
15331                 cls: 'roo-select2-choices',
15332                 cn:[
15333                     {
15334                         tag: 'li',
15335                         cls: 'roo-select2-search-field',
15336                         cn: [
15337
15338                             inputblock
15339                         ]
15340                     }
15341                 ]
15342             };
15343         
15344             
15345         }
15346         
15347         var combobox = {
15348             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15349             cn: [
15350                 {
15351                     tag: 'input',
15352                     type : 'hidden',
15353                     cls: 'form-hidden-field'
15354                 },
15355                 ibwrap
15356             ]
15357         };
15358         
15359         if(!this.multiple && this.showToggleBtn){
15360             
15361             var caret = {
15362                         tag: 'span',
15363                         cls: 'caret'
15364             };
15365             
15366             if (this.caret != false) {
15367                 caret = {
15368                      tag: 'i',
15369                      cls: 'fa fa-' + this.caret
15370                 };
15371                 
15372             }
15373             
15374             combobox.cn.push({
15375                 tag :'span',
15376                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15377                 cn : [
15378                     caret,
15379                     {
15380                         tag: 'span',
15381                         cls: 'combobox-clear',
15382                         cn  : [
15383                             {
15384                                 tag : 'i',
15385                                 cls: 'icon-remove'
15386                             }
15387                         ]
15388                     }
15389                 ]
15390
15391             })
15392         }
15393         
15394         if(this.multiple){
15395             combobox.cls += ' roo-select2-container-multi';
15396         }
15397         
15398         var align = this.labelAlign || this.parentLabelAlign();
15399         
15400         if (align ==='left' && this.fieldLabel.length) {
15401
15402             cfg.cn = [
15403                 {
15404                    tag : 'i',
15405                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15406                    tooltip : 'This field is required'
15407                 },
15408                 {
15409                     tag: 'label',
15410                     cls : 'control-label col-form-label',
15411                     html : this.fieldLabel
15412
15413                 },
15414                 {
15415                     cls : '', 
15416                     cn: [
15417                         combobox
15418                     ]
15419                 }
15420             ];
15421             
15422             var labelCfg = cfg.cn[1];
15423             var contentCfg = cfg.cn[2];
15424             
15425
15426             if(this.indicatorpos == 'right'){
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             {
15438                                 tag : 'i',
15439                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15440                                 tooltip : 'This field is required'
15441                             }
15442                         ]
15443                     },
15444                     {
15445                         cls : "",
15446                         cn: [
15447                             combobox
15448                         ]
15449                     }
15450
15451                 ];
15452                 
15453                 labelCfg = cfg.cn[0];
15454                 contentCfg = cfg.cn[1];
15455             }
15456             
15457            
15458             
15459             if(this.labelWidth > 12){
15460                 labelCfg.style = "width: " + this.labelWidth + 'px';
15461             }
15462             
15463             if(this.labelWidth < 13 && this.labelmd == 0){
15464                 this.labelmd = this.labelWidth;
15465             }
15466             
15467             if(this.labellg > 0){
15468                 labelCfg.cls += ' col-lg-' + this.labellg;
15469                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15470             }
15471             
15472             if(this.labelmd > 0){
15473                 labelCfg.cls += ' col-md-' + this.labelmd;
15474                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15475             }
15476             
15477             if(this.labelsm > 0){
15478                 labelCfg.cls += ' col-sm-' + this.labelsm;
15479                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15480             }
15481             
15482             if(this.labelxs > 0){
15483                 labelCfg.cls += ' col-xs-' + this.labelxs;
15484                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15485             }
15486                 
15487                 
15488         } else if ( this.fieldLabel.length) {
15489             cfg.cn = [
15490                 {
15491                    tag : 'i',
15492                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15493                    tooltip : 'This field is required'
15494                 },
15495                 {
15496                     tag: 'label',
15497                     cls : 'control-label',
15498                     html : this.fieldLabel
15499
15500                 },
15501                 {
15502                     cls : '', 
15503                     cn: [
15504                         combobox
15505                     ]
15506                 }
15507             ];
15508             
15509             if(this.indicatorpos == 'right'){
15510                 cfg.cn = [
15511                     {
15512                         tag: 'label',
15513                         cls : 'control-label',
15514                         html : this.fieldLabel,
15515                         cn : [
15516                             {
15517                                tag : 'i',
15518                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15519                                tooltip : 'This field is required'
15520                             }
15521                         ]
15522                     },
15523                     {
15524                         cls : '', 
15525                         cn: [
15526                             combobox
15527                         ]
15528                     }
15529                 ];
15530             }
15531         } else {
15532             cfg.cn = combobox;    
15533         }
15534         
15535         
15536         var settings = this;
15537         
15538         ['xs','sm','md','lg'].map(function(size){
15539             if (settings[size]) {
15540                 cfg.cls += ' col-' + size + '-' + settings[size];
15541             }
15542         });
15543         
15544         return cfg;
15545     },
15546     
15547     initTouchView : function()
15548     {
15549         this.renderTouchView();
15550         
15551         this.touchViewEl.on('scroll', function(){
15552             this.el.dom.scrollTop = 0;
15553         }, this);
15554         
15555         this.originalValue = this.getValue();
15556         
15557         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15558         
15559         this.inputEl().on("click", this.showTouchView, this);
15560         if (this.triggerEl) {
15561             this.triggerEl.on("click", this.showTouchView, this);
15562         }
15563         
15564         
15565         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15566         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15567         
15568         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15569         
15570         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15571         this.store.on('load', this.onTouchViewLoad, this);
15572         this.store.on('loadexception', this.onTouchViewLoadException, this);
15573         
15574         if(this.hiddenName){
15575             
15576             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15577             
15578             this.hiddenField.dom.value =
15579                 this.hiddenValue !== undefined ? this.hiddenValue :
15580                 this.value !== undefined ? this.value : '';
15581         
15582             this.el.dom.removeAttribute('name');
15583             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15584         }
15585         
15586         if(this.multiple){
15587             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15588             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15589         }
15590         
15591         if(this.removable && !this.multiple){
15592             var close = this.closeTriggerEl();
15593             if(close){
15594                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15595                 close.on('click', this.removeBtnClick, this, close);
15596             }
15597         }
15598         /*
15599          * fix the bug in Safari iOS8
15600          */
15601         this.inputEl().on("focus", function(e){
15602             document.activeElement.blur();
15603         }, this);
15604         
15605         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15606         
15607         return;
15608         
15609         
15610     },
15611     
15612     renderTouchView : function()
15613     {
15614         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15615         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15616         
15617         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15618         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15619         
15620         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15621         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15622         this.touchViewBodyEl.setStyle('overflow', 'auto');
15623         
15624         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15625         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15626         
15627         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15628         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15629         
15630     },
15631     
15632     showTouchView : function()
15633     {
15634         if(this.disabled){
15635             return;
15636         }
15637         
15638         this.touchViewHeaderEl.hide();
15639
15640         if(this.modalTitle.length){
15641             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15642             this.touchViewHeaderEl.show();
15643         }
15644
15645         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15646         this.touchViewEl.show();
15647
15648         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15649         
15650         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15651         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15652
15653         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15654
15655         if(this.modalTitle.length){
15656             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15657         }
15658         
15659         this.touchViewBodyEl.setHeight(bodyHeight);
15660
15661         if(this.animate){
15662             var _this = this;
15663             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15664         }else{
15665             this.touchViewEl.addClass('in');
15666         }
15667         
15668         if(this._touchViewMask){
15669             Roo.get(document.body).addClass("x-body-masked");
15670             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15671             this._touchViewMask.setStyle('z-index', 10000);
15672             this._touchViewMask.addClass('show');
15673         }
15674         
15675         this.doTouchViewQuery();
15676         
15677     },
15678     
15679     hideTouchView : function()
15680     {
15681         this.touchViewEl.removeClass('in');
15682
15683         if(this.animate){
15684             var _this = this;
15685             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15686         }else{
15687             this.touchViewEl.setStyle('display', 'none');
15688         }
15689         
15690         if(this._touchViewMask){
15691             this._touchViewMask.removeClass('show');
15692             Roo.get(document.body).removeClass("x-body-masked");
15693         }
15694     },
15695     
15696     setTouchViewValue : function()
15697     {
15698         if(this.multiple){
15699             this.clearItem();
15700         
15701             var _this = this;
15702
15703             Roo.each(this.tickItems, function(o){
15704                 this.addItem(o);
15705             }, this);
15706         }
15707         
15708         this.hideTouchView();
15709     },
15710     
15711     doTouchViewQuery : function()
15712     {
15713         var qe = {
15714             query: '',
15715             forceAll: true,
15716             combo: this,
15717             cancel:false
15718         };
15719         
15720         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15721             return false;
15722         }
15723         
15724         if(!this.alwaysQuery || this.mode == 'local'){
15725             this.onTouchViewLoad();
15726             return;
15727         }
15728         
15729         this.store.load();
15730     },
15731     
15732     onTouchViewBeforeLoad : function(combo,opts)
15733     {
15734         return;
15735     },
15736
15737     // private
15738     onTouchViewLoad : function()
15739     {
15740         if(this.store.getCount() < 1){
15741             this.onTouchViewEmptyResults();
15742             return;
15743         }
15744         
15745         this.clearTouchView();
15746         
15747         var rawValue = this.getRawValue();
15748         
15749         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15750         
15751         this.tickItems = [];
15752         
15753         this.store.data.each(function(d, rowIndex){
15754             var row = this.touchViewListGroup.createChild(template);
15755             
15756             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15757                 row.addClass(d.data.cls);
15758             }
15759             
15760             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15761                 var cfg = {
15762                     data : d.data,
15763                     html : d.data[this.displayField]
15764                 };
15765                 
15766                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15767                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15768                 }
15769             }
15770             row.removeClass('selected');
15771             if(!this.multiple && this.valueField &&
15772                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15773             {
15774                 // radio buttons..
15775                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15776                 row.addClass('selected');
15777             }
15778             
15779             if(this.multiple && this.valueField &&
15780                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15781             {
15782                 
15783                 // checkboxes...
15784                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15785                 this.tickItems.push(d.data);
15786             }
15787             
15788             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15789             
15790         }, this);
15791         
15792         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15793         
15794         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15795
15796         if(this.modalTitle.length){
15797             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15798         }
15799
15800         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15801         
15802         if(this.mobile_restrict_height && listHeight < bodyHeight){
15803             this.touchViewBodyEl.setHeight(listHeight);
15804         }
15805         
15806         var _this = this;
15807         
15808         if(firstChecked && listHeight > bodyHeight){
15809             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15810         }
15811         
15812     },
15813     
15814     onTouchViewLoadException : function()
15815     {
15816         this.hideTouchView();
15817     },
15818     
15819     onTouchViewEmptyResults : function()
15820     {
15821         this.clearTouchView();
15822         
15823         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15824         
15825         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15826         
15827     },
15828     
15829     clearTouchView : function()
15830     {
15831         this.touchViewListGroup.dom.innerHTML = '';
15832     },
15833     
15834     onTouchViewClick : function(e, el, o)
15835     {
15836         e.preventDefault();
15837         
15838         var row = o.row;
15839         var rowIndex = o.rowIndex;
15840         
15841         var r = this.store.getAt(rowIndex);
15842         
15843         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15844             
15845             if(!this.multiple){
15846                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15847                     c.dom.removeAttribute('checked');
15848                 }, this);
15849
15850                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15851
15852                 this.setFromData(r.data);
15853
15854                 var close = this.closeTriggerEl();
15855
15856                 if(close){
15857                     close.show();
15858                 }
15859
15860                 this.hideTouchView();
15861
15862                 this.fireEvent('select', this, r, rowIndex);
15863
15864                 return;
15865             }
15866
15867             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15868                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15869                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15870                 return;
15871             }
15872
15873             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15874             this.addItem(r.data);
15875             this.tickItems.push(r.data);
15876         }
15877     },
15878     
15879     getAutoCreateNativeIOS : function()
15880     {
15881         var cfg = {
15882             cls: 'form-group' //input-group,
15883         };
15884         
15885         var combobox =  {
15886             tag: 'select',
15887             cls : 'roo-ios-select'
15888         };
15889         
15890         if (this.name) {
15891             combobox.name = this.name;
15892         }
15893         
15894         if (this.disabled) {
15895             combobox.disabled = true;
15896         }
15897         
15898         var settings = this;
15899         
15900         ['xs','sm','md','lg'].map(function(size){
15901             if (settings[size]) {
15902                 cfg.cls += ' col-' + size + '-' + settings[size];
15903             }
15904         });
15905         
15906         cfg.cn = combobox;
15907         
15908         return cfg;
15909         
15910     },
15911     
15912     initIOSView : function()
15913     {
15914         this.store.on('load', this.onIOSViewLoad, this);
15915         
15916         return;
15917     },
15918     
15919     onIOSViewLoad : function()
15920     {
15921         if(this.store.getCount() < 1){
15922             return;
15923         }
15924         
15925         this.clearIOSView();
15926         
15927         if(this.allowBlank) {
15928             
15929             var default_text = '-- SELECT --';
15930             
15931             if(this.placeholder.length){
15932                 default_text = this.placeholder;
15933             }
15934             
15935             if(this.emptyTitle.length){
15936                 default_text += ' - ' + this.emptyTitle + ' -';
15937             }
15938             
15939             var opt = this.inputEl().createChild({
15940                 tag: 'option',
15941                 value : 0,
15942                 html : default_text
15943             });
15944             
15945             var o = {};
15946             o[this.valueField] = 0;
15947             o[this.displayField] = default_text;
15948             
15949             this.ios_options.push({
15950                 data : o,
15951                 el : opt
15952             });
15953             
15954         }
15955         
15956         this.store.data.each(function(d, rowIndex){
15957             
15958             var html = '';
15959             
15960             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15961                 html = d.data[this.displayField];
15962             }
15963             
15964             var value = '';
15965             
15966             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15967                 value = d.data[this.valueField];
15968             }
15969             
15970             var option = {
15971                 tag: 'option',
15972                 value : value,
15973                 html : html
15974             };
15975             
15976             if(this.value == d.data[this.valueField]){
15977                 option['selected'] = true;
15978             }
15979             
15980             var opt = this.inputEl().createChild(option);
15981             
15982             this.ios_options.push({
15983                 data : d.data,
15984                 el : opt
15985             });
15986             
15987         }, this);
15988         
15989         this.inputEl().on('change', function(){
15990            this.fireEvent('select', this);
15991         }, this);
15992         
15993     },
15994     
15995     clearIOSView: function()
15996     {
15997         this.inputEl().dom.innerHTML = '';
15998         
15999         this.ios_options = [];
16000     },
16001     
16002     setIOSValue: function(v)
16003     {
16004         this.value = v;
16005         
16006         if(!this.ios_options){
16007             return;
16008         }
16009         
16010         Roo.each(this.ios_options, function(opts){
16011            
16012            opts.el.dom.removeAttribute('selected');
16013            
16014            if(opts.data[this.valueField] != v){
16015                return;
16016            }
16017            
16018            opts.el.dom.setAttribute('selected', true);
16019            
16020         }, this);
16021     }
16022
16023     /** 
16024     * @cfg {Boolean} grow 
16025     * @hide 
16026     */
16027     /** 
16028     * @cfg {Number} growMin 
16029     * @hide 
16030     */
16031     /** 
16032     * @cfg {Number} growMax 
16033     * @hide 
16034     */
16035     /**
16036      * @hide
16037      * @method autoSize
16038      */
16039 });
16040
16041 Roo.apply(Roo.bootstrap.ComboBox,  {
16042     
16043     header : {
16044         tag: 'div',
16045         cls: 'modal-header',
16046         cn: [
16047             {
16048                 tag: 'h4',
16049                 cls: 'modal-title'
16050             }
16051         ]
16052     },
16053     
16054     body : {
16055         tag: 'div',
16056         cls: 'modal-body',
16057         cn: [
16058             {
16059                 tag: 'ul',
16060                 cls: 'list-group'
16061             }
16062         ]
16063     },
16064     
16065     listItemRadio : {
16066         tag: 'li',
16067         cls: 'list-group-item',
16068         cn: [
16069             {
16070                 tag: 'span',
16071                 cls: 'roo-combobox-list-group-item-value'
16072             },
16073             {
16074                 tag: 'div',
16075                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16076                 cn: [
16077                     {
16078                         tag: 'input',
16079                         type: 'radio'
16080                     },
16081                     {
16082                         tag: 'label'
16083                     }
16084                 ]
16085             }
16086         ]
16087     },
16088     
16089     listItemCheckbox : {
16090         tag: 'li',
16091         cls: 'list-group-item',
16092         cn: [
16093             {
16094                 tag: 'span',
16095                 cls: 'roo-combobox-list-group-item-value'
16096             },
16097             {
16098                 tag: 'div',
16099                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16100                 cn: [
16101                     {
16102                         tag: 'input',
16103                         type: 'checkbox'
16104                     },
16105                     {
16106                         tag: 'label'
16107                     }
16108                 ]
16109             }
16110         ]
16111     },
16112     
16113     emptyResult : {
16114         tag: 'div',
16115         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16116     },
16117     
16118     footer : {
16119         tag: 'div',
16120         cls: 'modal-footer',
16121         cn: [
16122             {
16123                 tag: 'div',
16124                 cls: 'row',
16125                 cn: [
16126                     {
16127                         tag: 'div',
16128                         cls: 'col-xs-6 text-left',
16129                         cn: {
16130                             tag: 'button',
16131                             cls: 'btn btn-danger roo-touch-view-cancel',
16132                             html: 'Cancel'
16133                         }
16134                     },
16135                     {
16136                         tag: 'div',
16137                         cls: 'col-xs-6 text-right',
16138                         cn: {
16139                             tag: 'button',
16140                             cls: 'btn btn-success roo-touch-view-ok',
16141                             html: 'OK'
16142                         }
16143                     }
16144                 ]
16145             }
16146         ]
16147         
16148     }
16149 });
16150
16151 Roo.apply(Roo.bootstrap.ComboBox,  {
16152     
16153     touchViewTemplate : {
16154         tag: 'div',
16155         cls: 'modal fade roo-combobox-touch-view',
16156         cn: [
16157             {
16158                 tag: 'div',
16159                 cls: 'modal-dialog',
16160                 style : 'position:fixed', // we have to fix position....
16161                 cn: [
16162                     {
16163                         tag: 'div',
16164                         cls: 'modal-content',
16165                         cn: [
16166                             Roo.bootstrap.ComboBox.header,
16167                             Roo.bootstrap.ComboBox.body,
16168                             Roo.bootstrap.ComboBox.footer
16169                         ]
16170                     }
16171                 ]
16172             }
16173         ]
16174     }
16175 });/*
16176  * Based on:
16177  * Ext JS Library 1.1.1
16178  * Copyright(c) 2006-2007, Ext JS, LLC.
16179  *
16180  * Originally Released Under LGPL - original licence link has changed is not relivant.
16181  *
16182  * Fork - LGPL
16183  * <script type="text/javascript">
16184  */
16185
16186 /**
16187  * @class Roo.View
16188  * @extends Roo.util.Observable
16189  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16190  * This class also supports single and multi selection modes. <br>
16191  * Create a data model bound view:
16192  <pre><code>
16193  var store = new Roo.data.Store(...);
16194
16195  var view = new Roo.View({
16196     el : "my-element",
16197     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16198  
16199     singleSelect: true,
16200     selectedClass: "ydataview-selected",
16201     store: store
16202  });
16203
16204  // listen for node click?
16205  view.on("click", function(vw, index, node, e){
16206  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16207  });
16208
16209  // load XML data
16210  dataModel.load("foobar.xml");
16211  </code></pre>
16212  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16213  * <br><br>
16214  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16215  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16216  * 
16217  * Note: old style constructor is still suported (container, template, config)
16218  * 
16219  * @constructor
16220  * Create a new View
16221  * @param {Object} config The config object
16222  * 
16223  */
16224 Roo.View = function(config, depreciated_tpl, depreciated_config){
16225     
16226     this.parent = false;
16227     
16228     if (typeof(depreciated_tpl) == 'undefined') {
16229         // new way.. - universal constructor.
16230         Roo.apply(this, config);
16231         this.el  = Roo.get(this.el);
16232     } else {
16233         // old format..
16234         this.el  = Roo.get(config);
16235         this.tpl = depreciated_tpl;
16236         Roo.apply(this, depreciated_config);
16237     }
16238     this.wrapEl  = this.el.wrap().wrap();
16239     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16240     
16241     
16242     if(typeof(this.tpl) == "string"){
16243         this.tpl = new Roo.Template(this.tpl);
16244     } else {
16245         // support xtype ctors..
16246         this.tpl = new Roo.factory(this.tpl, Roo);
16247     }
16248     
16249     
16250     this.tpl.compile();
16251     
16252     /** @private */
16253     this.addEvents({
16254         /**
16255          * @event beforeclick
16256          * Fires before a click is processed. Returns false to cancel the default action.
16257          * @param {Roo.View} this
16258          * @param {Number} index The index of the target node
16259          * @param {HTMLElement} node The target node
16260          * @param {Roo.EventObject} e The raw event object
16261          */
16262             "beforeclick" : true,
16263         /**
16264          * @event click
16265          * Fires when a template node is clicked.
16266          * @param {Roo.View} this
16267          * @param {Number} index The index of the target node
16268          * @param {HTMLElement} node The target node
16269          * @param {Roo.EventObject} e The raw event object
16270          */
16271             "click" : true,
16272         /**
16273          * @event dblclick
16274          * Fires when a template node is double clicked.
16275          * @param {Roo.View} this
16276          * @param {Number} index The index of the target node
16277          * @param {HTMLElement} node The target node
16278          * @param {Roo.EventObject} e The raw event object
16279          */
16280             "dblclick" : true,
16281         /**
16282          * @event contextmenu
16283          * Fires when a template node is right clicked.
16284          * @param {Roo.View} this
16285          * @param {Number} index The index of the target node
16286          * @param {HTMLElement} node The target node
16287          * @param {Roo.EventObject} e The raw event object
16288          */
16289             "contextmenu" : true,
16290         /**
16291          * @event selectionchange
16292          * Fires when the selected nodes change.
16293          * @param {Roo.View} this
16294          * @param {Array} selections Array of the selected nodes
16295          */
16296             "selectionchange" : true,
16297     
16298         /**
16299          * @event beforeselect
16300          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16301          * @param {Roo.View} this
16302          * @param {HTMLElement} node The node to be selected
16303          * @param {Array} selections Array of currently selected nodes
16304          */
16305             "beforeselect" : true,
16306         /**
16307          * @event preparedata
16308          * Fires on every row to render, to allow you to change the data.
16309          * @param {Roo.View} this
16310          * @param {Object} data to be rendered (change this)
16311          */
16312           "preparedata" : true
16313           
16314           
16315         });
16316
16317
16318
16319     this.el.on({
16320         "click": this.onClick,
16321         "dblclick": this.onDblClick,
16322         "contextmenu": this.onContextMenu,
16323         scope:this
16324     });
16325
16326     this.selections = [];
16327     this.nodes = [];
16328     this.cmp = new Roo.CompositeElementLite([]);
16329     if(this.store){
16330         this.store = Roo.factory(this.store, Roo.data);
16331         this.setStore(this.store, true);
16332     }
16333     
16334     if ( this.footer && this.footer.xtype) {
16335            
16336          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16337         
16338         this.footer.dataSource = this.store;
16339         this.footer.container = fctr;
16340         this.footer = Roo.factory(this.footer, Roo);
16341         fctr.insertFirst(this.el);
16342         
16343         // this is a bit insane - as the paging toolbar seems to detach the el..
16344 //        dom.parentNode.parentNode.parentNode
16345          // they get detached?
16346     }
16347     
16348     
16349     Roo.View.superclass.constructor.call(this);
16350     
16351     
16352 };
16353
16354 Roo.extend(Roo.View, Roo.util.Observable, {
16355     
16356      /**
16357      * @cfg {Roo.data.Store} store Data store to load data from.
16358      */
16359     store : false,
16360     
16361     /**
16362      * @cfg {String|Roo.Element} el The container element.
16363      */
16364     el : '',
16365     
16366     /**
16367      * @cfg {String|Roo.Template} tpl The template used by this View 
16368      */
16369     tpl : false,
16370     /**
16371      * @cfg {String} dataName the named area of the template to use as the data area
16372      *                          Works with domtemplates roo-name="name"
16373      */
16374     dataName: false,
16375     /**
16376      * @cfg {String} selectedClass The css class to add to selected nodes
16377      */
16378     selectedClass : "x-view-selected",
16379      /**
16380      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16381      */
16382     emptyText : "",
16383     
16384     /**
16385      * @cfg {String} text to display on mask (default Loading)
16386      */
16387     mask : false,
16388     /**
16389      * @cfg {Boolean} multiSelect Allow multiple selection
16390      */
16391     multiSelect : false,
16392     /**
16393      * @cfg {Boolean} singleSelect Allow single selection
16394      */
16395     singleSelect:  false,
16396     
16397     /**
16398      * @cfg {Boolean} toggleSelect - selecting 
16399      */
16400     toggleSelect : false,
16401     
16402     /**
16403      * @cfg {Boolean} tickable - selecting 
16404      */
16405     tickable : false,
16406     
16407     /**
16408      * Returns the element this view is bound to.
16409      * @return {Roo.Element}
16410      */
16411     getEl : function(){
16412         return this.wrapEl;
16413     },
16414     
16415     
16416
16417     /**
16418      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16419      */
16420     refresh : function(){
16421         //Roo.log('refresh');
16422         var t = this.tpl;
16423         
16424         // if we are using something like 'domtemplate', then
16425         // the what gets used is:
16426         // t.applySubtemplate(NAME, data, wrapping data..)
16427         // the outer template then get' applied with
16428         //     the store 'extra data'
16429         // and the body get's added to the
16430         //      roo-name="data" node?
16431         //      <span class='roo-tpl-{name}'></span> ?????
16432         
16433         
16434         
16435         this.clearSelections();
16436         this.el.update("");
16437         var html = [];
16438         var records = this.store.getRange();
16439         if(records.length < 1) {
16440             
16441             // is this valid??  = should it render a template??
16442             
16443             this.el.update(this.emptyText);
16444             return;
16445         }
16446         var el = this.el;
16447         if (this.dataName) {
16448             this.el.update(t.apply(this.store.meta)); //????
16449             el = this.el.child('.roo-tpl-' + this.dataName);
16450         }
16451         
16452         for(var i = 0, len = records.length; i < len; i++){
16453             var data = this.prepareData(records[i].data, i, records[i]);
16454             this.fireEvent("preparedata", this, data, i, records[i]);
16455             
16456             var d = Roo.apply({}, data);
16457             
16458             if(this.tickable){
16459                 Roo.apply(d, {'roo-id' : Roo.id()});
16460                 
16461                 var _this = this;
16462             
16463                 Roo.each(this.parent.item, function(item){
16464                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16465                         return;
16466                     }
16467                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16468                 });
16469             }
16470             
16471             html[html.length] = Roo.util.Format.trim(
16472                 this.dataName ?
16473                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16474                     t.apply(d)
16475             );
16476         }
16477         
16478         
16479         
16480         el.update(html.join(""));
16481         this.nodes = el.dom.childNodes;
16482         this.updateIndexes(0);
16483     },
16484     
16485
16486     /**
16487      * Function to override to reformat the data that is sent to
16488      * the template for each node.
16489      * DEPRICATED - use the preparedata event handler.
16490      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16491      * a JSON object for an UpdateManager bound view).
16492      */
16493     prepareData : function(data, index, record)
16494     {
16495         this.fireEvent("preparedata", this, data, index, record);
16496         return data;
16497     },
16498
16499     onUpdate : function(ds, record){
16500         // Roo.log('on update');   
16501         this.clearSelections();
16502         var index = this.store.indexOf(record);
16503         var n = this.nodes[index];
16504         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16505         n.parentNode.removeChild(n);
16506         this.updateIndexes(index, index);
16507     },
16508
16509     
16510     
16511 // --------- FIXME     
16512     onAdd : function(ds, records, index)
16513     {
16514         //Roo.log(['on Add', ds, records, index] );        
16515         this.clearSelections();
16516         if(this.nodes.length == 0){
16517             this.refresh();
16518             return;
16519         }
16520         var n = this.nodes[index];
16521         for(var i = 0, len = records.length; i < len; i++){
16522             var d = this.prepareData(records[i].data, i, records[i]);
16523             if(n){
16524                 this.tpl.insertBefore(n, d);
16525             }else{
16526                 
16527                 this.tpl.append(this.el, d);
16528             }
16529         }
16530         this.updateIndexes(index);
16531     },
16532
16533     onRemove : function(ds, record, index){
16534        // Roo.log('onRemove');
16535         this.clearSelections();
16536         var el = this.dataName  ?
16537             this.el.child('.roo-tpl-' + this.dataName) :
16538             this.el; 
16539         
16540         el.dom.removeChild(this.nodes[index]);
16541         this.updateIndexes(index);
16542     },
16543
16544     /**
16545      * Refresh an individual node.
16546      * @param {Number} index
16547      */
16548     refreshNode : function(index){
16549         this.onUpdate(this.store, this.store.getAt(index));
16550     },
16551
16552     updateIndexes : function(startIndex, endIndex){
16553         var ns = this.nodes;
16554         startIndex = startIndex || 0;
16555         endIndex = endIndex || ns.length - 1;
16556         for(var i = startIndex; i <= endIndex; i++){
16557             ns[i].nodeIndex = i;
16558         }
16559     },
16560
16561     /**
16562      * Changes the data store this view uses and refresh the view.
16563      * @param {Store} store
16564      */
16565     setStore : function(store, initial){
16566         if(!initial && this.store){
16567             this.store.un("datachanged", this.refresh);
16568             this.store.un("add", this.onAdd);
16569             this.store.un("remove", this.onRemove);
16570             this.store.un("update", this.onUpdate);
16571             this.store.un("clear", this.refresh);
16572             this.store.un("beforeload", this.onBeforeLoad);
16573             this.store.un("load", this.onLoad);
16574             this.store.un("loadexception", this.onLoad);
16575         }
16576         if(store){
16577           
16578             store.on("datachanged", this.refresh, this);
16579             store.on("add", this.onAdd, this);
16580             store.on("remove", this.onRemove, this);
16581             store.on("update", this.onUpdate, this);
16582             store.on("clear", this.refresh, this);
16583             store.on("beforeload", this.onBeforeLoad, this);
16584             store.on("load", this.onLoad, this);
16585             store.on("loadexception", this.onLoad, this);
16586         }
16587         
16588         if(store){
16589             this.refresh();
16590         }
16591     },
16592     /**
16593      * onbeforeLoad - masks the loading area.
16594      *
16595      */
16596     onBeforeLoad : function(store,opts)
16597     {
16598          //Roo.log('onBeforeLoad');   
16599         if (!opts.add) {
16600             this.el.update("");
16601         }
16602         this.el.mask(this.mask ? this.mask : "Loading" ); 
16603     },
16604     onLoad : function ()
16605     {
16606         this.el.unmask();
16607     },
16608     
16609
16610     /**
16611      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16612      * @param {HTMLElement} node
16613      * @return {HTMLElement} The template node
16614      */
16615     findItemFromChild : function(node){
16616         var el = this.dataName  ?
16617             this.el.child('.roo-tpl-' + this.dataName,true) :
16618             this.el.dom; 
16619         
16620         if(!node || node.parentNode == el){
16621                     return node;
16622             }
16623             var p = node.parentNode;
16624             while(p && p != el){
16625             if(p.parentNode == el){
16626                 return p;
16627             }
16628             p = p.parentNode;
16629         }
16630             return null;
16631     },
16632
16633     /** @ignore */
16634     onClick : function(e){
16635         var item = this.findItemFromChild(e.getTarget());
16636         if(item){
16637             var index = this.indexOf(item);
16638             if(this.onItemClick(item, index, e) !== false){
16639                 this.fireEvent("click", this, index, item, e);
16640             }
16641         }else{
16642             this.clearSelections();
16643         }
16644     },
16645
16646     /** @ignore */
16647     onContextMenu : function(e){
16648         var item = this.findItemFromChild(e.getTarget());
16649         if(item){
16650             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16651         }
16652     },
16653
16654     /** @ignore */
16655     onDblClick : function(e){
16656         var item = this.findItemFromChild(e.getTarget());
16657         if(item){
16658             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16659         }
16660     },
16661
16662     onItemClick : function(item, index, e)
16663     {
16664         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16665             return false;
16666         }
16667         if (this.toggleSelect) {
16668             var m = this.isSelected(item) ? 'unselect' : 'select';
16669             //Roo.log(m);
16670             var _t = this;
16671             _t[m](item, true, false);
16672             return true;
16673         }
16674         if(this.multiSelect || this.singleSelect){
16675             if(this.multiSelect && e.shiftKey && this.lastSelection){
16676                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16677             }else{
16678                 this.select(item, this.multiSelect && e.ctrlKey);
16679                 this.lastSelection = item;
16680             }
16681             
16682             if(!this.tickable){
16683                 e.preventDefault();
16684             }
16685             
16686         }
16687         return true;
16688     },
16689
16690     /**
16691      * Get the number of selected nodes.
16692      * @return {Number}
16693      */
16694     getSelectionCount : function(){
16695         return this.selections.length;
16696     },
16697
16698     /**
16699      * Get the currently selected nodes.
16700      * @return {Array} An array of HTMLElements
16701      */
16702     getSelectedNodes : function(){
16703         return this.selections;
16704     },
16705
16706     /**
16707      * Get the indexes of the selected nodes.
16708      * @return {Array}
16709      */
16710     getSelectedIndexes : function(){
16711         var indexes = [], s = this.selections;
16712         for(var i = 0, len = s.length; i < len; i++){
16713             indexes.push(s[i].nodeIndex);
16714         }
16715         return indexes;
16716     },
16717
16718     /**
16719      * Clear all selections
16720      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16721      */
16722     clearSelections : function(suppressEvent){
16723         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16724             this.cmp.elements = this.selections;
16725             this.cmp.removeClass(this.selectedClass);
16726             this.selections = [];
16727             if(!suppressEvent){
16728                 this.fireEvent("selectionchange", this, this.selections);
16729             }
16730         }
16731     },
16732
16733     /**
16734      * Returns true if the passed node is selected
16735      * @param {HTMLElement/Number} node The node or node index
16736      * @return {Boolean}
16737      */
16738     isSelected : function(node){
16739         var s = this.selections;
16740         if(s.length < 1){
16741             return false;
16742         }
16743         node = this.getNode(node);
16744         return s.indexOf(node) !== -1;
16745     },
16746
16747     /**
16748      * Selects nodes.
16749      * @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
16750      * @param {Boolean} keepExisting (optional) true to keep existing selections
16751      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16752      */
16753     select : function(nodeInfo, keepExisting, suppressEvent){
16754         if(nodeInfo instanceof Array){
16755             if(!keepExisting){
16756                 this.clearSelections(true);
16757             }
16758             for(var i = 0, len = nodeInfo.length; i < len; i++){
16759                 this.select(nodeInfo[i], true, true);
16760             }
16761             return;
16762         } 
16763         var node = this.getNode(nodeInfo);
16764         if(!node || this.isSelected(node)){
16765             return; // already selected.
16766         }
16767         if(!keepExisting){
16768             this.clearSelections(true);
16769         }
16770         
16771         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16772             Roo.fly(node).addClass(this.selectedClass);
16773             this.selections.push(node);
16774             if(!suppressEvent){
16775                 this.fireEvent("selectionchange", this, this.selections);
16776             }
16777         }
16778         
16779         
16780     },
16781       /**
16782      * Unselects nodes.
16783      * @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
16784      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16785      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16786      */
16787     unselect : function(nodeInfo, keepExisting, suppressEvent)
16788     {
16789         if(nodeInfo instanceof Array){
16790             Roo.each(this.selections, function(s) {
16791                 this.unselect(s, nodeInfo);
16792             }, this);
16793             return;
16794         }
16795         var node = this.getNode(nodeInfo);
16796         if(!node || !this.isSelected(node)){
16797             //Roo.log("not selected");
16798             return; // not selected.
16799         }
16800         // fireevent???
16801         var ns = [];
16802         Roo.each(this.selections, function(s) {
16803             if (s == node ) {
16804                 Roo.fly(node).removeClass(this.selectedClass);
16805
16806                 return;
16807             }
16808             ns.push(s);
16809         },this);
16810         
16811         this.selections= ns;
16812         this.fireEvent("selectionchange", this, this.selections);
16813     },
16814
16815     /**
16816      * Gets a template node.
16817      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16818      * @return {HTMLElement} The node or null if it wasn't found
16819      */
16820     getNode : function(nodeInfo){
16821         if(typeof nodeInfo == "string"){
16822             return document.getElementById(nodeInfo);
16823         }else if(typeof nodeInfo == "number"){
16824             return this.nodes[nodeInfo];
16825         }
16826         return nodeInfo;
16827     },
16828
16829     /**
16830      * Gets a range template nodes.
16831      * @param {Number} startIndex
16832      * @param {Number} endIndex
16833      * @return {Array} An array of nodes
16834      */
16835     getNodes : function(start, end){
16836         var ns = this.nodes;
16837         start = start || 0;
16838         end = typeof end == "undefined" ? ns.length - 1 : end;
16839         var nodes = [];
16840         if(start <= end){
16841             for(var i = start; i <= end; i++){
16842                 nodes.push(ns[i]);
16843             }
16844         } else{
16845             for(var i = start; i >= end; i--){
16846                 nodes.push(ns[i]);
16847             }
16848         }
16849         return nodes;
16850     },
16851
16852     /**
16853      * Finds the index of the passed node
16854      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16855      * @return {Number} The index of the node or -1
16856      */
16857     indexOf : function(node){
16858         node = this.getNode(node);
16859         if(typeof node.nodeIndex == "number"){
16860             return node.nodeIndex;
16861         }
16862         var ns = this.nodes;
16863         for(var i = 0, len = ns.length; i < len; i++){
16864             if(ns[i] == node){
16865                 return i;
16866             }
16867         }
16868         return -1;
16869     }
16870 });
16871 /*
16872  * - LGPL
16873  *
16874  * based on jquery fullcalendar
16875  * 
16876  */
16877
16878 Roo.bootstrap = Roo.bootstrap || {};
16879 /**
16880  * @class Roo.bootstrap.Calendar
16881  * @extends Roo.bootstrap.Component
16882  * Bootstrap Calendar class
16883  * @cfg {Boolean} loadMask (true|false) default false
16884  * @cfg {Object} header generate the user specific header of the calendar, default false
16885
16886  * @constructor
16887  * Create a new Container
16888  * @param {Object} config The config object
16889  */
16890
16891
16892
16893 Roo.bootstrap.Calendar = function(config){
16894     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16895      this.addEvents({
16896         /**
16897              * @event select
16898              * Fires when a date is selected
16899              * @param {DatePicker} this
16900              * @param {Date} date The selected date
16901              */
16902         'select': true,
16903         /**
16904              * @event monthchange
16905              * Fires when the displayed month changes 
16906              * @param {DatePicker} this
16907              * @param {Date} date The selected month
16908              */
16909         'monthchange': true,
16910         /**
16911              * @event evententer
16912              * Fires when mouse over an event
16913              * @param {Calendar} this
16914              * @param {event} Event
16915              */
16916         'evententer': true,
16917         /**
16918              * @event eventleave
16919              * Fires when the mouse leaves an
16920              * @param {Calendar} this
16921              * @param {event}
16922              */
16923         'eventleave': true,
16924         /**
16925              * @event eventclick
16926              * Fires when the mouse click an
16927              * @param {Calendar} this
16928              * @param {event}
16929              */
16930         'eventclick': true
16931         
16932     });
16933
16934 };
16935
16936 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16937     
16938      /**
16939      * @cfg {Number} startDay
16940      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16941      */
16942     startDay : 0,
16943     
16944     loadMask : false,
16945     
16946     header : false,
16947       
16948     getAutoCreate : function(){
16949         
16950         
16951         var fc_button = function(name, corner, style, content ) {
16952             return Roo.apply({},{
16953                 tag : 'span',
16954                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16955                          (corner.length ?
16956                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16957                             ''
16958                         ),
16959                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16960                 unselectable: 'on'
16961             });
16962         };
16963         
16964         var header = {};
16965         
16966         if(!this.header){
16967             header = {
16968                 tag : 'table',
16969                 cls : 'fc-header',
16970                 style : 'width:100%',
16971                 cn : [
16972                     {
16973                         tag: 'tr',
16974                         cn : [
16975                             {
16976                                 tag : 'td',
16977                                 cls : 'fc-header-left',
16978                                 cn : [
16979                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16980                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16981                                     { tag: 'span', cls: 'fc-header-space' },
16982                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16983
16984
16985                                 ]
16986                             },
16987
16988                             {
16989                                 tag : 'td',
16990                                 cls : 'fc-header-center',
16991                                 cn : [
16992                                     {
16993                                         tag: 'span',
16994                                         cls: 'fc-header-title',
16995                                         cn : {
16996                                             tag: 'H2',
16997                                             html : 'month / year'
16998                                         }
16999                                     }
17000
17001                                 ]
17002                             },
17003                             {
17004                                 tag : 'td',
17005                                 cls : 'fc-header-right',
17006                                 cn : [
17007                               /*      fc_button('month', 'left', '', 'month' ),
17008                                     fc_button('week', '', '', 'week' ),
17009                                     fc_button('day', 'right', '', 'day' )
17010                                 */    
17011
17012                                 ]
17013                             }
17014
17015                         ]
17016                     }
17017                 ]
17018             };
17019         }
17020         
17021         header = this.header;
17022         
17023        
17024         var cal_heads = function() {
17025             var ret = [];
17026             // fixme - handle this.
17027             
17028             for (var i =0; i < Date.dayNames.length; i++) {
17029                 var d = Date.dayNames[i];
17030                 ret.push({
17031                     tag: 'th',
17032                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17033                     html : d.substring(0,3)
17034                 });
17035                 
17036             }
17037             ret[0].cls += ' fc-first';
17038             ret[6].cls += ' fc-last';
17039             return ret;
17040         };
17041         var cal_cell = function(n) {
17042             return  {
17043                 tag: 'td',
17044                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17045                 cn : [
17046                     {
17047                         cn : [
17048                             {
17049                                 cls: 'fc-day-number',
17050                                 html: 'D'
17051                             },
17052                             {
17053                                 cls: 'fc-day-content',
17054                              
17055                                 cn : [
17056                                      {
17057                                         style: 'position: relative;' // height: 17px;
17058                                     }
17059                                 ]
17060                             }
17061                             
17062                             
17063                         ]
17064                     }
17065                 ]
17066                 
17067             }
17068         };
17069         var cal_rows = function() {
17070             
17071             var ret = [];
17072             for (var r = 0; r < 6; r++) {
17073                 var row= {
17074                     tag : 'tr',
17075                     cls : 'fc-week',
17076                     cn : []
17077                 };
17078                 
17079                 for (var i =0; i < Date.dayNames.length; i++) {
17080                     var d = Date.dayNames[i];
17081                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17082
17083                 }
17084                 row.cn[0].cls+=' fc-first';
17085                 row.cn[0].cn[0].style = 'min-height:90px';
17086                 row.cn[6].cls+=' fc-last';
17087                 ret.push(row);
17088                 
17089             }
17090             ret[0].cls += ' fc-first';
17091             ret[4].cls += ' fc-prev-last';
17092             ret[5].cls += ' fc-last';
17093             return ret;
17094             
17095         };
17096         
17097         var cal_table = {
17098             tag: 'table',
17099             cls: 'fc-border-separate',
17100             style : 'width:100%',
17101             cellspacing  : 0,
17102             cn : [
17103                 { 
17104                     tag: 'thead',
17105                     cn : [
17106                         { 
17107                             tag: 'tr',
17108                             cls : 'fc-first fc-last',
17109                             cn : cal_heads()
17110                         }
17111                     ]
17112                 },
17113                 { 
17114                     tag: 'tbody',
17115                     cn : cal_rows()
17116                 }
17117                   
17118             ]
17119         };
17120          
17121          var cfg = {
17122             cls : 'fc fc-ltr',
17123             cn : [
17124                 header,
17125                 {
17126                     cls : 'fc-content',
17127                     style : "position: relative;",
17128                     cn : [
17129                         {
17130                             cls : 'fc-view fc-view-month fc-grid',
17131                             style : 'position: relative',
17132                             unselectable : 'on',
17133                             cn : [
17134                                 {
17135                                     cls : 'fc-event-container',
17136                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17137                                 },
17138                                 cal_table
17139                             ]
17140                         }
17141                     ]
17142     
17143                 }
17144            ] 
17145             
17146         };
17147         
17148          
17149         
17150         return cfg;
17151     },
17152     
17153     
17154     initEvents : function()
17155     {
17156         if(!this.store){
17157             throw "can not find store for calendar";
17158         }
17159         
17160         var mark = {
17161             tag: "div",
17162             cls:"x-dlg-mask",
17163             style: "text-align:center",
17164             cn: [
17165                 {
17166                     tag: "div",
17167                     style: "background-color:white;width:50%;margin:250 auto",
17168                     cn: [
17169                         {
17170                             tag: "img",
17171                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17172                         },
17173                         {
17174                             tag: "span",
17175                             html: "Loading"
17176                         }
17177                         
17178                     ]
17179                 }
17180             ]
17181         };
17182         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17183         
17184         var size = this.el.select('.fc-content', true).first().getSize();
17185         this.maskEl.setSize(size.width, size.height);
17186         this.maskEl.enableDisplayMode("block");
17187         if(!this.loadMask){
17188             this.maskEl.hide();
17189         }
17190         
17191         this.store = Roo.factory(this.store, Roo.data);
17192         this.store.on('load', this.onLoad, this);
17193         this.store.on('beforeload', this.onBeforeLoad, this);
17194         
17195         this.resize();
17196         
17197         this.cells = this.el.select('.fc-day',true);
17198         //Roo.log(this.cells);
17199         this.textNodes = this.el.query('.fc-day-number');
17200         this.cells.addClassOnOver('fc-state-hover');
17201         
17202         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17203         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17204         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17205         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17206         
17207         this.on('monthchange', this.onMonthChange, this);
17208         
17209         this.update(new Date().clearTime());
17210     },
17211     
17212     resize : function() {
17213         var sz  = this.el.getSize();
17214         
17215         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17216         this.el.select('.fc-day-content div',true).setHeight(34);
17217     },
17218     
17219     
17220     // private
17221     showPrevMonth : function(e){
17222         this.update(this.activeDate.add("mo", -1));
17223     },
17224     showToday : function(e){
17225         this.update(new Date().clearTime());
17226     },
17227     // private
17228     showNextMonth : function(e){
17229         this.update(this.activeDate.add("mo", 1));
17230     },
17231
17232     // private
17233     showPrevYear : function(){
17234         this.update(this.activeDate.add("y", -1));
17235     },
17236
17237     // private
17238     showNextYear : function(){
17239         this.update(this.activeDate.add("y", 1));
17240     },
17241
17242     
17243    // private
17244     update : function(date)
17245     {
17246         var vd = this.activeDate;
17247         this.activeDate = date;
17248 //        if(vd && this.el){
17249 //            var t = date.getTime();
17250 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17251 //                Roo.log('using add remove');
17252 //                
17253 //                this.fireEvent('monthchange', this, date);
17254 //                
17255 //                this.cells.removeClass("fc-state-highlight");
17256 //                this.cells.each(function(c){
17257 //                   if(c.dateValue == t){
17258 //                       c.addClass("fc-state-highlight");
17259 //                       setTimeout(function(){
17260 //                            try{c.dom.firstChild.focus();}catch(e){}
17261 //                       }, 50);
17262 //                       return false;
17263 //                   }
17264 //                   return true;
17265 //                });
17266 //                return;
17267 //            }
17268 //        }
17269         
17270         var days = date.getDaysInMonth();
17271         
17272         var firstOfMonth = date.getFirstDateOfMonth();
17273         var startingPos = firstOfMonth.getDay()-this.startDay;
17274         
17275         if(startingPos < this.startDay){
17276             startingPos += 7;
17277         }
17278         
17279         var pm = date.add(Date.MONTH, -1);
17280         var prevStart = pm.getDaysInMonth()-startingPos;
17281 //        
17282         this.cells = this.el.select('.fc-day',true);
17283         this.textNodes = this.el.query('.fc-day-number');
17284         this.cells.addClassOnOver('fc-state-hover');
17285         
17286         var cells = this.cells.elements;
17287         var textEls = this.textNodes;
17288         
17289         Roo.each(cells, function(cell){
17290             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17291         });
17292         
17293         days += startingPos;
17294
17295         // convert everything to numbers so it's fast
17296         var day = 86400000;
17297         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17298         //Roo.log(d);
17299         //Roo.log(pm);
17300         //Roo.log(prevStart);
17301         
17302         var today = new Date().clearTime().getTime();
17303         var sel = date.clearTime().getTime();
17304         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17305         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17306         var ddMatch = this.disabledDatesRE;
17307         var ddText = this.disabledDatesText;
17308         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17309         var ddaysText = this.disabledDaysText;
17310         var format = this.format;
17311         
17312         var setCellClass = function(cal, cell){
17313             cell.row = 0;
17314             cell.events = [];
17315             cell.more = [];
17316             //Roo.log('set Cell Class');
17317             cell.title = "";
17318             var t = d.getTime();
17319             
17320             //Roo.log(d);
17321             
17322             cell.dateValue = t;
17323             if(t == today){
17324                 cell.className += " fc-today";
17325                 cell.className += " fc-state-highlight";
17326                 cell.title = cal.todayText;
17327             }
17328             if(t == sel){
17329                 // disable highlight in other month..
17330                 //cell.className += " fc-state-highlight";
17331                 
17332             }
17333             // disabling
17334             if(t < min) {
17335                 cell.className = " fc-state-disabled";
17336                 cell.title = cal.minText;
17337                 return;
17338             }
17339             if(t > max) {
17340                 cell.className = " fc-state-disabled";
17341                 cell.title = cal.maxText;
17342                 return;
17343             }
17344             if(ddays){
17345                 if(ddays.indexOf(d.getDay()) != -1){
17346                     cell.title = ddaysText;
17347                     cell.className = " fc-state-disabled";
17348                 }
17349             }
17350             if(ddMatch && format){
17351                 var fvalue = d.dateFormat(format);
17352                 if(ddMatch.test(fvalue)){
17353                     cell.title = ddText.replace("%0", fvalue);
17354                     cell.className = " fc-state-disabled";
17355                 }
17356             }
17357             
17358             if (!cell.initialClassName) {
17359                 cell.initialClassName = cell.dom.className;
17360             }
17361             
17362             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17363         };
17364
17365         var i = 0;
17366         
17367         for(; i < startingPos; i++) {
17368             textEls[i].innerHTML = (++prevStart);
17369             d.setDate(d.getDate()+1);
17370             
17371             cells[i].className = "fc-past fc-other-month";
17372             setCellClass(this, cells[i]);
17373         }
17374         
17375         var intDay = 0;
17376         
17377         for(; i < days; i++){
17378             intDay = i - startingPos + 1;
17379             textEls[i].innerHTML = (intDay);
17380             d.setDate(d.getDate()+1);
17381             
17382             cells[i].className = ''; // "x-date-active";
17383             setCellClass(this, cells[i]);
17384         }
17385         var extraDays = 0;
17386         
17387         for(; i < 42; i++) {
17388             textEls[i].innerHTML = (++extraDays);
17389             d.setDate(d.getDate()+1);
17390             
17391             cells[i].className = "fc-future fc-other-month";
17392             setCellClass(this, cells[i]);
17393         }
17394         
17395         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17396         
17397         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17398         
17399         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17400         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17401         
17402         if(totalRows != 6){
17403             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17404             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17405         }
17406         
17407         this.fireEvent('monthchange', this, date);
17408         
17409         
17410         /*
17411         if(!this.internalRender){
17412             var main = this.el.dom.firstChild;
17413             var w = main.offsetWidth;
17414             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17415             Roo.fly(main).setWidth(w);
17416             this.internalRender = true;
17417             // opera does not respect the auto grow header center column
17418             // then, after it gets a width opera refuses to recalculate
17419             // without a second pass
17420             if(Roo.isOpera && !this.secondPass){
17421                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17422                 this.secondPass = true;
17423                 this.update.defer(10, this, [date]);
17424             }
17425         }
17426         */
17427         
17428     },
17429     
17430     findCell : function(dt) {
17431         dt = dt.clearTime().getTime();
17432         var ret = false;
17433         this.cells.each(function(c){
17434             //Roo.log("check " +c.dateValue + '?=' + dt);
17435             if(c.dateValue == dt){
17436                 ret = c;
17437                 return false;
17438             }
17439             return true;
17440         });
17441         
17442         return ret;
17443     },
17444     
17445     findCells : function(ev) {
17446         var s = ev.start.clone().clearTime().getTime();
17447        // Roo.log(s);
17448         var e= ev.end.clone().clearTime().getTime();
17449        // Roo.log(e);
17450         var ret = [];
17451         this.cells.each(function(c){
17452              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17453             
17454             if(c.dateValue > e){
17455                 return ;
17456             }
17457             if(c.dateValue < s){
17458                 return ;
17459             }
17460             ret.push(c);
17461         });
17462         
17463         return ret;    
17464     },
17465     
17466 //    findBestRow: function(cells)
17467 //    {
17468 //        var ret = 0;
17469 //        
17470 //        for (var i =0 ; i < cells.length;i++) {
17471 //            ret  = Math.max(cells[i].rows || 0,ret);
17472 //        }
17473 //        return ret;
17474 //        
17475 //    },
17476     
17477     
17478     addItem : function(ev)
17479     {
17480         // look for vertical location slot in
17481         var cells = this.findCells(ev);
17482         
17483 //        ev.row = this.findBestRow(cells);
17484         
17485         // work out the location.
17486         
17487         var crow = false;
17488         var rows = [];
17489         for(var i =0; i < cells.length; i++) {
17490             
17491             cells[i].row = cells[0].row;
17492             
17493             if(i == 0){
17494                 cells[i].row = cells[i].row + 1;
17495             }
17496             
17497             if (!crow) {
17498                 crow = {
17499                     start : cells[i],
17500                     end :  cells[i]
17501                 };
17502                 continue;
17503             }
17504             if (crow.start.getY() == cells[i].getY()) {
17505                 // on same row.
17506                 crow.end = cells[i];
17507                 continue;
17508             }
17509             // different row.
17510             rows.push(crow);
17511             crow = {
17512                 start: cells[i],
17513                 end : cells[i]
17514             };
17515             
17516         }
17517         
17518         rows.push(crow);
17519         ev.els = [];
17520         ev.rows = rows;
17521         ev.cells = cells;
17522         
17523         cells[0].events.push(ev);
17524         
17525         this.calevents.push(ev);
17526     },
17527     
17528     clearEvents: function() {
17529         
17530         if(!this.calevents){
17531             return;
17532         }
17533         
17534         Roo.each(this.cells.elements, function(c){
17535             c.row = 0;
17536             c.events = [];
17537             c.more = [];
17538         });
17539         
17540         Roo.each(this.calevents, function(e) {
17541             Roo.each(e.els, function(el) {
17542                 el.un('mouseenter' ,this.onEventEnter, this);
17543                 el.un('mouseleave' ,this.onEventLeave, this);
17544                 el.remove();
17545             },this);
17546         },this);
17547         
17548         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17549             e.remove();
17550         });
17551         
17552     },
17553     
17554     renderEvents: function()
17555     {   
17556         var _this = this;
17557         
17558         this.cells.each(function(c) {
17559             
17560             if(c.row < 5){
17561                 return;
17562             }
17563             
17564             var ev = c.events;
17565             
17566             var r = 4;
17567             if(c.row != c.events.length){
17568                 r = 4 - (4 - (c.row - c.events.length));
17569             }
17570             
17571             c.events = ev.slice(0, r);
17572             c.more = ev.slice(r);
17573             
17574             if(c.more.length && c.more.length == 1){
17575                 c.events.push(c.more.pop());
17576             }
17577             
17578             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17579             
17580         });
17581             
17582         this.cells.each(function(c) {
17583             
17584             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17585             
17586             
17587             for (var e = 0; e < c.events.length; e++){
17588                 var ev = c.events[e];
17589                 var rows = ev.rows;
17590                 
17591                 for(var i = 0; i < rows.length; i++) {
17592                 
17593                     // how many rows should it span..
17594
17595                     var  cfg = {
17596                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17597                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17598
17599                         unselectable : "on",
17600                         cn : [
17601                             {
17602                                 cls: 'fc-event-inner',
17603                                 cn : [
17604     //                                {
17605     //                                  tag:'span',
17606     //                                  cls: 'fc-event-time',
17607     //                                  html : cells.length > 1 ? '' : ev.time
17608     //                                },
17609                                     {
17610                                       tag:'span',
17611                                       cls: 'fc-event-title',
17612                                       html : String.format('{0}', ev.title)
17613                                     }
17614
17615
17616                                 ]
17617                             },
17618                             {
17619                                 cls: 'ui-resizable-handle ui-resizable-e',
17620                                 html : '&nbsp;&nbsp;&nbsp'
17621                             }
17622
17623                         ]
17624                     };
17625
17626                     if (i == 0) {
17627                         cfg.cls += ' fc-event-start';
17628                     }
17629                     if ((i+1) == rows.length) {
17630                         cfg.cls += ' fc-event-end';
17631                     }
17632
17633                     var ctr = _this.el.select('.fc-event-container',true).first();
17634                     var cg = ctr.createChild(cfg);
17635
17636                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17637                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17638
17639                     var r = (c.more.length) ? 1 : 0;
17640                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17641                     cg.setWidth(ebox.right - sbox.x -2);
17642
17643                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17644                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17645                     cg.on('click', _this.onEventClick, _this, ev);
17646
17647                     ev.els.push(cg);
17648                     
17649                 }
17650                 
17651             }
17652             
17653             
17654             if(c.more.length){
17655                 var  cfg = {
17656                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17657                     style : 'position: absolute',
17658                     unselectable : "on",
17659                     cn : [
17660                         {
17661                             cls: 'fc-event-inner',
17662                             cn : [
17663                                 {
17664                                   tag:'span',
17665                                   cls: 'fc-event-title',
17666                                   html : 'More'
17667                                 }
17668
17669
17670                             ]
17671                         },
17672                         {
17673                             cls: 'ui-resizable-handle ui-resizable-e',
17674                             html : '&nbsp;&nbsp;&nbsp'
17675                         }
17676
17677                     ]
17678                 };
17679
17680                 var ctr = _this.el.select('.fc-event-container',true).first();
17681                 var cg = ctr.createChild(cfg);
17682
17683                 var sbox = c.select('.fc-day-content',true).first().getBox();
17684                 var ebox = c.select('.fc-day-content',true).first().getBox();
17685                 //Roo.log(cg);
17686                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17687                 cg.setWidth(ebox.right - sbox.x -2);
17688
17689                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17690                 
17691             }
17692             
17693         });
17694         
17695         
17696         
17697     },
17698     
17699     onEventEnter: function (e, el,event,d) {
17700         this.fireEvent('evententer', this, el, event);
17701     },
17702     
17703     onEventLeave: function (e, el,event,d) {
17704         this.fireEvent('eventleave', this, el, event);
17705     },
17706     
17707     onEventClick: function (e, el,event,d) {
17708         this.fireEvent('eventclick', this, el, event);
17709     },
17710     
17711     onMonthChange: function () {
17712         this.store.load();
17713     },
17714     
17715     onMoreEventClick: function(e, el, more)
17716     {
17717         var _this = this;
17718         
17719         this.calpopover.placement = 'right';
17720         this.calpopover.setTitle('More');
17721         
17722         this.calpopover.setContent('');
17723         
17724         var ctr = this.calpopover.el.select('.popover-content', true).first();
17725         
17726         Roo.each(more, function(m){
17727             var cfg = {
17728                 cls : 'fc-event-hori fc-event-draggable',
17729                 html : m.title
17730             };
17731             var cg = ctr.createChild(cfg);
17732             
17733             cg.on('click', _this.onEventClick, _this, m);
17734         });
17735         
17736         this.calpopover.show(el);
17737         
17738         
17739     },
17740     
17741     onLoad: function () 
17742     {   
17743         this.calevents = [];
17744         var cal = this;
17745         
17746         if(this.store.getCount() > 0){
17747             this.store.data.each(function(d){
17748                cal.addItem({
17749                     id : d.data.id,
17750                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17751                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17752                     time : d.data.start_time,
17753                     title : d.data.title,
17754                     description : d.data.description,
17755                     venue : d.data.venue
17756                 });
17757             });
17758         }
17759         
17760         this.renderEvents();
17761         
17762         if(this.calevents.length && this.loadMask){
17763             this.maskEl.hide();
17764         }
17765     },
17766     
17767     onBeforeLoad: function()
17768     {
17769         this.clearEvents();
17770         if(this.loadMask){
17771             this.maskEl.show();
17772         }
17773     }
17774 });
17775
17776  
17777  /*
17778  * - LGPL
17779  *
17780  * element
17781  * 
17782  */
17783
17784 /**
17785  * @class Roo.bootstrap.Popover
17786  * @extends Roo.bootstrap.Component
17787  * Bootstrap Popover class
17788  * @cfg {String} html contents of the popover   (or false to use children..)
17789  * @cfg {String} title of popover (or false to hide)
17790  * @cfg {String} placement how it is placed
17791  * @cfg {String} trigger click || hover (or false to trigger manually)
17792  * @cfg {String} over what (parent or false to trigger manually.)
17793  * @cfg {Number} delay - delay before showing
17794  
17795  * @constructor
17796  * Create a new Popover
17797  * @param {Object} config The config object
17798  */
17799
17800 Roo.bootstrap.Popover = function(config){
17801     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17802     
17803     this.addEvents({
17804         // raw events
17805          /**
17806          * @event show
17807          * After the popover show
17808          * 
17809          * @param {Roo.bootstrap.Popover} this
17810          */
17811         "show" : true,
17812         /**
17813          * @event hide
17814          * After the popover hide
17815          * 
17816          * @param {Roo.bootstrap.Popover} this
17817          */
17818         "hide" : true
17819     });
17820 };
17821
17822 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17823     
17824     title: 'Fill in a title',
17825     html: false,
17826     
17827     placement : 'right',
17828     trigger : 'hover', // hover
17829     
17830     delay : 0,
17831     
17832     over: 'parent',
17833     
17834     can_build_overlaid : false,
17835     
17836     getChildContainer : function()
17837     {
17838         return this.el.select('.popover-content',true).first();
17839     },
17840     
17841     getAutoCreate : function(){
17842          
17843         var cfg = {
17844            cls : 'popover roo-dynamic',
17845            style: 'display:block',
17846            cn : [
17847                 {
17848                     cls : 'arrow'
17849                 },
17850                 {
17851                     cls : 'popover-inner',
17852                     cn : [
17853                         {
17854                             tag: 'h3',
17855                             cls: 'popover-title popover-header',
17856                             html : this.title
17857                         },
17858                         {
17859                             cls : 'popover-content popover-body',
17860                             html : this.html
17861                         }
17862                     ]
17863                     
17864                 }
17865            ]
17866         };
17867         
17868         return cfg;
17869     },
17870     setTitle: function(str)
17871     {
17872         this.title = str;
17873         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17874     },
17875     setContent: function(str)
17876     {
17877         this.html = str;
17878         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17879     },
17880     // as it get's added to the bottom of the page.
17881     onRender : function(ct, position)
17882     {
17883         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17884         if(!this.el){
17885             var cfg = Roo.apply({},  this.getAutoCreate());
17886             cfg.id = Roo.id();
17887             
17888             if (this.cls) {
17889                 cfg.cls += ' ' + this.cls;
17890             }
17891             if (this.style) {
17892                 cfg.style = this.style;
17893             }
17894             //Roo.log("adding to ");
17895             this.el = Roo.get(document.body).createChild(cfg, position);
17896 //            Roo.log(this.el);
17897         }
17898         this.initEvents();
17899     },
17900     
17901     initEvents : function()
17902     {
17903         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17904         this.el.enableDisplayMode('block');
17905         this.el.hide();
17906         if (this.over === false) {
17907             return; 
17908         }
17909         if (this.triggers === false) {
17910             return;
17911         }
17912         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17913         var triggers = this.trigger ? this.trigger.split(' ') : [];
17914         Roo.each(triggers, function(trigger) {
17915         
17916             if (trigger == 'click') {
17917                 on_el.on('click', this.toggle, this);
17918             } else if (trigger != 'manual') {
17919                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17920                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17921       
17922                 on_el.on(eventIn  ,this.enter, this);
17923                 on_el.on(eventOut, this.leave, this);
17924             }
17925         }, this);
17926         
17927     },
17928     
17929     
17930     // private
17931     timeout : null,
17932     hoverState : null,
17933     
17934     toggle : function () {
17935         this.hoverState == 'in' ? this.leave() : this.enter();
17936     },
17937     
17938     enter : function () {
17939         
17940         clearTimeout(this.timeout);
17941     
17942         this.hoverState = 'in';
17943     
17944         if (!this.delay || !this.delay.show) {
17945             this.show();
17946             return;
17947         }
17948         var _t = this;
17949         this.timeout = setTimeout(function () {
17950             if (_t.hoverState == 'in') {
17951                 _t.show();
17952             }
17953         }, this.delay.show)
17954     },
17955     
17956     leave : function() {
17957         clearTimeout(this.timeout);
17958     
17959         this.hoverState = 'out';
17960     
17961         if (!this.delay || !this.delay.hide) {
17962             this.hide();
17963             return;
17964         }
17965         var _t = this;
17966         this.timeout = setTimeout(function () {
17967             if (_t.hoverState == 'out') {
17968                 _t.hide();
17969             }
17970         }, this.delay.hide)
17971     },
17972     
17973     show : function (on_el)
17974     {
17975         if (!on_el) {
17976             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17977         }
17978         
17979         // set content.
17980         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17981         if (this.html !== false) {
17982             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17983         }
17984         this.el.removeClass([
17985             'fade','top','bottom', 'left', 'right','in',
17986             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17987         ]);
17988         if (!this.title.length) {
17989             this.el.select('.popover-title',true).hide();
17990         }
17991         
17992         var placement = typeof this.placement == 'function' ?
17993             this.placement.call(this, this.el, on_el) :
17994             this.placement;
17995             
17996         var autoToken = /\s?auto?\s?/i;
17997         var autoPlace = autoToken.test(placement);
17998         if (autoPlace) {
17999             placement = placement.replace(autoToken, '') || 'top';
18000         }
18001         
18002         //this.el.detach()
18003         //this.el.setXY([0,0]);
18004         this.el.show();
18005         this.el.dom.style.display='block';
18006         this.el.addClass(placement);
18007         
18008         //this.el.appendTo(on_el);
18009         
18010         var p = this.getPosition();
18011         var box = this.el.getBox();
18012         
18013         if (autoPlace) {
18014             // fixme..
18015         }
18016         var align = Roo.bootstrap.Popover.alignment[placement];
18017         
18018 //        Roo.log(align);
18019         this.el.alignTo(on_el, align[0],align[1]);
18020         //var arrow = this.el.select('.arrow',true).first();
18021         //arrow.set(align[2], 
18022         
18023         this.el.addClass('in');
18024         
18025         
18026         if (this.el.hasClass('fade')) {
18027             // fade it?
18028         }
18029         
18030         this.hoverState = 'in';
18031         
18032         this.fireEvent('show', this);
18033         
18034     },
18035     hide : function()
18036     {
18037         this.el.setXY([0,0]);
18038         this.el.removeClass('in');
18039         this.el.hide();
18040         this.hoverState = null;
18041         
18042         this.fireEvent('hide', this);
18043     }
18044     
18045 });
18046
18047 Roo.bootstrap.Popover.alignment = {
18048     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18049     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18050     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18051     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18052 };
18053
18054  /*
18055  * - LGPL
18056  *
18057  * Progress
18058  * 
18059  */
18060
18061 /**
18062  * @class Roo.bootstrap.Progress
18063  * @extends Roo.bootstrap.Component
18064  * Bootstrap Progress class
18065  * @cfg {Boolean} striped striped of the progress bar
18066  * @cfg {Boolean} active animated of the progress bar
18067  * 
18068  * 
18069  * @constructor
18070  * Create a new Progress
18071  * @param {Object} config The config object
18072  */
18073
18074 Roo.bootstrap.Progress = function(config){
18075     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18076 };
18077
18078 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18079     
18080     striped : false,
18081     active: false,
18082     
18083     getAutoCreate : function(){
18084         var cfg = {
18085             tag: 'div',
18086             cls: 'progress'
18087         };
18088         
18089         
18090         if(this.striped){
18091             cfg.cls += ' progress-striped';
18092         }
18093       
18094         if(this.active){
18095             cfg.cls += ' active';
18096         }
18097         
18098         
18099         return cfg;
18100     }
18101    
18102 });
18103
18104  
18105
18106  /*
18107  * - LGPL
18108  *
18109  * ProgressBar
18110  * 
18111  */
18112
18113 /**
18114  * @class Roo.bootstrap.ProgressBar
18115  * @extends Roo.bootstrap.Component
18116  * Bootstrap ProgressBar class
18117  * @cfg {Number} aria_valuenow aria-value now
18118  * @cfg {Number} aria_valuemin aria-value min
18119  * @cfg {Number} aria_valuemax aria-value max
18120  * @cfg {String} label label for the progress bar
18121  * @cfg {String} panel (success | info | warning | danger )
18122  * @cfg {String} role role of the progress bar
18123  * @cfg {String} sr_only text
18124  * 
18125  * 
18126  * @constructor
18127  * Create a new ProgressBar
18128  * @param {Object} config The config object
18129  */
18130
18131 Roo.bootstrap.ProgressBar = function(config){
18132     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18133 };
18134
18135 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18136     
18137     aria_valuenow : 0,
18138     aria_valuemin : 0,
18139     aria_valuemax : 100,
18140     label : false,
18141     panel : false,
18142     role : false,
18143     sr_only: false,
18144     
18145     getAutoCreate : function()
18146     {
18147         
18148         var cfg = {
18149             tag: 'div',
18150             cls: 'progress-bar',
18151             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18152         };
18153         
18154         if(this.sr_only){
18155             cfg.cn = {
18156                 tag: 'span',
18157                 cls: 'sr-only',
18158                 html: this.sr_only
18159             }
18160         }
18161         
18162         if(this.role){
18163             cfg.role = this.role;
18164         }
18165         
18166         if(this.aria_valuenow){
18167             cfg['aria-valuenow'] = this.aria_valuenow;
18168         }
18169         
18170         if(this.aria_valuemin){
18171             cfg['aria-valuemin'] = this.aria_valuemin;
18172         }
18173         
18174         if(this.aria_valuemax){
18175             cfg['aria-valuemax'] = this.aria_valuemax;
18176         }
18177         
18178         if(this.label && !this.sr_only){
18179             cfg.html = this.label;
18180         }
18181         
18182         if(this.panel){
18183             cfg.cls += ' progress-bar-' + this.panel;
18184         }
18185         
18186         return cfg;
18187     },
18188     
18189     update : function(aria_valuenow)
18190     {
18191         this.aria_valuenow = aria_valuenow;
18192         
18193         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18194     }
18195    
18196 });
18197
18198  
18199
18200  /*
18201  * - LGPL
18202  *
18203  * column
18204  * 
18205  */
18206
18207 /**
18208  * @class Roo.bootstrap.TabGroup
18209  * @extends Roo.bootstrap.Column
18210  * Bootstrap Column class
18211  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18212  * @cfg {Boolean} carousel true to make the group behave like a carousel
18213  * @cfg {Boolean} bullets show bullets for the panels
18214  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18215  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18216  * @cfg {Boolean} showarrow (true|false) show arrow default true
18217  * 
18218  * @constructor
18219  * Create a new TabGroup
18220  * @param {Object} config The config object
18221  */
18222
18223 Roo.bootstrap.TabGroup = function(config){
18224     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18225     if (!this.navId) {
18226         this.navId = Roo.id();
18227     }
18228     this.tabs = [];
18229     Roo.bootstrap.TabGroup.register(this);
18230     
18231 };
18232
18233 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18234     
18235     carousel : false,
18236     transition : false,
18237     bullets : 0,
18238     timer : 0,
18239     autoslide : false,
18240     slideFn : false,
18241     slideOnTouch : false,
18242     showarrow : true,
18243     
18244     getAutoCreate : function()
18245     {
18246         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18247         
18248         cfg.cls += ' tab-content';
18249         
18250         if (this.carousel) {
18251             cfg.cls += ' carousel slide';
18252             
18253             cfg.cn = [{
18254                cls : 'carousel-inner',
18255                cn : []
18256             }];
18257         
18258             if(this.bullets  && !Roo.isTouch){
18259                 
18260                 var bullets = {
18261                     cls : 'carousel-bullets',
18262                     cn : []
18263                 };
18264                
18265                 if(this.bullets_cls){
18266                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18267                 }
18268                 
18269                 bullets.cn.push({
18270                     cls : 'clear'
18271                 });
18272                 
18273                 cfg.cn[0].cn.push(bullets);
18274             }
18275             
18276             if(this.showarrow){
18277                 cfg.cn[0].cn.push({
18278                     tag : 'div',
18279                     class : 'carousel-arrow',
18280                     cn : [
18281                         {
18282                             tag : 'div',
18283                             class : 'carousel-prev',
18284                             cn : [
18285                                 {
18286                                     tag : 'i',
18287                                     class : 'fa fa-chevron-left'
18288                                 }
18289                             ]
18290                         },
18291                         {
18292                             tag : 'div',
18293                             class : 'carousel-next',
18294                             cn : [
18295                                 {
18296                                     tag : 'i',
18297                                     class : 'fa fa-chevron-right'
18298                                 }
18299                             ]
18300                         }
18301                     ]
18302                 });
18303             }
18304             
18305         }
18306         
18307         return cfg;
18308     },
18309     
18310     initEvents:  function()
18311     {
18312 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18313 //            this.el.on("touchstart", this.onTouchStart, this);
18314 //        }
18315         
18316         if(this.autoslide){
18317             var _this = this;
18318             
18319             this.slideFn = window.setInterval(function() {
18320                 _this.showPanelNext();
18321             }, this.timer);
18322         }
18323         
18324         if(this.showarrow){
18325             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18326             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18327         }
18328         
18329         
18330     },
18331     
18332 //    onTouchStart : function(e, el, o)
18333 //    {
18334 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18335 //            return;
18336 //        }
18337 //        
18338 //        this.showPanelNext();
18339 //    },
18340     
18341     
18342     getChildContainer : function()
18343     {
18344         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18345     },
18346     
18347     /**
18348     * register a Navigation item
18349     * @param {Roo.bootstrap.NavItem} the navitem to add
18350     */
18351     register : function(item)
18352     {
18353         this.tabs.push( item);
18354         item.navId = this.navId; // not really needed..
18355         this.addBullet();
18356     
18357     },
18358     
18359     getActivePanel : function()
18360     {
18361         var r = false;
18362         Roo.each(this.tabs, function(t) {
18363             if (t.active) {
18364                 r = t;
18365                 return false;
18366             }
18367             return null;
18368         });
18369         return r;
18370         
18371     },
18372     getPanelByName : function(n)
18373     {
18374         var r = false;
18375         Roo.each(this.tabs, function(t) {
18376             if (t.tabId == n) {
18377                 r = t;
18378                 return false;
18379             }
18380             return null;
18381         });
18382         return r;
18383     },
18384     indexOfPanel : function(p)
18385     {
18386         var r = false;
18387         Roo.each(this.tabs, function(t,i) {
18388             if (t.tabId == p.tabId) {
18389                 r = i;
18390                 return false;
18391             }
18392             return null;
18393         });
18394         return r;
18395     },
18396     /**
18397      * show a specific panel
18398      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18399      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18400      */
18401     showPanel : function (pan)
18402     {
18403         if(this.transition || typeof(pan) == 'undefined'){
18404             Roo.log("waiting for the transitionend");
18405             return false;
18406         }
18407         
18408         if (typeof(pan) == 'number') {
18409             pan = this.tabs[pan];
18410         }
18411         
18412         if (typeof(pan) == 'string') {
18413             pan = this.getPanelByName(pan);
18414         }
18415         
18416         var cur = this.getActivePanel();
18417         
18418         if(!pan || !cur){
18419             Roo.log('pan or acitve pan is undefined');
18420             return false;
18421         }
18422         
18423         if (pan.tabId == this.getActivePanel().tabId) {
18424             return true;
18425         }
18426         
18427         if (false === cur.fireEvent('beforedeactivate')) {
18428             return false;
18429         }
18430         
18431         if(this.bullets > 0 && !Roo.isTouch){
18432             this.setActiveBullet(this.indexOfPanel(pan));
18433         }
18434         
18435         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18436             
18437             //class="carousel-item carousel-item-next carousel-item-left"
18438             
18439             this.transition = true;
18440             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18441             var lr = dir == 'next' ? 'left' : 'right';
18442             pan.el.addClass(dir); // or prev
18443             pan.el.addClass('carousel-item-' + dir); // or prev
18444             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18445             cur.el.addClass(lr); // or right
18446             pan.el.addClass(lr);
18447             cur.el.addClass('carousel-item-' +lr); // or right
18448             pan.el.addClass('carousel-item-' +lr);
18449             
18450             
18451             var _this = this;
18452             cur.el.on('transitionend', function() {
18453                 Roo.log("trans end?");
18454                 
18455                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18456                 pan.setActive(true);
18457                 
18458                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18459                 cur.setActive(false);
18460                 
18461                 _this.transition = false;
18462                 
18463             }, this, { single:  true } );
18464             
18465             return true;
18466         }
18467         
18468         cur.setActive(false);
18469         pan.setActive(true);
18470         
18471         return true;
18472         
18473     },
18474     showPanelNext : function()
18475     {
18476         var i = this.indexOfPanel(this.getActivePanel());
18477         
18478         if (i >= this.tabs.length - 1 && !this.autoslide) {
18479             return;
18480         }
18481         
18482         if (i >= this.tabs.length - 1 && this.autoslide) {
18483             i = -1;
18484         }
18485         
18486         this.showPanel(this.tabs[i+1]);
18487     },
18488     
18489     showPanelPrev : function()
18490     {
18491         var i = this.indexOfPanel(this.getActivePanel());
18492         
18493         if (i  < 1 && !this.autoslide) {
18494             return;
18495         }
18496         
18497         if (i < 1 && this.autoslide) {
18498             i = this.tabs.length;
18499         }
18500         
18501         this.showPanel(this.tabs[i-1]);
18502     },
18503     
18504     
18505     addBullet: function()
18506     {
18507         if(!this.bullets || Roo.isTouch){
18508             return;
18509         }
18510         var ctr = this.el.select('.carousel-bullets',true).first();
18511         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18512         var bullet = ctr.createChild({
18513             cls : 'bullet bullet-' + i
18514         },ctr.dom.lastChild);
18515         
18516         
18517         var _this = this;
18518         
18519         bullet.on('click', (function(e, el, o, ii, t){
18520
18521             e.preventDefault();
18522
18523             this.showPanel(ii);
18524
18525             if(this.autoslide && this.slideFn){
18526                 clearInterval(this.slideFn);
18527                 this.slideFn = window.setInterval(function() {
18528                     _this.showPanelNext();
18529                 }, this.timer);
18530             }
18531
18532         }).createDelegate(this, [i, bullet], true));
18533                 
18534         
18535     },
18536      
18537     setActiveBullet : function(i)
18538     {
18539         if(Roo.isTouch){
18540             return;
18541         }
18542         
18543         Roo.each(this.el.select('.bullet', true).elements, function(el){
18544             el.removeClass('selected');
18545         });
18546
18547         var bullet = this.el.select('.bullet-' + i, true).first();
18548         
18549         if(!bullet){
18550             return;
18551         }
18552         
18553         bullet.addClass('selected');
18554     }
18555     
18556     
18557   
18558 });
18559
18560  
18561
18562  
18563  
18564 Roo.apply(Roo.bootstrap.TabGroup, {
18565     
18566     groups: {},
18567      /**
18568     * register a Navigation Group
18569     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18570     */
18571     register : function(navgrp)
18572     {
18573         this.groups[navgrp.navId] = navgrp;
18574         
18575     },
18576     /**
18577     * fetch a Navigation Group based on the navigation ID
18578     * if one does not exist , it will get created.
18579     * @param {string} the navgroup to add
18580     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18581     */
18582     get: function(navId) {
18583         if (typeof(this.groups[navId]) == 'undefined') {
18584             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18585         }
18586         return this.groups[navId] ;
18587     }
18588     
18589     
18590     
18591 });
18592
18593  /*
18594  * - LGPL
18595  *
18596  * TabPanel
18597  * 
18598  */
18599
18600 /**
18601  * @class Roo.bootstrap.TabPanel
18602  * @extends Roo.bootstrap.Component
18603  * Bootstrap TabPanel class
18604  * @cfg {Boolean} active panel active
18605  * @cfg {String} html panel content
18606  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18607  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18608  * @cfg {String} href click to link..
18609  * 
18610  * 
18611  * @constructor
18612  * Create a new TabPanel
18613  * @param {Object} config The config object
18614  */
18615
18616 Roo.bootstrap.TabPanel = function(config){
18617     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18618     this.addEvents({
18619         /**
18620              * @event changed
18621              * Fires when the active status changes
18622              * @param {Roo.bootstrap.TabPanel} this
18623              * @param {Boolean} state the new state
18624             
18625          */
18626         'changed': true,
18627         /**
18628              * @event beforedeactivate
18629              * Fires before a tab is de-activated - can be used to do validation on a form.
18630              * @param {Roo.bootstrap.TabPanel} this
18631              * @return {Boolean} false if there is an error
18632             
18633          */
18634         'beforedeactivate': true
18635      });
18636     
18637     this.tabId = this.tabId || Roo.id();
18638   
18639 };
18640
18641 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18642     
18643     active: false,
18644     html: false,
18645     tabId: false,
18646     navId : false,
18647     href : '',
18648     
18649     getAutoCreate : function(){
18650         
18651         
18652         var cfg = {
18653             tag: 'div',
18654             // item is needed for carousel - not sure if it has any effect otherwise
18655             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18656             html: this.html || ''
18657         };
18658         
18659         if(this.active){
18660             cfg.cls += ' active';
18661         }
18662         
18663         if(this.tabId){
18664             cfg.tabId = this.tabId;
18665         }
18666         
18667         
18668         
18669         return cfg;
18670     },
18671     
18672     initEvents:  function()
18673     {
18674         var p = this.parent();
18675         
18676         this.navId = this.navId || p.navId;
18677         
18678         if (typeof(this.navId) != 'undefined') {
18679             // not really needed.. but just in case.. parent should be a NavGroup.
18680             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18681             
18682             tg.register(this);
18683             
18684             var i = tg.tabs.length - 1;
18685             
18686             if(this.active && tg.bullets > 0 && i < tg.bullets){
18687                 tg.setActiveBullet(i);
18688             }
18689         }
18690         
18691         this.el.on('click', this.onClick, this);
18692         
18693         if(Roo.isTouch){
18694             this.el.on("touchstart", this.onTouchStart, this);
18695             this.el.on("touchmove", this.onTouchMove, this);
18696             this.el.on("touchend", this.onTouchEnd, this);
18697         }
18698         
18699     },
18700     
18701     onRender : function(ct, position)
18702     {
18703         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18704     },
18705     
18706     setActive : function(state)
18707     {
18708         Roo.log("panel - set active " + this.tabId + "=" + state);
18709         
18710         this.active = state;
18711         if (!state) {
18712             this.el.removeClass('active');
18713             
18714         } else  if (!this.el.hasClass('active')) {
18715             this.el.addClass('active');
18716         }
18717         
18718         this.fireEvent('changed', this, state);
18719     },
18720     
18721     onClick : function(e)
18722     {
18723         e.preventDefault();
18724         
18725         if(!this.href.length){
18726             return;
18727         }
18728         
18729         window.location.href = this.href;
18730     },
18731     
18732     startX : 0,
18733     startY : 0,
18734     endX : 0,
18735     endY : 0,
18736     swiping : false,
18737     
18738     onTouchStart : function(e)
18739     {
18740         this.swiping = false;
18741         
18742         this.startX = e.browserEvent.touches[0].clientX;
18743         this.startY = e.browserEvent.touches[0].clientY;
18744     },
18745     
18746     onTouchMove : function(e)
18747     {
18748         this.swiping = true;
18749         
18750         this.endX = e.browserEvent.touches[0].clientX;
18751         this.endY = e.browserEvent.touches[0].clientY;
18752     },
18753     
18754     onTouchEnd : function(e)
18755     {
18756         if(!this.swiping){
18757             this.onClick(e);
18758             return;
18759         }
18760         
18761         var tabGroup = this.parent();
18762         
18763         if(this.endX > this.startX){ // swiping right
18764             tabGroup.showPanelPrev();
18765             return;
18766         }
18767         
18768         if(this.startX > this.endX){ // swiping left
18769             tabGroup.showPanelNext();
18770             return;
18771         }
18772     }
18773     
18774     
18775 });
18776  
18777
18778  
18779
18780  /*
18781  * - LGPL
18782  *
18783  * DateField
18784  * 
18785  */
18786
18787 /**
18788  * @class Roo.bootstrap.DateField
18789  * @extends Roo.bootstrap.Input
18790  * Bootstrap DateField class
18791  * @cfg {Number} weekStart default 0
18792  * @cfg {String} viewMode default empty, (months|years)
18793  * @cfg {String} minViewMode default empty, (months|years)
18794  * @cfg {Number} startDate default -Infinity
18795  * @cfg {Number} endDate default Infinity
18796  * @cfg {Boolean} todayHighlight default false
18797  * @cfg {Boolean} todayBtn default false
18798  * @cfg {Boolean} calendarWeeks default false
18799  * @cfg {Object} daysOfWeekDisabled default empty
18800  * @cfg {Boolean} singleMode default false (true | false)
18801  * 
18802  * @cfg {Boolean} keyboardNavigation default true
18803  * @cfg {String} language default en
18804  * 
18805  * @constructor
18806  * Create a new DateField
18807  * @param {Object} config The config object
18808  */
18809
18810 Roo.bootstrap.DateField = function(config){
18811     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18812      this.addEvents({
18813             /**
18814              * @event show
18815              * Fires when this field show.
18816              * @param {Roo.bootstrap.DateField} this
18817              * @param {Mixed} date The date value
18818              */
18819             show : true,
18820             /**
18821              * @event show
18822              * Fires when this field hide.
18823              * @param {Roo.bootstrap.DateField} this
18824              * @param {Mixed} date The date value
18825              */
18826             hide : true,
18827             /**
18828              * @event select
18829              * Fires when select a date.
18830              * @param {Roo.bootstrap.DateField} this
18831              * @param {Mixed} date The date value
18832              */
18833             select : true,
18834             /**
18835              * @event beforeselect
18836              * Fires when before select a date.
18837              * @param {Roo.bootstrap.DateField} this
18838              * @param {Mixed} date The date value
18839              */
18840             beforeselect : true
18841         });
18842 };
18843
18844 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18845     
18846     /**
18847      * @cfg {String} format
18848      * The default date format string which can be overriden for localization support.  The format must be
18849      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18850      */
18851     format : "m/d/y",
18852     /**
18853      * @cfg {String} altFormats
18854      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18855      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18856      */
18857     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18858     
18859     weekStart : 0,
18860     
18861     viewMode : '',
18862     
18863     minViewMode : '',
18864     
18865     todayHighlight : false,
18866     
18867     todayBtn: false,
18868     
18869     language: 'en',
18870     
18871     keyboardNavigation: true,
18872     
18873     calendarWeeks: false,
18874     
18875     startDate: -Infinity,
18876     
18877     endDate: Infinity,
18878     
18879     daysOfWeekDisabled: [],
18880     
18881     _events: [],
18882     
18883     singleMode : false,
18884     
18885     UTCDate: function()
18886     {
18887         return new Date(Date.UTC.apply(Date, arguments));
18888     },
18889     
18890     UTCToday: function()
18891     {
18892         var today = new Date();
18893         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18894     },
18895     
18896     getDate: function() {
18897             var d = this.getUTCDate();
18898             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18899     },
18900     
18901     getUTCDate: function() {
18902             return this.date;
18903     },
18904     
18905     setDate: function(d) {
18906             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18907     },
18908     
18909     setUTCDate: function(d) {
18910             this.date = d;
18911             this.setValue(this.formatDate(this.date));
18912     },
18913         
18914     onRender: function(ct, position)
18915     {
18916         
18917         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18918         
18919         this.language = this.language || 'en';
18920         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18921         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18922         
18923         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18924         this.format = this.format || 'm/d/y';
18925         this.isInline = false;
18926         this.isInput = true;
18927         this.component = this.el.select('.add-on', true).first() || false;
18928         this.component = (this.component && this.component.length === 0) ? false : this.component;
18929         this.hasInput = this.component && this.inputEl().length;
18930         
18931         if (typeof(this.minViewMode === 'string')) {
18932             switch (this.minViewMode) {
18933                 case 'months':
18934                     this.minViewMode = 1;
18935                     break;
18936                 case 'years':
18937                     this.minViewMode = 2;
18938                     break;
18939                 default:
18940                     this.minViewMode = 0;
18941                     break;
18942             }
18943         }
18944         
18945         if (typeof(this.viewMode === 'string')) {
18946             switch (this.viewMode) {
18947                 case 'months':
18948                     this.viewMode = 1;
18949                     break;
18950                 case 'years':
18951                     this.viewMode = 2;
18952                     break;
18953                 default:
18954                     this.viewMode = 0;
18955                     break;
18956             }
18957         }
18958                 
18959         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18960         
18961 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18962         
18963         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18964         
18965         this.picker().on('mousedown', this.onMousedown, this);
18966         this.picker().on('click', this.onClick, this);
18967         
18968         this.picker().addClass('datepicker-dropdown');
18969         
18970         this.startViewMode = this.viewMode;
18971         
18972         if(this.singleMode){
18973             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18974                 v.setVisibilityMode(Roo.Element.DISPLAY);
18975                 v.hide();
18976             });
18977             
18978             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18979                 v.setStyle('width', '189px');
18980             });
18981         }
18982         
18983         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18984             if(!this.calendarWeeks){
18985                 v.remove();
18986                 return;
18987             }
18988             
18989             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18990             v.attr('colspan', function(i, val){
18991                 return parseInt(val) + 1;
18992             });
18993         });
18994                         
18995         
18996         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18997         
18998         this.setStartDate(this.startDate);
18999         this.setEndDate(this.endDate);
19000         
19001         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19002         
19003         this.fillDow();
19004         this.fillMonths();
19005         this.update();
19006         this.showMode();
19007         
19008         if(this.isInline) {
19009             this.showPopup();
19010         }
19011     },
19012     
19013     picker : function()
19014     {
19015         return this.pickerEl;
19016 //        return this.el.select('.datepicker', true).first();
19017     },
19018     
19019     fillDow: function()
19020     {
19021         var dowCnt = this.weekStart;
19022         
19023         var dow = {
19024             tag: 'tr',
19025             cn: [
19026                 
19027             ]
19028         };
19029         
19030         if(this.calendarWeeks){
19031             dow.cn.push({
19032                 tag: 'th',
19033                 cls: 'cw',
19034                 html: '&nbsp;'
19035             })
19036         }
19037         
19038         while (dowCnt < this.weekStart + 7) {
19039             dow.cn.push({
19040                 tag: 'th',
19041                 cls: 'dow',
19042                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19043             });
19044         }
19045         
19046         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19047     },
19048     
19049     fillMonths: function()
19050     {    
19051         var i = 0;
19052         var months = this.picker().select('>.datepicker-months td', true).first();
19053         
19054         months.dom.innerHTML = '';
19055         
19056         while (i < 12) {
19057             var month = {
19058                 tag: 'span',
19059                 cls: 'month',
19060                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19061             };
19062             
19063             months.createChild(month);
19064         }
19065         
19066     },
19067     
19068     update: function()
19069     {
19070         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;
19071         
19072         if (this.date < this.startDate) {
19073             this.viewDate = new Date(this.startDate);
19074         } else if (this.date > this.endDate) {
19075             this.viewDate = new Date(this.endDate);
19076         } else {
19077             this.viewDate = new Date(this.date);
19078         }
19079         
19080         this.fill();
19081     },
19082     
19083     fill: function() 
19084     {
19085         var d = new Date(this.viewDate),
19086                 year = d.getUTCFullYear(),
19087                 month = d.getUTCMonth(),
19088                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19089                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19090                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19091                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19092                 currentDate = this.date && this.date.valueOf(),
19093                 today = this.UTCToday();
19094         
19095         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19096         
19097 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19098         
19099 //        this.picker.select('>tfoot th.today').
19100 //                                              .text(dates[this.language].today)
19101 //                                              .toggle(this.todayBtn !== false);
19102     
19103         this.updateNavArrows();
19104         this.fillMonths();
19105                                                 
19106         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19107         
19108         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19109          
19110         prevMonth.setUTCDate(day);
19111         
19112         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19113         
19114         var nextMonth = new Date(prevMonth);
19115         
19116         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19117         
19118         nextMonth = nextMonth.valueOf();
19119         
19120         var fillMonths = false;
19121         
19122         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19123         
19124         while(prevMonth.valueOf() <= nextMonth) {
19125             var clsName = '';
19126             
19127             if (prevMonth.getUTCDay() === this.weekStart) {
19128                 if(fillMonths){
19129                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19130                 }
19131                     
19132                 fillMonths = {
19133                     tag: 'tr',
19134                     cn: []
19135                 };
19136                 
19137                 if(this.calendarWeeks){
19138                     // ISO 8601: First week contains first thursday.
19139                     // ISO also states week starts on Monday, but we can be more abstract here.
19140                     var
19141                     // Start of current week: based on weekstart/current date
19142                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19143                     // Thursday of this week
19144                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19145                     // First Thursday of year, year from thursday
19146                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19147                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19148                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19149                     
19150                     fillMonths.cn.push({
19151                         tag: 'td',
19152                         cls: 'cw',
19153                         html: calWeek
19154                     });
19155                 }
19156             }
19157             
19158             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19159                 clsName += ' old';
19160             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19161                 clsName += ' new';
19162             }
19163             if (this.todayHighlight &&
19164                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19165                 prevMonth.getUTCMonth() == today.getMonth() &&
19166                 prevMonth.getUTCDate() == today.getDate()) {
19167                 clsName += ' today';
19168             }
19169             
19170             if (currentDate && prevMonth.valueOf() === currentDate) {
19171                 clsName += ' active';
19172             }
19173             
19174             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19175                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19176                     clsName += ' disabled';
19177             }
19178             
19179             fillMonths.cn.push({
19180                 tag: 'td',
19181                 cls: 'day ' + clsName,
19182                 html: prevMonth.getDate()
19183             });
19184             
19185             prevMonth.setDate(prevMonth.getDate()+1);
19186         }
19187           
19188         var currentYear = this.date && this.date.getUTCFullYear();
19189         var currentMonth = this.date && this.date.getUTCMonth();
19190         
19191         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19192         
19193         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19194             v.removeClass('active');
19195             
19196             if(currentYear === year && k === currentMonth){
19197                 v.addClass('active');
19198             }
19199             
19200             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19201                 v.addClass('disabled');
19202             }
19203             
19204         });
19205         
19206         
19207         year = parseInt(year/10, 10) * 10;
19208         
19209         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19210         
19211         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19212         
19213         year -= 1;
19214         for (var i = -1; i < 11; i++) {
19215             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19216                 tag: 'span',
19217                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19218                 html: year
19219             });
19220             
19221             year += 1;
19222         }
19223     },
19224     
19225     showMode: function(dir) 
19226     {
19227         if (dir) {
19228             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19229         }
19230         
19231         Roo.each(this.picker().select('>div',true).elements, function(v){
19232             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19233             v.hide();
19234         });
19235         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19236     },
19237     
19238     place: function()
19239     {
19240         if(this.isInline) {
19241             return;
19242         }
19243         
19244         this.picker().removeClass(['bottom', 'top']);
19245         
19246         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19247             /*
19248              * place to the top of element!
19249              *
19250              */
19251             
19252             this.picker().addClass('top');
19253             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19254             
19255             return;
19256         }
19257         
19258         this.picker().addClass('bottom');
19259         
19260         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19261     },
19262     
19263     parseDate : function(value)
19264     {
19265         if(!value || value instanceof Date){
19266             return value;
19267         }
19268         var v = Date.parseDate(value, this.format);
19269         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19270             v = Date.parseDate(value, 'Y-m-d');
19271         }
19272         if(!v && this.altFormats){
19273             if(!this.altFormatsArray){
19274                 this.altFormatsArray = this.altFormats.split("|");
19275             }
19276             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19277                 v = Date.parseDate(value, this.altFormatsArray[i]);
19278             }
19279         }
19280         return v;
19281     },
19282     
19283     formatDate : function(date, fmt)
19284     {   
19285         return (!date || !(date instanceof Date)) ?
19286         date : date.dateFormat(fmt || this.format);
19287     },
19288     
19289     onFocus : function()
19290     {
19291         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19292         this.showPopup();
19293     },
19294     
19295     onBlur : function()
19296     {
19297         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19298         
19299         var d = this.inputEl().getValue();
19300         
19301         this.setValue(d);
19302                 
19303         this.hidePopup();
19304     },
19305     
19306     showPopup : function()
19307     {
19308         this.picker().show();
19309         this.update();
19310         this.place();
19311         
19312         this.fireEvent('showpopup', this, this.date);
19313     },
19314     
19315     hidePopup : function()
19316     {
19317         if(this.isInline) {
19318             return;
19319         }
19320         this.picker().hide();
19321         this.viewMode = this.startViewMode;
19322         this.showMode();
19323         
19324         this.fireEvent('hidepopup', this, this.date);
19325         
19326     },
19327     
19328     onMousedown: function(e)
19329     {
19330         e.stopPropagation();
19331         e.preventDefault();
19332     },
19333     
19334     keyup: function(e)
19335     {
19336         Roo.bootstrap.DateField.superclass.keyup.call(this);
19337         this.update();
19338     },
19339
19340     setValue: function(v)
19341     {
19342         if(this.fireEvent('beforeselect', this, v) !== false){
19343             var d = new Date(this.parseDate(v) ).clearTime();
19344         
19345             if(isNaN(d.getTime())){
19346                 this.date = this.viewDate = '';
19347                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19348                 return;
19349             }
19350
19351             v = this.formatDate(d);
19352
19353             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19354
19355             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19356
19357             this.update();
19358
19359             this.fireEvent('select', this, this.date);
19360         }
19361     },
19362     
19363     getValue: function()
19364     {
19365         return this.formatDate(this.date);
19366     },
19367     
19368     fireKey: function(e)
19369     {
19370         if (!this.picker().isVisible()){
19371             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19372                 this.showPopup();
19373             }
19374             return;
19375         }
19376         
19377         var dateChanged = false,
19378         dir, day, month,
19379         newDate, newViewDate;
19380         
19381         switch(e.keyCode){
19382             case 27: // escape
19383                 this.hidePopup();
19384                 e.preventDefault();
19385                 break;
19386             case 37: // left
19387             case 39: // right
19388                 if (!this.keyboardNavigation) {
19389                     break;
19390                 }
19391                 dir = e.keyCode == 37 ? -1 : 1;
19392                 
19393                 if (e.ctrlKey){
19394                     newDate = this.moveYear(this.date, dir);
19395                     newViewDate = this.moveYear(this.viewDate, dir);
19396                 } else if (e.shiftKey){
19397                     newDate = this.moveMonth(this.date, dir);
19398                     newViewDate = this.moveMonth(this.viewDate, dir);
19399                 } else {
19400                     newDate = new Date(this.date);
19401                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19402                     newViewDate = new Date(this.viewDate);
19403                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19404                 }
19405                 if (this.dateWithinRange(newDate)){
19406                     this.date = newDate;
19407                     this.viewDate = newViewDate;
19408                     this.setValue(this.formatDate(this.date));
19409 //                    this.update();
19410                     e.preventDefault();
19411                     dateChanged = true;
19412                 }
19413                 break;
19414             case 38: // up
19415             case 40: // down
19416                 if (!this.keyboardNavigation) {
19417                     break;
19418                 }
19419                 dir = e.keyCode == 38 ? -1 : 1;
19420                 if (e.ctrlKey){
19421                     newDate = this.moveYear(this.date, dir);
19422                     newViewDate = this.moveYear(this.viewDate, dir);
19423                 } else if (e.shiftKey){
19424                     newDate = this.moveMonth(this.date, dir);
19425                     newViewDate = this.moveMonth(this.viewDate, dir);
19426                 } else {
19427                     newDate = new Date(this.date);
19428                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19429                     newViewDate = new Date(this.viewDate);
19430                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19431                 }
19432                 if (this.dateWithinRange(newDate)){
19433                     this.date = newDate;
19434                     this.viewDate = newViewDate;
19435                     this.setValue(this.formatDate(this.date));
19436 //                    this.update();
19437                     e.preventDefault();
19438                     dateChanged = true;
19439                 }
19440                 break;
19441             case 13: // enter
19442                 this.setValue(this.formatDate(this.date));
19443                 this.hidePopup();
19444                 e.preventDefault();
19445                 break;
19446             case 9: // tab
19447                 this.setValue(this.formatDate(this.date));
19448                 this.hidePopup();
19449                 break;
19450             case 16: // shift
19451             case 17: // ctrl
19452             case 18: // alt
19453                 break;
19454             default :
19455                 this.hidePopup();
19456                 
19457         }
19458     },
19459     
19460     
19461     onClick: function(e) 
19462     {
19463         e.stopPropagation();
19464         e.preventDefault();
19465         
19466         var target = e.getTarget();
19467         
19468         if(target.nodeName.toLowerCase() === 'i'){
19469             target = Roo.get(target).dom.parentNode;
19470         }
19471         
19472         var nodeName = target.nodeName;
19473         var className = target.className;
19474         var html = target.innerHTML;
19475         //Roo.log(nodeName);
19476         
19477         switch(nodeName.toLowerCase()) {
19478             case 'th':
19479                 switch(className) {
19480                     case 'switch':
19481                         this.showMode(1);
19482                         break;
19483                     case 'prev':
19484                     case 'next':
19485                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19486                         switch(this.viewMode){
19487                                 case 0:
19488                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19489                                         break;
19490                                 case 1:
19491                                 case 2:
19492                                         this.viewDate = this.moveYear(this.viewDate, dir);
19493                                         break;
19494                         }
19495                         this.fill();
19496                         break;
19497                     case 'today':
19498                         var date = new Date();
19499                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19500 //                        this.fill()
19501                         this.setValue(this.formatDate(this.date));
19502                         
19503                         this.hidePopup();
19504                         break;
19505                 }
19506                 break;
19507             case 'span':
19508                 if (className.indexOf('disabled') < 0) {
19509                     this.viewDate.setUTCDate(1);
19510                     if (className.indexOf('month') > -1) {
19511                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19512                     } else {
19513                         var year = parseInt(html, 10) || 0;
19514                         this.viewDate.setUTCFullYear(year);
19515                         
19516                     }
19517                     
19518                     if(this.singleMode){
19519                         this.setValue(this.formatDate(this.viewDate));
19520                         this.hidePopup();
19521                         return;
19522                     }
19523                     
19524                     this.showMode(-1);
19525                     this.fill();
19526                 }
19527                 break;
19528                 
19529             case 'td':
19530                 //Roo.log(className);
19531                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19532                     var day = parseInt(html, 10) || 1;
19533                     var year = this.viewDate.getUTCFullYear(),
19534                         month = this.viewDate.getUTCMonth();
19535
19536                     if (className.indexOf('old') > -1) {
19537                         if(month === 0 ){
19538                             month = 11;
19539                             year -= 1;
19540                         }else{
19541                             month -= 1;
19542                         }
19543                     } else if (className.indexOf('new') > -1) {
19544                         if (month == 11) {
19545                             month = 0;
19546                             year += 1;
19547                         } else {
19548                             month += 1;
19549                         }
19550                     }
19551                     //Roo.log([year,month,day]);
19552                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19553                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19554 //                    this.fill();
19555                     //Roo.log(this.formatDate(this.date));
19556                     this.setValue(this.formatDate(this.date));
19557                     this.hidePopup();
19558                 }
19559                 break;
19560         }
19561     },
19562     
19563     setStartDate: function(startDate)
19564     {
19565         this.startDate = startDate || -Infinity;
19566         if (this.startDate !== -Infinity) {
19567             this.startDate = this.parseDate(this.startDate);
19568         }
19569         this.update();
19570         this.updateNavArrows();
19571     },
19572
19573     setEndDate: function(endDate)
19574     {
19575         this.endDate = endDate || Infinity;
19576         if (this.endDate !== Infinity) {
19577             this.endDate = this.parseDate(this.endDate);
19578         }
19579         this.update();
19580         this.updateNavArrows();
19581     },
19582     
19583     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19584     {
19585         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19586         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19587             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19588         }
19589         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19590             return parseInt(d, 10);
19591         });
19592         this.update();
19593         this.updateNavArrows();
19594     },
19595     
19596     updateNavArrows: function() 
19597     {
19598         if(this.singleMode){
19599             return;
19600         }
19601         
19602         var d = new Date(this.viewDate),
19603         year = d.getUTCFullYear(),
19604         month = d.getUTCMonth();
19605         
19606         Roo.each(this.picker().select('.prev', true).elements, function(v){
19607             v.show();
19608             switch (this.viewMode) {
19609                 case 0:
19610
19611                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19612                         v.hide();
19613                     }
19614                     break;
19615                 case 1:
19616                 case 2:
19617                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19618                         v.hide();
19619                     }
19620                     break;
19621             }
19622         });
19623         
19624         Roo.each(this.picker().select('.next', true).elements, function(v){
19625             v.show();
19626             switch (this.viewMode) {
19627                 case 0:
19628
19629                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19630                         v.hide();
19631                     }
19632                     break;
19633                 case 1:
19634                 case 2:
19635                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19636                         v.hide();
19637                     }
19638                     break;
19639             }
19640         })
19641     },
19642     
19643     moveMonth: function(date, dir)
19644     {
19645         if (!dir) {
19646             return date;
19647         }
19648         var new_date = new Date(date.valueOf()),
19649         day = new_date.getUTCDate(),
19650         month = new_date.getUTCMonth(),
19651         mag = Math.abs(dir),
19652         new_month, test;
19653         dir = dir > 0 ? 1 : -1;
19654         if (mag == 1){
19655             test = dir == -1
19656             // If going back one month, make sure month is not current month
19657             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19658             ? function(){
19659                 return new_date.getUTCMonth() == month;
19660             }
19661             // If going forward one month, make sure month is as expected
19662             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19663             : function(){
19664                 return new_date.getUTCMonth() != new_month;
19665             };
19666             new_month = month + dir;
19667             new_date.setUTCMonth(new_month);
19668             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19669             if (new_month < 0 || new_month > 11) {
19670                 new_month = (new_month + 12) % 12;
19671             }
19672         } else {
19673             // For magnitudes >1, move one month at a time...
19674             for (var i=0; i<mag; i++) {
19675                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19676                 new_date = this.moveMonth(new_date, dir);
19677             }
19678             // ...then reset the day, keeping it in the new month
19679             new_month = new_date.getUTCMonth();
19680             new_date.setUTCDate(day);
19681             test = function(){
19682                 return new_month != new_date.getUTCMonth();
19683             };
19684         }
19685         // Common date-resetting loop -- if date is beyond end of month, make it
19686         // end of month
19687         while (test()){
19688             new_date.setUTCDate(--day);
19689             new_date.setUTCMonth(new_month);
19690         }
19691         return new_date;
19692     },
19693
19694     moveYear: function(date, dir)
19695     {
19696         return this.moveMonth(date, dir*12);
19697     },
19698
19699     dateWithinRange: function(date)
19700     {
19701         return date >= this.startDate && date <= this.endDate;
19702     },
19703
19704     
19705     remove: function() 
19706     {
19707         this.picker().remove();
19708     },
19709     
19710     validateValue : function(value)
19711     {
19712         if(this.getVisibilityEl().hasClass('hidden')){
19713             return true;
19714         }
19715         
19716         if(value.length < 1)  {
19717             if(this.allowBlank){
19718                 return true;
19719             }
19720             return false;
19721         }
19722         
19723         if(value.length < this.minLength){
19724             return false;
19725         }
19726         if(value.length > this.maxLength){
19727             return false;
19728         }
19729         if(this.vtype){
19730             var vt = Roo.form.VTypes;
19731             if(!vt[this.vtype](value, this)){
19732                 return false;
19733             }
19734         }
19735         if(typeof this.validator == "function"){
19736             var msg = this.validator(value);
19737             if(msg !== true){
19738                 return false;
19739             }
19740         }
19741         
19742         if(this.regex && !this.regex.test(value)){
19743             return false;
19744         }
19745         
19746         if(typeof(this.parseDate(value)) == 'undefined'){
19747             return false;
19748         }
19749         
19750         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19751             return false;
19752         }      
19753         
19754         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19755             return false;
19756         } 
19757         
19758         
19759         return true;
19760     },
19761     
19762     reset : function()
19763     {
19764         this.date = this.viewDate = '';
19765         
19766         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19767     }
19768    
19769 });
19770
19771 Roo.apply(Roo.bootstrap.DateField,  {
19772     
19773     head : {
19774         tag: 'thead',
19775         cn: [
19776         {
19777             tag: 'tr',
19778             cn: [
19779             {
19780                 tag: 'th',
19781                 cls: 'prev',
19782                 html: '<i class="fa fa-arrow-left"/>'
19783             },
19784             {
19785                 tag: 'th',
19786                 cls: 'switch',
19787                 colspan: '5'
19788             },
19789             {
19790                 tag: 'th',
19791                 cls: 'next',
19792                 html: '<i class="fa fa-arrow-right"/>'
19793             }
19794
19795             ]
19796         }
19797         ]
19798     },
19799     
19800     content : {
19801         tag: 'tbody',
19802         cn: [
19803         {
19804             tag: 'tr',
19805             cn: [
19806             {
19807                 tag: 'td',
19808                 colspan: '7'
19809             }
19810             ]
19811         }
19812         ]
19813     },
19814     
19815     footer : {
19816         tag: 'tfoot',
19817         cn: [
19818         {
19819             tag: 'tr',
19820             cn: [
19821             {
19822                 tag: 'th',
19823                 colspan: '7',
19824                 cls: 'today'
19825             }
19826                     
19827             ]
19828         }
19829         ]
19830     },
19831     
19832     dates:{
19833         en: {
19834             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19835             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19836             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19837             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19838             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19839             today: "Today"
19840         }
19841     },
19842     
19843     modes: [
19844     {
19845         clsName: 'days',
19846         navFnc: 'Month',
19847         navStep: 1
19848     },
19849     {
19850         clsName: 'months',
19851         navFnc: 'FullYear',
19852         navStep: 1
19853     },
19854     {
19855         clsName: 'years',
19856         navFnc: 'FullYear',
19857         navStep: 10
19858     }]
19859 });
19860
19861 Roo.apply(Roo.bootstrap.DateField,  {
19862   
19863     template : {
19864         tag: 'div',
19865         cls: 'datepicker dropdown-menu roo-dynamic',
19866         cn: [
19867         {
19868             tag: 'div',
19869             cls: 'datepicker-days',
19870             cn: [
19871             {
19872                 tag: 'table',
19873                 cls: 'table-condensed',
19874                 cn:[
19875                 Roo.bootstrap.DateField.head,
19876                 {
19877                     tag: 'tbody'
19878                 },
19879                 Roo.bootstrap.DateField.footer
19880                 ]
19881             }
19882             ]
19883         },
19884         {
19885             tag: 'div',
19886             cls: 'datepicker-months',
19887             cn: [
19888             {
19889                 tag: 'table',
19890                 cls: 'table-condensed',
19891                 cn:[
19892                 Roo.bootstrap.DateField.head,
19893                 Roo.bootstrap.DateField.content,
19894                 Roo.bootstrap.DateField.footer
19895                 ]
19896             }
19897             ]
19898         },
19899         {
19900             tag: 'div',
19901             cls: 'datepicker-years',
19902             cn: [
19903             {
19904                 tag: 'table',
19905                 cls: 'table-condensed',
19906                 cn:[
19907                 Roo.bootstrap.DateField.head,
19908                 Roo.bootstrap.DateField.content,
19909                 Roo.bootstrap.DateField.footer
19910                 ]
19911             }
19912             ]
19913         }
19914         ]
19915     }
19916 });
19917
19918  
19919
19920  /*
19921  * - LGPL
19922  *
19923  * TimeField
19924  * 
19925  */
19926
19927 /**
19928  * @class Roo.bootstrap.TimeField
19929  * @extends Roo.bootstrap.Input
19930  * Bootstrap DateField class
19931  * 
19932  * 
19933  * @constructor
19934  * Create a new TimeField
19935  * @param {Object} config The config object
19936  */
19937
19938 Roo.bootstrap.TimeField = function(config){
19939     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19940     this.addEvents({
19941             /**
19942              * @event show
19943              * Fires when this field show.
19944              * @param {Roo.bootstrap.DateField} thisthis
19945              * @param {Mixed} date The date value
19946              */
19947             show : true,
19948             /**
19949              * @event show
19950              * Fires when this field hide.
19951              * @param {Roo.bootstrap.DateField} this
19952              * @param {Mixed} date The date value
19953              */
19954             hide : true,
19955             /**
19956              * @event select
19957              * Fires when select a date.
19958              * @param {Roo.bootstrap.DateField} this
19959              * @param {Mixed} date The date value
19960              */
19961             select : true
19962         });
19963 };
19964
19965 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19966     
19967     /**
19968      * @cfg {String} format
19969      * The default time format string which can be overriden for localization support.  The format must be
19970      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19971      */
19972     format : "H:i",
19973        
19974     onRender: function(ct, position)
19975     {
19976         
19977         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19978                 
19979         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19980         
19981         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19982         
19983         this.pop = this.picker().select('>.datepicker-time',true).first();
19984         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19985         
19986         this.picker().on('mousedown', this.onMousedown, this);
19987         this.picker().on('click', this.onClick, this);
19988         
19989         this.picker().addClass('datepicker-dropdown');
19990     
19991         this.fillTime();
19992         this.update();
19993             
19994         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19995         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19996         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19997         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19998         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19999         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20000
20001     },
20002     
20003     fireKey: function(e){
20004         if (!this.picker().isVisible()){
20005             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20006                 this.show();
20007             }
20008             return;
20009         }
20010
20011         e.preventDefault();
20012         
20013         switch(e.keyCode){
20014             case 27: // escape
20015                 this.hide();
20016                 break;
20017             case 37: // left
20018             case 39: // right
20019                 this.onTogglePeriod();
20020                 break;
20021             case 38: // up
20022                 this.onIncrementMinutes();
20023                 break;
20024             case 40: // down
20025                 this.onDecrementMinutes();
20026                 break;
20027             case 13: // enter
20028             case 9: // tab
20029                 this.setTime();
20030                 break;
20031         }
20032     },
20033     
20034     onClick: function(e) {
20035         e.stopPropagation();
20036         e.preventDefault();
20037     },
20038     
20039     picker : function()
20040     {
20041         return this.el.select('.datepicker', true).first();
20042     },
20043     
20044     fillTime: function()
20045     {    
20046         var time = this.pop.select('tbody', true).first();
20047         
20048         time.dom.innerHTML = '';
20049         
20050         time.createChild({
20051             tag: 'tr',
20052             cn: [
20053                 {
20054                     tag: 'td',
20055                     cn: [
20056                         {
20057                             tag: 'a',
20058                             href: '#',
20059                             cls: 'btn',
20060                             cn: [
20061                                 {
20062                                     tag: 'span',
20063                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20064                                 }
20065                             ]
20066                         } 
20067                     ]
20068                 },
20069                 {
20070                     tag: 'td',
20071                     cls: 'separator'
20072                 },
20073                 {
20074                     tag: 'td',
20075                     cn: [
20076                         {
20077                             tag: 'a',
20078                             href: '#',
20079                             cls: 'btn',
20080                             cn: [
20081                                 {
20082                                     tag: 'span',
20083                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20084                                 }
20085                             ]
20086                         }
20087                     ]
20088                 },
20089                 {
20090                     tag: 'td',
20091                     cls: 'separator'
20092                 }
20093             ]
20094         });
20095         
20096         time.createChild({
20097             tag: 'tr',
20098             cn: [
20099                 {
20100                     tag: 'td',
20101                     cn: [
20102                         {
20103                             tag: 'span',
20104                             cls: 'timepicker-hour',
20105                             html: '00'
20106                         }  
20107                     ]
20108                 },
20109                 {
20110                     tag: 'td',
20111                     cls: 'separator',
20112                     html: ':'
20113                 },
20114                 {
20115                     tag: 'td',
20116                     cn: [
20117                         {
20118                             tag: 'span',
20119                             cls: 'timepicker-minute',
20120                             html: '00'
20121                         }  
20122                     ]
20123                 },
20124                 {
20125                     tag: 'td',
20126                     cls: 'separator'
20127                 },
20128                 {
20129                     tag: 'td',
20130                     cn: [
20131                         {
20132                             tag: 'button',
20133                             type: 'button',
20134                             cls: 'btn btn-primary period',
20135                             html: 'AM'
20136                             
20137                         }
20138                     ]
20139                 }
20140             ]
20141         });
20142         
20143         time.createChild({
20144             tag: 'tr',
20145             cn: [
20146                 {
20147                     tag: 'td',
20148                     cn: [
20149                         {
20150                             tag: 'a',
20151                             href: '#',
20152                             cls: 'btn',
20153                             cn: [
20154                                 {
20155                                     tag: 'span',
20156                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20157                                 }
20158                             ]
20159                         }
20160                     ]
20161                 },
20162                 {
20163                     tag: 'td',
20164                     cls: 'separator'
20165                 },
20166                 {
20167                     tag: 'td',
20168                     cn: [
20169                         {
20170                             tag: 'a',
20171                             href: '#',
20172                             cls: 'btn',
20173                             cn: [
20174                                 {
20175                                     tag: 'span',
20176                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20177                                 }
20178                             ]
20179                         }
20180                     ]
20181                 },
20182                 {
20183                     tag: 'td',
20184                     cls: 'separator'
20185                 }
20186             ]
20187         });
20188         
20189     },
20190     
20191     update: function()
20192     {
20193         
20194         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20195         
20196         this.fill();
20197     },
20198     
20199     fill: function() 
20200     {
20201         var hours = this.time.getHours();
20202         var minutes = this.time.getMinutes();
20203         var period = 'AM';
20204         
20205         if(hours > 11){
20206             period = 'PM';
20207         }
20208         
20209         if(hours == 0){
20210             hours = 12;
20211         }
20212         
20213         
20214         if(hours > 12){
20215             hours = hours - 12;
20216         }
20217         
20218         if(hours < 10){
20219             hours = '0' + hours;
20220         }
20221         
20222         if(minutes < 10){
20223             minutes = '0' + minutes;
20224         }
20225         
20226         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20227         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20228         this.pop.select('button', true).first().dom.innerHTML = period;
20229         
20230     },
20231     
20232     place: function()
20233     {   
20234         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20235         
20236         var cls = ['bottom'];
20237         
20238         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20239             cls.pop();
20240             cls.push('top');
20241         }
20242         
20243         cls.push('right');
20244         
20245         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20246             cls.pop();
20247             cls.push('left');
20248         }
20249         
20250         this.picker().addClass(cls.join('-'));
20251         
20252         var _this = this;
20253         
20254         Roo.each(cls, function(c){
20255             if(c == 'bottom'){
20256                 _this.picker().setTop(_this.inputEl().getHeight());
20257                 return;
20258             }
20259             if(c == 'top'){
20260                 _this.picker().setTop(0 - _this.picker().getHeight());
20261                 return;
20262             }
20263             
20264             if(c == 'left'){
20265                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20266                 return;
20267             }
20268             if(c == 'right'){
20269                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20270                 return;
20271             }
20272         });
20273         
20274     },
20275   
20276     onFocus : function()
20277     {
20278         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20279         this.show();
20280     },
20281     
20282     onBlur : function()
20283     {
20284         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20285         this.hide();
20286     },
20287     
20288     show : function()
20289     {
20290         this.picker().show();
20291         this.pop.show();
20292         this.update();
20293         this.place();
20294         
20295         this.fireEvent('show', this, this.date);
20296     },
20297     
20298     hide : function()
20299     {
20300         this.picker().hide();
20301         this.pop.hide();
20302         
20303         this.fireEvent('hide', this, this.date);
20304     },
20305     
20306     setTime : function()
20307     {
20308         this.hide();
20309         this.setValue(this.time.format(this.format));
20310         
20311         this.fireEvent('select', this, this.date);
20312         
20313         
20314     },
20315     
20316     onMousedown: function(e){
20317         e.stopPropagation();
20318         e.preventDefault();
20319     },
20320     
20321     onIncrementHours: function()
20322     {
20323         Roo.log('onIncrementHours');
20324         this.time = this.time.add(Date.HOUR, 1);
20325         this.update();
20326         
20327     },
20328     
20329     onDecrementHours: function()
20330     {
20331         Roo.log('onDecrementHours');
20332         this.time = this.time.add(Date.HOUR, -1);
20333         this.update();
20334     },
20335     
20336     onIncrementMinutes: function()
20337     {
20338         Roo.log('onIncrementMinutes');
20339         this.time = this.time.add(Date.MINUTE, 1);
20340         this.update();
20341     },
20342     
20343     onDecrementMinutes: function()
20344     {
20345         Roo.log('onDecrementMinutes');
20346         this.time = this.time.add(Date.MINUTE, -1);
20347         this.update();
20348     },
20349     
20350     onTogglePeriod: function()
20351     {
20352         Roo.log('onTogglePeriod');
20353         this.time = this.time.add(Date.HOUR, 12);
20354         this.update();
20355     }
20356     
20357    
20358 });
20359
20360 Roo.apply(Roo.bootstrap.TimeField,  {
20361     
20362     content : {
20363         tag: 'tbody',
20364         cn: [
20365             {
20366                 tag: 'tr',
20367                 cn: [
20368                 {
20369                     tag: 'td',
20370                     colspan: '7'
20371                 }
20372                 ]
20373             }
20374         ]
20375     },
20376     
20377     footer : {
20378         tag: 'tfoot',
20379         cn: [
20380             {
20381                 tag: 'tr',
20382                 cn: [
20383                 {
20384                     tag: 'th',
20385                     colspan: '7',
20386                     cls: '',
20387                     cn: [
20388                         {
20389                             tag: 'button',
20390                             cls: 'btn btn-info ok',
20391                             html: 'OK'
20392                         }
20393                     ]
20394                 }
20395
20396                 ]
20397             }
20398         ]
20399     }
20400 });
20401
20402 Roo.apply(Roo.bootstrap.TimeField,  {
20403   
20404     template : {
20405         tag: 'div',
20406         cls: 'datepicker dropdown-menu',
20407         cn: [
20408             {
20409                 tag: 'div',
20410                 cls: 'datepicker-time',
20411                 cn: [
20412                 {
20413                     tag: 'table',
20414                     cls: 'table-condensed',
20415                     cn:[
20416                     Roo.bootstrap.TimeField.content,
20417                     Roo.bootstrap.TimeField.footer
20418                     ]
20419                 }
20420                 ]
20421             }
20422         ]
20423     }
20424 });
20425
20426  
20427
20428  /*
20429  * - LGPL
20430  *
20431  * MonthField
20432  * 
20433  */
20434
20435 /**
20436  * @class Roo.bootstrap.MonthField
20437  * @extends Roo.bootstrap.Input
20438  * Bootstrap MonthField class
20439  * 
20440  * @cfg {String} language default en
20441  * 
20442  * @constructor
20443  * Create a new MonthField
20444  * @param {Object} config The config object
20445  */
20446
20447 Roo.bootstrap.MonthField = function(config){
20448     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20449     
20450     this.addEvents({
20451         /**
20452          * @event show
20453          * Fires when this field show.
20454          * @param {Roo.bootstrap.MonthField} this
20455          * @param {Mixed} date The date value
20456          */
20457         show : true,
20458         /**
20459          * @event show
20460          * Fires when this field hide.
20461          * @param {Roo.bootstrap.MonthField} this
20462          * @param {Mixed} date The date value
20463          */
20464         hide : true,
20465         /**
20466          * @event select
20467          * Fires when select a date.
20468          * @param {Roo.bootstrap.MonthField} this
20469          * @param {String} oldvalue The old value
20470          * @param {String} newvalue The new value
20471          */
20472         select : true
20473     });
20474 };
20475
20476 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20477     
20478     onRender: function(ct, position)
20479     {
20480         
20481         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20482         
20483         this.language = this.language || 'en';
20484         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20485         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20486         
20487         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20488         this.isInline = false;
20489         this.isInput = true;
20490         this.component = this.el.select('.add-on', true).first() || false;
20491         this.component = (this.component && this.component.length === 0) ? false : this.component;
20492         this.hasInput = this.component && this.inputEL().length;
20493         
20494         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20495         
20496         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20497         
20498         this.picker().on('mousedown', this.onMousedown, this);
20499         this.picker().on('click', this.onClick, this);
20500         
20501         this.picker().addClass('datepicker-dropdown');
20502         
20503         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20504             v.setStyle('width', '189px');
20505         });
20506         
20507         this.fillMonths();
20508         
20509         this.update();
20510         
20511         if(this.isInline) {
20512             this.show();
20513         }
20514         
20515     },
20516     
20517     setValue: function(v, suppressEvent)
20518     {   
20519         var o = this.getValue();
20520         
20521         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20522         
20523         this.update();
20524
20525         if(suppressEvent !== true){
20526             this.fireEvent('select', this, o, v);
20527         }
20528         
20529     },
20530     
20531     getValue: function()
20532     {
20533         return this.value;
20534     },
20535     
20536     onClick: function(e) 
20537     {
20538         e.stopPropagation();
20539         e.preventDefault();
20540         
20541         var target = e.getTarget();
20542         
20543         if(target.nodeName.toLowerCase() === 'i'){
20544             target = Roo.get(target).dom.parentNode;
20545         }
20546         
20547         var nodeName = target.nodeName;
20548         var className = target.className;
20549         var html = target.innerHTML;
20550         
20551         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20552             return;
20553         }
20554         
20555         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20556         
20557         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20558         
20559         this.hide();
20560                         
20561     },
20562     
20563     picker : function()
20564     {
20565         return this.pickerEl;
20566     },
20567     
20568     fillMonths: function()
20569     {    
20570         var i = 0;
20571         var months = this.picker().select('>.datepicker-months td', true).first();
20572         
20573         months.dom.innerHTML = '';
20574         
20575         while (i < 12) {
20576             var month = {
20577                 tag: 'span',
20578                 cls: 'month',
20579                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20580             };
20581             
20582             months.createChild(month);
20583         }
20584         
20585     },
20586     
20587     update: function()
20588     {
20589         var _this = this;
20590         
20591         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20592             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20593         }
20594         
20595         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20596             e.removeClass('active');
20597             
20598             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20599                 e.addClass('active');
20600             }
20601         })
20602     },
20603     
20604     place: function()
20605     {
20606         if(this.isInline) {
20607             return;
20608         }
20609         
20610         this.picker().removeClass(['bottom', 'top']);
20611         
20612         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20613             /*
20614              * place to the top of element!
20615              *
20616              */
20617             
20618             this.picker().addClass('top');
20619             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20620             
20621             return;
20622         }
20623         
20624         this.picker().addClass('bottom');
20625         
20626         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20627     },
20628     
20629     onFocus : function()
20630     {
20631         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20632         this.show();
20633     },
20634     
20635     onBlur : function()
20636     {
20637         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20638         
20639         var d = this.inputEl().getValue();
20640         
20641         this.setValue(d);
20642                 
20643         this.hide();
20644     },
20645     
20646     show : function()
20647     {
20648         this.picker().show();
20649         this.picker().select('>.datepicker-months', true).first().show();
20650         this.update();
20651         this.place();
20652         
20653         this.fireEvent('show', this, this.date);
20654     },
20655     
20656     hide : function()
20657     {
20658         if(this.isInline) {
20659             return;
20660         }
20661         this.picker().hide();
20662         this.fireEvent('hide', this, this.date);
20663         
20664     },
20665     
20666     onMousedown: function(e)
20667     {
20668         e.stopPropagation();
20669         e.preventDefault();
20670     },
20671     
20672     keyup: function(e)
20673     {
20674         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20675         this.update();
20676     },
20677
20678     fireKey: function(e)
20679     {
20680         if (!this.picker().isVisible()){
20681             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20682                 this.show();
20683             }
20684             return;
20685         }
20686         
20687         var dir;
20688         
20689         switch(e.keyCode){
20690             case 27: // escape
20691                 this.hide();
20692                 e.preventDefault();
20693                 break;
20694             case 37: // left
20695             case 39: // right
20696                 dir = e.keyCode == 37 ? -1 : 1;
20697                 
20698                 this.vIndex = this.vIndex + dir;
20699                 
20700                 if(this.vIndex < 0){
20701                     this.vIndex = 0;
20702                 }
20703                 
20704                 if(this.vIndex > 11){
20705                     this.vIndex = 11;
20706                 }
20707                 
20708                 if(isNaN(this.vIndex)){
20709                     this.vIndex = 0;
20710                 }
20711                 
20712                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20713                 
20714                 break;
20715             case 38: // up
20716             case 40: // down
20717                 
20718                 dir = e.keyCode == 38 ? -1 : 1;
20719                 
20720                 this.vIndex = this.vIndex + dir * 4;
20721                 
20722                 if(this.vIndex < 0){
20723                     this.vIndex = 0;
20724                 }
20725                 
20726                 if(this.vIndex > 11){
20727                     this.vIndex = 11;
20728                 }
20729                 
20730                 if(isNaN(this.vIndex)){
20731                     this.vIndex = 0;
20732                 }
20733                 
20734                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20735                 break;
20736                 
20737             case 13: // enter
20738                 
20739                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20740                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20741                 }
20742                 
20743                 this.hide();
20744                 e.preventDefault();
20745                 break;
20746             case 9: // tab
20747                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20748                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20749                 }
20750                 this.hide();
20751                 break;
20752             case 16: // shift
20753             case 17: // ctrl
20754             case 18: // alt
20755                 break;
20756             default :
20757                 this.hide();
20758                 
20759         }
20760     },
20761     
20762     remove: function() 
20763     {
20764         this.picker().remove();
20765     }
20766    
20767 });
20768
20769 Roo.apply(Roo.bootstrap.MonthField,  {
20770     
20771     content : {
20772         tag: 'tbody',
20773         cn: [
20774         {
20775             tag: 'tr',
20776             cn: [
20777             {
20778                 tag: 'td',
20779                 colspan: '7'
20780             }
20781             ]
20782         }
20783         ]
20784     },
20785     
20786     dates:{
20787         en: {
20788             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20789             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20790         }
20791     }
20792 });
20793
20794 Roo.apply(Roo.bootstrap.MonthField,  {
20795   
20796     template : {
20797         tag: 'div',
20798         cls: 'datepicker dropdown-menu roo-dynamic',
20799         cn: [
20800             {
20801                 tag: 'div',
20802                 cls: 'datepicker-months',
20803                 cn: [
20804                 {
20805                     tag: 'table',
20806                     cls: 'table-condensed',
20807                     cn:[
20808                         Roo.bootstrap.DateField.content
20809                     ]
20810                 }
20811                 ]
20812             }
20813         ]
20814     }
20815 });
20816
20817  
20818
20819  
20820  /*
20821  * - LGPL
20822  *
20823  * CheckBox
20824  * 
20825  */
20826
20827 /**
20828  * @class Roo.bootstrap.CheckBox
20829  * @extends Roo.bootstrap.Input
20830  * Bootstrap CheckBox class
20831  * 
20832  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20833  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20834  * @cfg {String} boxLabel The text that appears beside the checkbox
20835  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20836  * @cfg {Boolean} checked initnal the element
20837  * @cfg {Boolean} inline inline the element (default false)
20838  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20839  * @cfg {String} tooltip label tooltip
20840  * 
20841  * @constructor
20842  * Create a new CheckBox
20843  * @param {Object} config The config object
20844  */
20845
20846 Roo.bootstrap.CheckBox = function(config){
20847     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20848    
20849     this.addEvents({
20850         /**
20851         * @event check
20852         * Fires when the element is checked or unchecked.
20853         * @param {Roo.bootstrap.CheckBox} this This input
20854         * @param {Boolean} checked The new checked value
20855         */
20856        check : true,
20857        /**
20858         * @event click
20859         * Fires when the element is click.
20860         * @param {Roo.bootstrap.CheckBox} this This input
20861         */
20862        click : true
20863     });
20864     
20865 };
20866
20867 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20868   
20869     inputType: 'checkbox',
20870     inputValue: 1,
20871     valueOff: 0,
20872     boxLabel: false,
20873     checked: false,
20874     weight : false,
20875     inline: false,
20876     tooltip : '',
20877     
20878     getAutoCreate : function()
20879     {
20880         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20881         
20882         var id = Roo.id();
20883         
20884         var cfg = {};
20885         
20886         cfg.cls = 'form-group ' + this.inputType; //input-group
20887         
20888         if(this.inline){
20889             cfg.cls += ' ' + this.inputType + '-inline';
20890         }
20891         
20892         var input =  {
20893             tag: 'input',
20894             id : id,
20895             type : this.inputType,
20896             value : this.inputValue,
20897             cls : 'roo-' + this.inputType, //'form-box',
20898             placeholder : this.placeholder || ''
20899             
20900         };
20901         
20902         if(this.inputType != 'radio'){
20903             var hidden =  {
20904                 tag: 'input',
20905                 type : 'hidden',
20906                 cls : 'roo-hidden-value',
20907                 value : this.checked ? this.inputValue : this.valueOff
20908             };
20909         }
20910         
20911             
20912         if (this.weight) { // Validity check?
20913             cfg.cls += " " + this.inputType + "-" + this.weight;
20914         }
20915         
20916         if (this.disabled) {
20917             input.disabled=true;
20918         }
20919         
20920         if(this.checked){
20921             input.checked = this.checked;
20922         }
20923         
20924         if (this.name) {
20925             
20926             input.name = this.name;
20927             
20928             if(this.inputType != 'radio'){
20929                 hidden.name = this.name;
20930                 input.name = '_hidden_' + this.name;
20931             }
20932         }
20933         
20934         if (this.size) {
20935             input.cls += ' input-' + this.size;
20936         }
20937         
20938         var settings=this;
20939         
20940         ['xs','sm','md','lg'].map(function(size){
20941             if (settings[size]) {
20942                 cfg.cls += ' col-' + size + '-' + settings[size];
20943             }
20944         });
20945         
20946         var inputblock = input;
20947          
20948         if (this.before || this.after) {
20949             
20950             inputblock = {
20951                 cls : 'input-group',
20952                 cn :  [] 
20953             };
20954             
20955             if (this.before) {
20956                 inputblock.cn.push({
20957                     tag :'span',
20958                     cls : 'input-group-addon',
20959                     html : this.before
20960                 });
20961             }
20962             
20963             inputblock.cn.push(input);
20964             
20965             if(this.inputType != 'radio'){
20966                 inputblock.cn.push(hidden);
20967             }
20968             
20969             if (this.after) {
20970                 inputblock.cn.push({
20971                     tag :'span',
20972                     cls : 'input-group-addon',
20973                     html : this.after
20974                 });
20975             }
20976             
20977         }
20978         
20979         if (align ==='left' && this.fieldLabel.length) {
20980 //                Roo.log("left and has label");
20981             cfg.cn = [
20982                 {
20983                     tag: 'label',
20984                     'for' :  id,
20985                     cls : 'control-label',
20986                     html : this.fieldLabel
20987                 },
20988                 {
20989                     cls : "", 
20990                     cn: [
20991                         inputblock
20992                     ]
20993                 }
20994             ];
20995             
20996             if(this.labelWidth > 12){
20997                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20998             }
20999             
21000             if(this.labelWidth < 13 && this.labelmd == 0){
21001                 this.labelmd = this.labelWidth;
21002             }
21003             
21004             if(this.labellg > 0){
21005                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21006                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21007             }
21008             
21009             if(this.labelmd > 0){
21010                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21011                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21012             }
21013             
21014             if(this.labelsm > 0){
21015                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21016                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21017             }
21018             
21019             if(this.labelxs > 0){
21020                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21021                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21022             }
21023             
21024         } else if ( this.fieldLabel.length) {
21025 //                Roo.log(" label");
21026                 cfg.cn = [
21027                    
21028                     {
21029                         tag: this.boxLabel ? 'span' : 'label',
21030                         'for': id,
21031                         cls: 'control-label box-input-label',
21032                         //cls : 'input-group-addon',
21033                         html : this.fieldLabel
21034                     },
21035                     
21036                     inputblock
21037                     
21038                 ];
21039
21040         } else {
21041             
21042 //                Roo.log(" no label && no align");
21043                 cfg.cn = [  inputblock ] ;
21044                 
21045                 
21046         }
21047         
21048         if(this.boxLabel){
21049              var boxLabelCfg = {
21050                 tag: 'label',
21051                 //'for': id, // box label is handled by onclick - so no for...
21052                 cls: 'box-label',
21053                 html: this.boxLabel
21054             };
21055             
21056             if(this.tooltip){
21057                 boxLabelCfg.tooltip = this.tooltip;
21058             }
21059              
21060             cfg.cn.push(boxLabelCfg);
21061         }
21062         
21063         if(this.inputType != 'radio'){
21064             cfg.cn.push(hidden);
21065         }
21066         
21067         return cfg;
21068         
21069     },
21070     
21071     /**
21072      * return the real input element.
21073      */
21074     inputEl: function ()
21075     {
21076         return this.el.select('input.roo-' + this.inputType,true).first();
21077     },
21078     hiddenEl: function ()
21079     {
21080         return this.el.select('input.roo-hidden-value',true).first();
21081     },
21082     
21083     labelEl: function()
21084     {
21085         return this.el.select('label.control-label',true).first();
21086     },
21087     /* depricated... */
21088     
21089     label: function()
21090     {
21091         return this.labelEl();
21092     },
21093     
21094     boxLabelEl: function()
21095     {
21096         return this.el.select('label.box-label',true).first();
21097     },
21098     
21099     initEvents : function()
21100     {
21101 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21102         
21103         this.inputEl().on('click', this.onClick,  this);
21104         
21105         if (this.boxLabel) { 
21106             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21107         }
21108         
21109         this.startValue = this.getValue();
21110         
21111         if(this.groupId){
21112             Roo.bootstrap.CheckBox.register(this);
21113         }
21114     },
21115     
21116     onClick : function(e)
21117     {   
21118         if(this.fireEvent('click', this, e) !== false){
21119             this.setChecked(!this.checked);
21120         }
21121         
21122     },
21123     
21124     setChecked : function(state,suppressEvent)
21125     {
21126         this.startValue = this.getValue();
21127
21128         if(this.inputType == 'radio'){
21129             
21130             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21131                 e.dom.checked = false;
21132             });
21133             
21134             this.inputEl().dom.checked = true;
21135             
21136             this.inputEl().dom.value = this.inputValue;
21137             
21138             if(suppressEvent !== true){
21139                 this.fireEvent('check', this, true);
21140             }
21141             
21142             this.validate();
21143             
21144             return;
21145         }
21146         
21147         this.checked = state;
21148         
21149         this.inputEl().dom.checked = state;
21150         
21151         
21152         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21153         
21154         if(suppressEvent !== true){
21155             this.fireEvent('check', this, state);
21156         }
21157         
21158         this.validate();
21159     },
21160     
21161     getValue : function()
21162     {
21163         if(this.inputType == 'radio'){
21164             return this.getGroupValue();
21165         }
21166         
21167         return this.hiddenEl().dom.value;
21168         
21169     },
21170     
21171     getGroupValue : function()
21172     {
21173         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21174             return '';
21175         }
21176         
21177         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21178     },
21179     
21180     setValue : function(v,suppressEvent)
21181     {
21182         if(this.inputType == 'radio'){
21183             this.setGroupValue(v, suppressEvent);
21184             return;
21185         }
21186         
21187         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21188         
21189         this.validate();
21190     },
21191     
21192     setGroupValue : function(v, suppressEvent)
21193     {
21194         this.startValue = this.getValue();
21195         
21196         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21197             e.dom.checked = false;
21198             
21199             if(e.dom.value == v){
21200                 e.dom.checked = true;
21201             }
21202         });
21203         
21204         if(suppressEvent !== true){
21205             this.fireEvent('check', this, true);
21206         }
21207
21208         this.validate();
21209         
21210         return;
21211     },
21212     
21213     validate : function()
21214     {
21215         if(this.getVisibilityEl().hasClass('hidden')){
21216             return true;
21217         }
21218         
21219         if(
21220                 this.disabled || 
21221                 (this.inputType == 'radio' && this.validateRadio()) ||
21222                 (this.inputType == 'checkbox' && this.validateCheckbox())
21223         ){
21224             this.markValid();
21225             return true;
21226         }
21227         
21228         this.markInvalid();
21229         return false;
21230     },
21231     
21232     validateRadio : function()
21233     {
21234         if(this.getVisibilityEl().hasClass('hidden')){
21235             return true;
21236         }
21237         
21238         if(this.allowBlank){
21239             return true;
21240         }
21241         
21242         var valid = false;
21243         
21244         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21245             if(!e.dom.checked){
21246                 return;
21247             }
21248             
21249             valid = true;
21250             
21251             return false;
21252         });
21253         
21254         return valid;
21255     },
21256     
21257     validateCheckbox : function()
21258     {
21259         if(!this.groupId){
21260             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21261             //return (this.getValue() == this.inputValue) ? true : false;
21262         }
21263         
21264         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21265         
21266         if(!group){
21267             return false;
21268         }
21269         
21270         var r = false;
21271         
21272         for(var i in group){
21273             if(group[i].el.isVisible(true)){
21274                 r = false;
21275                 break;
21276             }
21277             
21278             r = true;
21279         }
21280         
21281         for(var i in group){
21282             if(r){
21283                 break;
21284             }
21285             
21286             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21287         }
21288         
21289         return r;
21290     },
21291     
21292     /**
21293      * Mark this field as valid
21294      */
21295     markValid : function()
21296     {
21297         var _this = this;
21298         
21299         this.fireEvent('valid', this);
21300         
21301         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21302         
21303         if(this.groupId){
21304             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21305         }
21306         
21307         if(label){
21308             label.markValid();
21309         }
21310
21311         if(this.inputType == 'radio'){
21312             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21313                 var fg = e.findParent('.form-group', false, true);
21314                 if (Roo.bootstrap.version == 3) {
21315                     fg.removeClass([_this.invalidClass, _this.validClass]);
21316                     fg.addClass(_this.validClass);
21317                 } else {
21318                     fg.removeClass(['is-valid', 'is-invalid']);
21319                     fg.addClass('is-valid');
21320                 }
21321             });
21322             
21323             return;
21324         }
21325
21326         if(!this.groupId){
21327             var fg = this.el.findParent('.form-group', false, true);
21328             if (Roo.bootstrap.version == 3) {
21329                 fg.removeClass([this.invalidClass, this.validClass]);
21330                 fg.addClass(this.validClass);
21331             } else {
21332                 fg.removeClass(['is-valid', 'is-invalid']);
21333                 fg.addClass('is-valid');
21334             }
21335             return;
21336         }
21337         
21338         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21339         
21340         if(!group){
21341             return;
21342         }
21343         
21344         for(var i in group){
21345             var fg = group[i].el.findParent('.form-group', false, true);
21346             if (Roo.bootstrap.version == 3) {
21347                 fg.removeClass([this.invalidClass, this.validClass]);
21348                 fg.addClass(this.validClass);
21349             } else {
21350                 fg.removeClass(['is-valid', 'is-invalid']);
21351                 fg.addClass('is-valid');
21352             }
21353         }
21354     },
21355     
21356      /**
21357      * Mark this field as invalid
21358      * @param {String} msg The validation message
21359      */
21360     markInvalid : function(msg)
21361     {
21362         if(this.allowBlank){
21363             return;
21364         }
21365         
21366         var _this = this;
21367         
21368         this.fireEvent('invalid', this, msg);
21369         
21370         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21371         
21372         if(this.groupId){
21373             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21374         }
21375         
21376         if(label){
21377             label.markInvalid();
21378         }
21379             
21380         if(this.inputType == 'radio'){
21381             
21382             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21383                 var fg = e.findParent('.form-group', false, true);
21384                 if (Roo.bootstrap.version == 3) {
21385                     fg.removeClass([_this.invalidClass, _this.validClass]);
21386                     fg.addClass(_this.invalidClass);
21387                 } else {
21388                     fg.removeClass(['is-invalid', 'is-valid']);
21389                     fg.addClass('is-invalid');
21390                 }
21391             });
21392             
21393             return;
21394         }
21395         
21396         if(!this.groupId){
21397             var fg = this.el.findParent('.form-group', false, true);
21398             if (Roo.bootstrap.version == 3) {
21399                 fg.removeClass([_this.invalidClass, _this.validClass]);
21400                 fg.addClass(_this.invalidClass);
21401             } else {
21402                 fg.removeClass(['is-invalid', 'is-valid']);
21403                 fg.addClass('is-invalid');
21404             }
21405             return;
21406         }
21407         
21408         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21409         
21410         if(!group){
21411             return;
21412         }
21413         
21414         for(var i in group){
21415             var fg = group[i].el.findParent('.form-group', false, true);
21416             if (Roo.bootstrap.version == 3) {
21417                 fg.removeClass([_this.invalidClass, _this.validClass]);
21418                 fg.addClass(_this.invalidClass);
21419             } else {
21420                 fg.removeClass(['is-invalid', 'is-valid']);
21421                 fg.addClass('is-invalid');
21422             }
21423         }
21424         
21425     },
21426     
21427     clearInvalid : function()
21428     {
21429         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21430         
21431         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21432         
21433         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21434         
21435         if (label && label.iconEl) {
21436             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21437             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21438         }
21439     },
21440     
21441     disable : function()
21442     {
21443         if(this.inputType != 'radio'){
21444             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21445             return;
21446         }
21447         
21448         var _this = this;
21449         
21450         if(this.rendered){
21451             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21452                 _this.getActionEl().addClass(this.disabledClass);
21453                 e.dom.disabled = true;
21454             });
21455         }
21456         
21457         this.disabled = true;
21458         this.fireEvent("disable", this);
21459         return this;
21460     },
21461
21462     enable : function()
21463     {
21464         if(this.inputType != 'radio'){
21465             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21466             return;
21467         }
21468         
21469         var _this = this;
21470         
21471         if(this.rendered){
21472             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21473                 _this.getActionEl().removeClass(this.disabledClass);
21474                 e.dom.disabled = false;
21475             });
21476         }
21477         
21478         this.disabled = false;
21479         this.fireEvent("enable", this);
21480         return this;
21481     },
21482     
21483     setBoxLabel : function(v)
21484     {
21485         this.boxLabel = v;
21486         
21487         if(this.rendered){
21488             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21489         }
21490     }
21491
21492 });
21493
21494 Roo.apply(Roo.bootstrap.CheckBox, {
21495     
21496     groups: {},
21497     
21498      /**
21499     * register a CheckBox Group
21500     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21501     */
21502     register : function(checkbox)
21503     {
21504         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21505             this.groups[checkbox.groupId] = {};
21506         }
21507         
21508         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21509             return;
21510         }
21511         
21512         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21513         
21514     },
21515     /**
21516     * fetch a CheckBox Group based on the group ID
21517     * @param {string} the group ID
21518     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21519     */
21520     get: function(groupId) {
21521         if (typeof(this.groups[groupId]) == 'undefined') {
21522             return false;
21523         }
21524         
21525         return this.groups[groupId] ;
21526     }
21527     
21528     
21529 });
21530 /*
21531  * - LGPL
21532  *
21533  * RadioItem
21534  * 
21535  */
21536
21537 /**
21538  * @class Roo.bootstrap.Radio
21539  * @extends Roo.bootstrap.Component
21540  * Bootstrap Radio class
21541  * @cfg {String} boxLabel - the label associated
21542  * @cfg {String} value - the value of radio
21543  * 
21544  * @constructor
21545  * Create a new Radio
21546  * @param {Object} config The config object
21547  */
21548 Roo.bootstrap.Radio = function(config){
21549     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21550     
21551 };
21552
21553 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21554     
21555     boxLabel : '',
21556     
21557     value : '',
21558     
21559     getAutoCreate : function()
21560     {
21561         var cfg = {
21562             tag : 'div',
21563             cls : 'form-group radio',
21564             cn : [
21565                 {
21566                     tag : 'label',
21567                     cls : 'box-label',
21568                     html : this.boxLabel
21569                 }
21570             ]
21571         };
21572         
21573         return cfg;
21574     },
21575     
21576     initEvents : function() 
21577     {
21578         this.parent().register(this);
21579         
21580         this.el.on('click', this.onClick, this);
21581         
21582     },
21583     
21584     onClick : function(e)
21585     {
21586         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21587             this.setChecked(true);
21588         }
21589     },
21590     
21591     setChecked : function(state, suppressEvent)
21592     {
21593         this.parent().setValue(this.value, suppressEvent);
21594         
21595     },
21596     
21597     setBoxLabel : function(v)
21598     {
21599         this.boxLabel = v;
21600         
21601         if(this.rendered){
21602             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21603         }
21604     }
21605     
21606 });
21607  
21608
21609  /*
21610  * - LGPL
21611  *
21612  * Input
21613  * 
21614  */
21615
21616 /**
21617  * @class Roo.bootstrap.SecurePass
21618  * @extends Roo.bootstrap.Input
21619  * Bootstrap SecurePass class
21620  *
21621  * 
21622  * @constructor
21623  * Create a new SecurePass
21624  * @param {Object} config The config object
21625  */
21626  
21627 Roo.bootstrap.SecurePass = function (config) {
21628     // these go here, so the translation tool can replace them..
21629     this.errors = {
21630         PwdEmpty: "Please type a password, and then retype it to confirm.",
21631         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21632         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21633         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21634         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21635         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21636         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21637         TooWeak: "Your password is Too Weak."
21638     },
21639     this.meterLabel = "Password strength:";
21640     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21641     this.meterClass = [
21642         "roo-password-meter-tooweak", 
21643         "roo-password-meter-weak", 
21644         "roo-password-meter-medium", 
21645         "roo-password-meter-strong", 
21646         "roo-password-meter-grey"
21647     ];
21648     
21649     this.errors = {};
21650     
21651     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21652 }
21653
21654 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21655     /**
21656      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21657      * {
21658      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21659      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21660      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21661      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21662      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21663      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21664      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21665      * })
21666      */
21667     // private
21668     
21669     meterWidth: 300,
21670     errorMsg :'',    
21671     errors: false,
21672     imageRoot: '/',
21673     /**
21674      * @cfg {String/Object} Label for the strength meter (defaults to
21675      * 'Password strength:')
21676      */
21677     // private
21678     meterLabel: '',
21679     /**
21680      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21681      * ['Weak', 'Medium', 'Strong'])
21682      */
21683     // private    
21684     pwdStrengths: false,    
21685     // private
21686     strength: 0,
21687     // private
21688     _lastPwd: null,
21689     // private
21690     kCapitalLetter: 0,
21691     kSmallLetter: 1,
21692     kDigit: 2,
21693     kPunctuation: 3,
21694     
21695     insecure: false,
21696     // private
21697     initEvents: function ()
21698     {
21699         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21700
21701         if (this.el.is('input[type=password]') && Roo.isSafari) {
21702             this.el.on('keydown', this.SafariOnKeyDown, this);
21703         }
21704
21705         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21706     },
21707     // private
21708     onRender: function (ct, position)
21709     {
21710         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21711         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21712         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21713
21714         this.trigger.createChild({
21715                    cn: [
21716                     {
21717                     //id: 'PwdMeter',
21718                     tag: 'div',
21719                     cls: 'roo-password-meter-grey col-xs-12',
21720                     style: {
21721                         //width: 0,
21722                         //width: this.meterWidth + 'px'                                                
21723                         }
21724                     },
21725                     {                            
21726                          cls: 'roo-password-meter-text'                          
21727                     }
21728                 ]            
21729         });
21730
21731          
21732         if (this.hideTrigger) {
21733             this.trigger.setDisplayed(false);
21734         }
21735         this.setSize(this.width || '', this.height || '');
21736     },
21737     // private
21738     onDestroy: function ()
21739     {
21740         if (this.trigger) {
21741             this.trigger.removeAllListeners();
21742             this.trigger.remove();
21743         }
21744         if (this.wrap) {
21745             this.wrap.remove();
21746         }
21747         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21748     },
21749     // private
21750     checkStrength: function ()
21751     {
21752         var pwd = this.inputEl().getValue();
21753         if (pwd == this._lastPwd) {
21754             return;
21755         }
21756
21757         var strength;
21758         if (this.ClientSideStrongPassword(pwd)) {
21759             strength = 3;
21760         } else if (this.ClientSideMediumPassword(pwd)) {
21761             strength = 2;
21762         } else if (this.ClientSideWeakPassword(pwd)) {
21763             strength = 1;
21764         } else {
21765             strength = 0;
21766         }
21767         
21768         Roo.log('strength1: ' + strength);
21769         
21770         //var pm = this.trigger.child('div/div/div').dom;
21771         var pm = this.trigger.child('div/div');
21772         pm.removeClass(this.meterClass);
21773         pm.addClass(this.meterClass[strength]);
21774                 
21775         
21776         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21777                 
21778         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21779         
21780         this._lastPwd = pwd;
21781     },
21782     reset: function ()
21783     {
21784         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21785         
21786         this._lastPwd = '';
21787         
21788         var pm = this.trigger.child('div/div');
21789         pm.removeClass(this.meterClass);
21790         pm.addClass('roo-password-meter-grey');        
21791         
21792         
21793         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21794         
21795         pt.innerHTML = '';
21796         this.inputEl().dom.type='password';
21797     },
21798     // private
21799     validateValue: function (value)
21800     {
21801         
21802         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21803             return false;
21804         }
21805         if (value.length == 0) {
21806             if (this.allowBlank) {
21807                 this.clearInvalid();
21808                 return true;
21809             }
21810
21811             this.markInvalid(this.errors.PwdEmpty);
21812             this.errorMsg = this.errors.PwdEmpty;
21813             return false;
21814         }
21815         
21816         if(this.insecure){
21817             return true;
21818         }
21819         
21820         if ('[\x21-\x7e]*'.match(value)) {
21821             this.markInvalid(this.errors.PwdBadChar);
21822             this.errorMsg = this.errors.PwdBadChar;
21823             return false;
21824         }
21825         if (value.length < 6) {
21826             this.markInvalid(this.errors.PwdShort);
21827             this.errorMsg = this.errors.PwdShort;
21828             return false;
21829         }
21830         if (value.length > 16) {
21831             this.markInvalid(this.errors.PwdLong);
21832             this.errorMsg = this.errors.PwdLong;
21833             return false;
21834         }
21835         var strength;
21836         if (this.ClientSideStrongPassword(value)) {
21837             strength = 3;
21838         } else if (this.ClientSideMediumPassword(value)) {
21839             strength = 2;
21840         } else if (this.ClientSideWeakPassword(value)) {
21841             strength = 1;
21842         } else {
21843             strength = 0;
21844         }
21845
21846         
21847         if (strength < 2) {
21848             //this.markInvalid(this.errors.TooWeak);
21849             this.errorMsg = this.errors.TooWeak;
21850             //return false;
21851         }
21852         
21853         
21854         console.log('strength2: ' + strength);
21855         
21856         //var pm = this.trigger.child('div/div/div').dom;
21857         
21858         var pm = this.trigger.child('div/div');
21859         pm.removeClass(this.meterClass);
21860         pm.addClass(this.meterClass[strength]);
21861                 
21862         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21863                 
21864         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21865         
21866         this.errorMsg = ''; 
21867         return true;
21868     },
21869     // private
21870     CharacterSetChecks: function (type)
21871     {
21872         this.type = type;
21873         this.fResult = false;
21874     },
21875     // private
21876     isctype: function (character, type)
21877     {
21878         switch (type) {  
21879             case this.kCapitalLetter:
21880                 if (character >= 'A' && character <= 'Z') {
21881                     return true;
21882                 }
21883                 break;
21884             
21885             case this.kSmallLetter:
21886                 if (character >= 'a' && character <= 'z') {
21887                     return true;
21888                 }
21889                 break;
21890             
21891             case this.kDigit:
21892                 if (character >= '0' && character <= '9') {
21893                     return true;
21894                 }
21895                 break;
21896             
21897             case this.kPunctuation:
21898                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21899                     return true;
21900                 }
21901                 break;
21902             
21903             default:
21904                 return false;
21905         }
21906
21907     },
21908     // private
21909     IsLongEnough: function (pwd, size)
21910     {
21911         return !(pwd == null || isNaN(size) || pwd.length < size);
21912     },
21913     // private
21914     SpansEnoughCharacterSets: function (word, nb)
21915     {
21916         if (!this.IsLongEnough(word, nb))
21917         {
21918             return false;
21919         }
21920
21921         var characterSetChecks = new Array(
21922             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21923             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21924         );
21925         
21926         for (var index = 0; index < word.length; ++index) {
21927             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21928                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21929                     characterSetChecks[nCharSet].fResult = true;
21930                     break;
21931                 }
21932             }
21933         }
21934
21935         var nCharSets = 0;
21936         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21937             if (characterSetChecks[nCharSet].fResult) {
21938                 ++nCharSets;
21939             }
21940         }
21941
21942         if (nCharSets < nb) {
21943             return false;
21944         }
21945         return true;
21946     },
21947     // private
21948     ClientSideStrongPassword: function (pwd)
21949     {
21950         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21951     },
21952     // private
21953     ClientSideMediumPassword: function (pwd)
21954     {
21955         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21956     },
21957     // private
21958     ClientSideWeakPassword: function (pwd)
21959     {
21960         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21961     }
21962           
21963 })//<script type="text/javascript">
21964
21965 /*
21966  * Based  Ext JS Library 1.1.1
21967  * Copyright(c) 2006-2007, Ext JS, LLC.
21968  * LGPL
21969  *
21970  */
21971  
21972 /**
21973  * @class Roo.HtmlEditorCore
21974  * @extends Roo.Component
21975  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21976  *
21977  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21978  */
21979
21980 Roo.HtmlEditorCore = function(config){
21981     
21982     
21983     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21984     
21985     
21986     this.addEvents({
21987         /**
21988          * @event initialize
21989          * Fires when the editor is fully initialized (including the iframe)
21990          * @param {Roo.HtmlEditorCore} this
21991          */
21992         initialize: true,
21993         /**
21994          * @event activate
21995          * Fires when the editor is first receives the focus. Any insertion must wait
21996          * until after this event.
21997          * @param {Roo.HtmlEditorCore} this
21998          */
21999         activate: true,
22000          /**
22001          * @event beforesync
22002          * Fires before the textarea is updated with content from the editor iframe. Return false
22003          * to cancel the sync.
22004          * @param {Roo.HtmlEditorCore} this
22005          * @param {String} html
22006          */
22007         beforesync: true,
22008          /**
22009          * @event beforepush
22010          * Fires before the iframe editor is updated with content from the textarea. Return false
22011          * to cancel the push.
22012          * @param {Roo.HtmlEditorCore} this
22013          * @param {String} html
22014          */
22015         beforepush: true,
22016          /**
22017          * @event sync
22018          * Fires when the textarea is updated with content from the editor iframe.
22019          * @param {Roo.HtmlEditorCore} this
22020          * @param {String} html
22021          */
22022         sync: true,
22023          /**
22024          * @event push
22025          * Fires when the iframe editor is updated with content from the textarea.
22026          * @param {Roo.HtmlEditorCore} this
22027          * @param {String} html
22028          */
22029         push: true,
22030         
22031         /**
22032          * @event editorevent
22033          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22034          * @param {Roo.HtmlEditorCore} this
22035          */
22036         editorevent: true
22037         
22038     });
22039     
22040     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22041     
22042     // defaults : white / black...
22043     this.applyBlacklists();
22044     
22045     
22046     
22047 };
22048
22049
22050 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22051
22052
22053      /**
22054      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22055      */
22056     
22057     owner : false,
22058     
22059      /**
22060      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22061      *                        Roo.resizable.
22062      */
22063     resizable : false,
22064      /**
22065      * @cfg {Number} height (in pixels)
22066      */   
22067     height: 300,
22068    /**
22069      * @cfg {Number} width (in pixels)
22070      */   
22071     width: 500,
22072     
22073     /**
22074      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22075      * 
22076      */
22077     stylesheets: false,
22078     
22079     // id of frame..
22080     frameId: false,
22081     
22082     // private properties
22083     validationEvent : false,
22084     deferHeight: true,
22085     initialized : false,
22086     activated : false,
22087     sourceEditMode : false,
22088     onFocus : Roo.emptyFn,
22089     iframePad:3,
22090     hideMode:'offsets',
22091     
22092     clearUp: true,
22093     
22094     // blacklist + whitelisted elements..
22095     black: false,
22096     white: false,
22097      
22098     bodyCls : '',
22099
22100     /**
22101      * Protected method that will not generally be called directly. It
22102      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22103      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22104      */
22105     getDocMarkup : function(){
22106         // body styles..
22107         var st = '';
22108         
22109         // inherit styels from page...?? 
22110         if (this.stylesheets === false) {
22111             
22112             Roo.get(document.head).select('style').each(function(node) {
22113                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22114             });
22115             
22116             Roo.get(document.head).select('link').each(function(node) { 
22117                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22118             });
22119             
22120         } else if (!this.stylesheets.length) {
22121                 // simple..
22122                 st = '<style type="text/css">' +
22123                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22124                    '</style>';
22125         } else { 
22126             st = '<style type="text/css">' +
22127                     this.stylesheets +
22128                 '</style>';
22129         }
22130         
22131         st +=  '<style type="text/css">' +
22132             'IMG { cursor: pointer } ' +
22133         '</style>';
22134
22135         var cls = 'roo-htmleditor-body';
22136         
22137         if(this.bodyCls.length){
22138             cls += ' ' + this.bodyCls;
22139         }
22140         
22141         return '<html><head>' + st  +
22142             //<style type="text/css">' +
22143             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22144             //'</style>' +
22145             ' </head><body class="' +  cls + '"></body></html>';
22146     },
22147
22148     // private
22149     onRender : function(ct, position)
22150     {
22151         var _t = this;
22152         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22153         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22154         
22155         
22156         this.el.dom.style.border = '0 none';
22157         this.el.dom.setAttribute('tabIndex', -1);
22158         this.el.addClass('x-hidden hide');
22159         
22160         
22161         
22162         if(Roo.isIE){ // fix IE 1px bogus margin
22163             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22164         }
22165        
22166         
22167         this.frameId = Roo.id();
22168         
22169          
22170         
22171         var iframe = this.owner.wrap.createChild({
22172             tag: 'iframe',
22173             cls: 'form-control', // bootstrap..
22174             id: this.frameId,
22175             name: this.frameId,
22176             frameBorder : 'no',
22177             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22178         }, this.el
22179         );
22180         
22181         
22182         this.iframe = iframe.dom;
22183
22184          this.assignDocWin();
22185         
22186         this.doc.designMode = 'on';
22187        
22188         this.doc.open();
22189         this.doc.write(this.getDocMarkup());
22190         this.doc.close();
22191
22192         
22193         var task = { // must defer to wait for browser to be ready
22194             run : function(){
22195                 //console.log("run task?" + this.doc.readyState);
22196                 this.assignDocWin();
22197                 if(this.doc.body || this.doc.readyState == 'complete'){
22198                     try {
22199                         this.doc.designMode="on";
22200                     } catch (e) {
22201                         return;
22202                     }
22203                     Roo.TaskMgr.stop(task);
22204                     this.initEditor.defer(10, this);
22205                 }
22206             },
22207             interval : 10,
22208             duration: 10000,
22209             scope: this
22210         };
22211         Roo.TaskMgr.start(task);
22212
22213     },
22214
22215     // private
22216     onResize : function(w, h)
22217     {
22218          Roo.log('resize: ' +w + ',' + h );
22219         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22220         if(!this.iframe){
22221             return;
22222         }
22223         if(typeof w == 'number'){
22224             
22225             this.iframe.style.width = w + 'px';
22226         }
22227         if(typeof h == 'number'){
22228             
22229             this.iframe.style.height = h + 'px';
22230             if(this.doc){
22231                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22232             }
22233         }
22234         
22235     },
22236
22237     /**
22238      * Toggles the editor between standard and source edit mode.
22239      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22240      */
22241     toggleSourceEdit : function(sourceEditMode){
22242         
22243         this.sourceEditMode = sourceEditMode === true;
22244         
22245         if(this.sourceEditMode){
22246  
22247             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22248             
22249         }else{
22250             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22251             //this.iframe.className = '';
22252             this.deferFocus();
22253         }
22254         //this.setSize(this.owner.wrap.getSize());
22255         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22256     },
22257
22258     
22259   
22260
22261     /**
22262      * Protected method that will not generally be called directly. If you need/want
22263      * custom HTML cleanup, this is the method you should override.
22264      * @param {String} html The HTML to be cleaned
22265      * return {String} The cleaned HTML
22266      */
22267     cleanHtml : function(html){
22268         html = String(html);
22269         if(html.length > 5){
22270             if(Roo.isSafari){ // strip safari nonsense
22271                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22272             }
22273         }
22274         if(html == '&nbsp;'){
22275             html = '';
22276         }
22277         return html;
22278     },
22279
22280     /**
22281      * HTML Editor -> Textarea
22282      * Protected method that will not generally be called directly. Syncs the contents
22283      * of the editor iframe with the textarea.
22284      */
22285     syncValue : function(){
22286         if(this.initialized){
22287             var bd = (this.doc.body || this.doc.documentElement);
22288             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22289             var html = bd.innerHTML;
22290             if(Roo.isSafari){
22291                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22292                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22293                 if(m && m[1]){
22294                     html = '<div style="'+m[0]+'">' + html + '</div>';
22295                 }
22296             }
22297             html = this.cleanHtml(html);
22298             // fix up the special chars.. normaly like back quotes in word...
22299             // however we do not want to do this with chinese..
22300             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22301                 var cc = b.charCodeAt();
22302                 if (
22303                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22304                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22305                     (cc >= 0xf900 && cc < 0xfb00 )
22306                 ) {
22307                         return b;
22308                 }
22309                 return "&#"+cc+";" 
22310             });
22311             if(this.owner.fireEvent('beforesync', this, html) !== false){
22312                 this.el.dom.value = html;
22313                 this.owner.fireEvent('sync', this, html);
22314             }
22315         }
22316     },
22317
22318     /**
22319      * Protected method that will not generally be called directly. Pushes the value of the textarea
22320      * into the iframe editor.
22321      */
22322     pushValue : function(){
22323         if(this.initialized){
22324             var v = this.el.dom.value.trim();
22325             
22326 //            if(v.length < 1){
22327 //                v = '&#160;';
22328 //            }
22329             
22330             if(this.owner.fireEvent('beforepush', this, v) !== false){
22331                 var d = (this.doc.body || this.doc.documentElement);
22332                 d.innerHTML = v;
22333                 this.cleanUpPaste();
22334                 this.el.dom.value = d.innerHTML;
22335                 this.owner.fireEvent('push', this, v);
22336             }
22337         }
22338     },
22339
22340     // private
22341     deferFocus : function(){
22342         this.focus.defer(10, this);
22343     },
22344
22345     // doc'ed in Field
22346     focus : function(){
22347         if(this.win && !this.sourceEditMode){
22348             this.win.focus();
22349         }else{
22350             this.el.focus();
22351         }
22352     },
22353     
22354     assignDocWin: function()
22355     {
22356         var iframe = this.iframe;
22357         
22358          if(Roo.isIE){
22359             this.doc = iframe.contentWindow.document;
22360             this.win = iframe.contentWindow;
22361         } else {
22362 //            if (!Roo.get(this.frameId)) {
22363 //                return;
22364 //            }
22365 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22366 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22367             
22368             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22369                 return;
22370             }
22371             
22372             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22373             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22374         }
22375     },
22376     
22377     // private
22378     initEditor : function(){
22379         //console.log("INIT EDITOR");
22380         this.assignDocWin();
22381         
22382         
22383         
22384         this.doc.designMode="on";
22385         this.doc.open();
22386         this.doc.write(this.getDocMarkup());
22387         this.doc.close();
22388         
22389         var dbody = (this.doc.body || this.doc.documentElement);
22390         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22391         // this copies styles from the containing element into thsi one..
22392         // not sure why we need all of this..
22393         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22394         
22395         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22396         //ss['background-attachment'] = 'fixed'; // w3c
22397         dbody.bgProperties = 'fixed'; // ie
22398         //Roo.DomHelper.applyStyles(dbody, ss);
22399         Roo.EventManager.on(this.doc, {
22400             //'mousedown': this.onEditorEvent,
22401             'mouseup': this.onEditorEvent,
22402             'dblclick': this.onEditorEvent,
22403             'click': this.onEditorEvent,
22404             'keyup': this.onEditorEvent,
22405             buffer:100,
22406             scope: this
22407         });
22408         if(Roo.isGecko){
22409             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22410         }
22411         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22412             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22413         }
22414         this.initialized = true;
22415
22416         this.owner.fireEvent('initialize', this);
22417         this.pushValue();
22418     },
22419
22420     // private
22421     onDestroy : function(){
22422         
22423         
22424         
22425         if(this.rendered){
22426             
22427             //for (var i =0; i < this.toolbars.length;i++) {
22428             //    // fixme - ask toolbars for heights?
22429             //    this.toolbars[i].onDestroy();
22430            // }
22431             
22432             //this.wrap.dom.innerHTML = '';
22433             //this.wrap.remove();
22434         }
22435     },
22436
22437     // private
22438     onFirstFocus : function(){
22439         
22440         this.assignDocWin();
22441         
22442         
22443         this.activated = true;
22444          
22445     
22446         if(Roo.isGecko){ // prevent silly gecko errors
22447             this.win.focus();
22448             var s = this.win.getSelection();
22449             if(!s.focusNode || s.focusNode.nodeType != 3){
22450                 var r = s.getRangeAt(0);
22451                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22452                 r.collapse(true);
22453                 this.deferFocus();
22454             }
22455             try{
22456                 this.execCmd('useCSS', true);
22457                 this.execCmd('styleWithCSS', false);
22458             }catch(e){}
22459         }
22460         this.owner.fireEvent('activate', this);
22461     },
22462
22463     // private
22464     adjustFont: function(btn){
22465         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22466         //if(Roo.isSafari){ // safari
22467         //    adjust *= 2;
22468        // }
22469         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22470         if(Roo.isSafari){ // safari
22471             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22472             v =  (v < 10) ? 10 : v;
22473             v =  (v > 48) ? 48 : v;
22474             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22475             
22476         }
22477         
22478         
22479         v = Math.max(1, v+adjust);
22480         
22481         this.execCmd('FontSize', v  );
22482     },
22483
22484     onEditorEvent : function(e)
22485     {
22486         this.owner.fireEvent('editorevent', this, e);
22487       //  this.updateToolbar();
22488         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22489     },
22490
22491     insertTag : function(tg)
22492     {
22493         // could be a bit smarter... -> wrap the current selected tRoo..
22494         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22495             
22496             range = this.createRange(this.getSelection());
22497             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22498             wrappingNode.appendChild(range.extractContents());
22499             range.insertNode(wrappingNode);
22500
22501             return;
22502             
22503             
22504             
22505         }
22506         this.execCmd("formatblock",   tg);
22507         
22508     },
22509     
22510     insertText : function(txt)
22511     {
22512         
22513         
22514         var range = this.createRange();
22515         range.deleteContents();
22516                //alert(Sender.getAttribute('label'));
22517                
22518         range.insertNode(this.doc.createTextNode(txt));
22519     } ,
22520     
22521      
22522
22523     /**
22524      * Executes a Midas editor command on the editor document and performs necessary focus and
22525      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22526      * @param {String} cmd The Midas command
22527      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22528      */
22529     relayCmd : function(cmd, value){
22530         this.win.focus();
22531         this.execCmd(cmd, value);
22532         this.owner.fireEvent('editorevent', this);
22533         //this.updateToolbar();
22534         this.owner.deferFocus();
22535     },
22536
22537     /**
22538      * Executes a Midas editor command directly on the editor document.
22539      * For visual commands, you should use {@link #relayCmd} instead.
22540      * <b>This should only be called after the editor is initialized.</b>
22541      * @param {String} cmd The Midas command
22542      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22543      */
22544     execCmd : function(cmd, value){
22545         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22546         this.syncValue();
22547     },
22548  
22549  
22550    
22551     /**
22552      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22553      * to insert tRoo.
22554      * @param {String} text | dom node.. 
22555      */
22556     insertAtCursor : function(text)
22557     {
22558         
22559         if(!this.activated){
22560             return;
22561         }
22562         /*
22563         if(Roo.isIE){
22564             this.win.focus();
22565             var r = this.doc.selection.createRange();
22566             if(r){
22567                 r.collapse(true);
22568                 r.pasteHTML(text);
22569                 this.syncValue();
22570                 this.deferFocus();
22571             
22572             }
22573             return;
22574         }
22575         */
22576         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22577             this.win.focus();
22578             
22579             
22580             // from jquery ui (MIT licenced)
22581             var range, node;
22582             var win = this.win;
22583             
22584             if (win.getSelection && win.getSelection().getRangeAt) {
22585                 range = win.getSelection().getRangeAt(0);
22586                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22587                 range.insertNode(node);
22588             } else if (win.document.selection && win.document.selection.createRange) {
22589                 // no firefox support
22590                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22591                 win.document.selection.createRange().pasteHTML(txt);
22592             } else {
22593                 // no firefox support
22594                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22595                 this.execCmd('InsertHTML', txt);
22596             } 
22597             
22598             this.syncValue();
22599             
22600             this.deferFocus();
22601         }
22602     },
22603  // private
22604     mozKeyPress : function(e){
22605         if(e.ctrlKey){
22606             var c = e.getCharCode(), cmd;
22607           
22608             if(c > 0){
22609                 c = String.fromCharCode(c).toLowerCase();
22610                 switch(c){
22611                     case 'b':
22612                         cmd = 'bold';
22613                         break;
22614                     case 'i':
22615                         cmd = 'italic';
22616                         break;
22617                     
22618                     case 'u':
22619                         cmd = 'underline';
22620                         break;
22621                     
22622                     case 'v':
22623                         this.cleanUpPaste.defer(100, this);
22624                         return;
22625                         
22626                 }
22627                 if(cmd){
22628                     this.win.focus();
22629                     this.execCmd(cmd);
22630                     this.deferFocus();
22631                     e.preventDefault();
22632                 }
22633                 
22634             }
22635         }
22636     },
22637
22638     // private
22639     fixKeys : function(){ // load time branching for fastest keydown performance
22640         if(Roo.isIE){
22641             return function(e){
22642                 var k = e.getKey(), r;
22643                 if(k == e.TAB){
22644                     e.stopEvent();
22645                     r = this.doc.selection.createRange();
22646                     if(r){
22647                         r.collapse(true);
22648                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22649                         this.deferFocus();
22650                     }
22651                     return;
22652                 }
22653                 
22654                 if(k == e.ENTER){
22655                     r = this.doc.selection.createRange();
22656                     if(r){
22657                         var target = r.parentElement();
22658                         if(!target || target.tagName.toLowerCase() != 'li'){
22659                             e.stopEvent();
22660                             r.pasteHTML('<br />');
22661                             r.collapse(false);
22662                             r.select();
22663                         }
22664                     }
22665                 }
22666                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22667                     this.cleanUpPaste.defer(100, this);
22668                     return;
22669                 }
22670                 
22671                 
22672             };
22673         }else if(Roo.isOpera){
22674             return function(e){
22675                 var k = e.getKey();
22676                 if(k == e.TAB){
22677                     e.stopEvent();
22678                     this.win.focus();
22679                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22680                     this.deferFocus();
22681                 }
22682                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22683                     this.cleanUpPaste.defer(100, this);
22684                     return;
22685                 }
22686                 
22687             };
22688         }else if(Roo.isSafari){
22689             return function(e){
22690                 var k = e.getKey();
22691                 
22692                 if(k == e.TAB){
22693                     e.stopEvent();
22694                     this.execCmd('InsertText','\t');
22695                     this.deferFocus();
22696                     return;
22697                 }
22698                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22699                     this.cleanUpPaste.defer(100, this);
22700                     return;
22701                 }
22702                 
22703              };
22704         }
22705     }(),
22706     
22707     getAllAncestors: function()
22708     {
22709         var p = this.getSelectedNode();
22710         var a = [];
22711         if (!p) {
22712             a.push(p); // push blank onto stack..
22713             p = this.getParentElement();
22714         }
22715         
22716         
22717         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22718             a.push(p);
22719             p = p.parentNode;
22720         }
22721         a.push(this.doc.body);
22722         return a;
22723     },
22724     lastSel : false,
22725     lastSelNode : false,
22726     
22727     
22728     getSelection : function() 
22729     {
22730         this.assignDocWin();
22731         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22732     },
22733     
22734     getSelectedNode: function() 
22735     {
22736         // this may only work on Gecko!!!
22737         
22738         // should we cache this!!!!
22739         
22740         
22741         
22742          
22743         var range = this.createRange(this.getSelection()).cloneRange();
22744         
22745         if (Roo.isIE) {
22746             var parent = range.parentElement();
22747             while (true) {
22748                 var testRange = range.duplicate();
22749                 testRange.moveToElementText(parent);
22750                 if (testRange.inRange(range)) {
22751                     break;
22752                 }
22753                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22754                     break;
22755                 }
22756                 parent = parent.parentElement;
22757             }
22758             return parent;
22759         }
22760         
22761         // is ancestor a text element.
22762         var ac =  range.commonAncestorContainer;
22763         if (ac.nodeType == 3) {
22764             ac = ac.parentNode;
22765         }
22766         
22767         var ar = ac.childNodes;
22768          
22769         var nodes = [];
22770         var other_nodes = [];
22771         var has_other_nodes = false;
22772         for (var i=0;i<ar.length;i++) {
22773             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22774                 continue;
22775             }
22776             // fullly contained node.
22777             
22778             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22779                 nodes.push(ar[i]);
22780                 continue;
22781             }
22782             
22783             // probably selected..
22784             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22785                 other_nodes.push(ar[i]);
22786                 continue;
22787             }
22788             // outer..
22789             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22790                 continue;
22791             }
22792             
22793             
22794             has_other_nodes = true;
22795         }
22796         if (!nodes.length && other_nodes.length) {
22797             nodes= other_nodes;
22798         }
22799         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22800             return false;
22801         }
22802         
22803         return nodes[0];
22804     },
22805     createRange: function(sel)
22806     {
22807         // this has strange effects when using with 
22808         // top toolbar - not sure if it's a great idea.
22809         //this.editor.contentWindow.focus();
22810         if (typeof sel != "undefined") {
22811             try {
22812                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22813             } catch(e) {
22814                 return this.doc.createRange();
22815             }
22816         } else {
22817             return this.doc.createRange();
22818         }
22819     },
22820     getParentElement: function()
22821     {
22822         
22823         this.assignDocWin();
22824         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22825         
22826         var range = this.createRange(sel);
22827          
22828         try {
22829             var p = range.commonAncestorContainer;
22830             while (p.nodeType == 3) { // text node
22831                 p = p.parentNode;
22832             }
22833             return p;
22834         } catch (e) {
22835             return null;
22836         }
22837     
22838     },
22839     /***
22840      *
22841      * Range intersection.. the hard stuff...
22842      *  '-1' = before
22843      *  '0' = hits..
22844      *  '1' = after.
22845      *         [ -- selected range --- ]
22846      *   [fail]                        [fail]
22847      *
22848      *    basically..
22849      *      if end is before start or  hits it. fail.
22850      *      if start is after end or hits it fail.
22851      *
22852      *   if either hits (but other is outside. - then it's not 
22853      *   
22854      *    
22855      **/
22856     
22857     
22858     // @see http://www.thismuchiknow.co.uk/?p=64.
22859     rangeIntersectsNode : function(range, node)
22860     {
22861         var nodeRange = node.ownerDocument.createRange();
22862         try {
22863             nodeRange.selectNode(node);
22864         } catch (e) {
22865             nodeRange.selectNodeContents(node);
22866         }
22867     
22868         var rangeStartRange = range.cloneRange();
22869         rangeStartRange.collapse(true);
22870     
22871         var rangeEndRange = range.cloneRange();
22872         rangeEndRange.collapse(false);
22873     
22874         var nodeStartRange = nodeRange.cloneRange();
22875         nodeStartRange.collapse(true);
22876     
22877         var nodeEndRange = nodeRange.cloneRange();
22878         nodeEndRange.collapse(false);
22879     
22880         return rangeStartRange.compareBoundaryPoints(
22881                  Range.START_TO_START, nodeEndRange) == -1 &&
22882                rangeEndRange.compareBoundaryPoints(
22883                  Range.START_TO_START, nodeStartRange) == 1;
22884         
22885          
22886     },
22887     rangeCompareNode : function(range, node)
22888     {
22889         var nodeRange = node.ownerDocument.createRange();
22890         try {
22891             nodeRange.selectNode(node);
22892         } catch (e) {
22893             nodeRange.selectNodeContents(node);
22894         }
22895         
22896         
22897         range.collapse(true);
22898     
22899         nodeRange.collapse(true);
22900      
22901         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22902         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22903          
22904         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22905         
22906         var nodeIsBefore   =  ss == 1;
22907         var nodeIsAfter    = ee == -1;
22908         
22909         if (nodeIsBefore && nodeIsAfter) {
22910             return 0; // outer
22911         }
22912         if (!nodeIsBefore && nodeIsAfter) {
22913             return 1; //right trailed.
22914         }
22915         
22916         if (nodeIsBefore && !nodeIsAfter) {
22917             return 2;  // left trailed.
22918         }
22919         // fully contined.
22920         return 3;
22921     },
22922
22923     // private? - in a new class?
22924     cleanUpPaste :  function()
22925     {
22926         // cleans up the whole document..
22927         Roo.log('cleanuppaste');
22928         
22929         this.cleanUpChildren(this.doc.body);
22930         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22931         if (clean != this.doc.body.innerHTML) {
22932             this.doc.body.innerHTML = clean;
22933         }
22934         
22935     },
22936     
22937     cleanWordChars : function(input) {// change the chars to hex code
22938         var he = Roo.HtmlEditorCore;
22939         
22940         var output = input;
22941         Roo.each(he.swapCodes, function(sw) { 
22942             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22943             
22944             output = output.replace(swapper, sw[1]);
22945         });
22946         
22947         return output;
22948     },
22949     
22950     
22951     cleanUpChildren : function (n)
22952     {
22953         if (!n.childNodes.length) {
22954             return;
22955         }
22956         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22957            this.cleanUpChild(n.childNodes[i]);
22958         }
22959     },
22960     
22961     
22962         
22963     
22964     cleanUpChild : function (node)
22965     {
22966         var ed = this;
22967         //console.log(node);
22968         if (node.nodeName == "#text") {
22969             // clean up silly Windows -- stuff?
22970             return; 
22971         }
22972         if (node.nodeName == "#comment") {
22973             node.parentNode.removeChild(node);
22974             // clean up silly Windows -- stuff?
22975             return; 
22976         }
22977         var lcname = node.tagName.toLowerCase();
22978         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22979         // whitelist of tags..
22980         
22981         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22982             // remove node.
22983             node.parentNode.removeChild(node);
22984             return;
22985             
22986         }
22987         
22988         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22989         
22990         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22991         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22992         
22993         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22994         //    remove_keep_children = true;
22995         //}
22996         
22997         if (remove_keep_children) {
22998             this.cleanUpChildren(node);
22999             // inserts everything just before this node...
23000             while (node.childNodes.length) {
23001                 var cn = node.childNodes[0];
23002                 node.removeChild(cn);
23003                 node.parentNode.insertBefore(cn, node);
23004             }
23005             node.parentNode.removeChild(node);
23006             return;
23007         }
23008         
23009         if (!node.attributes || !node.attributes.length) {
23010             this.cleanUpChildren(node);
23011             return;
23012         }
23013         
23014         function cleanAttr(n,v)
23015         {
23016             
23017             if (v.match(/^\./) || v.match(/^\//)) {
23018                 return;
23019             }
23020             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23021                 return;
23022             }
23023             if (v.match(/^#/)) {
23024                 return;
23025             }
23026 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23027             node.removeAttribute(n);
23028             
23029         }
23030         
23031         var cwhite = this.cwhite;
23032         var cblack = this.cblack;
23033             
23034         function cleanStyle(n,v)
23035         {
23036             if (v.match(/expression/)) { //XSS?? should we even bother..
23037                 node.removeAttribute(n);
23038                 return;
23039             }
23040             
23041             var parts = v.split(/;/);
23042             var clean = [];
23043             
23044             Roo.each(parts, function(p) {
23045                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23046                 if (!p.length) {
23047                     return true;
23048                 }
23049                 var l = p.split(':').shift().replace(/\s+/g,'');
23050                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23051                 
23052                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23053 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23054                     //node.removeAttribute(n);
23055                     return true;
23056                 }
23057                 //Roo.log()
23058                 // only allow 'c whitelisted system attributes'
23059                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23060 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23061                     //node.removeAttribute(n);
23062                     return true;
23063                 }
23064                 
23065                 
23066                  
23067                 
23068                 clean.push(p);
23069                 return true;
23070             });
23071             if (clean.length) { 
23072                 node.setAttribute(n, clean.join(';'));
23073             } else {
23074                 node.removeAttribute(n);
23075             }
23076             
23077         }
23078         
23079         
23080         for (var i = node.attributes.length-1; i > -1 ; i--) {
23081             var a = node.attributes[i];
23082             //console.log(a);
23083             
23084             if (a.name.toLowerCase().substr(0,2)=='on')  {
23085                 node.removeAttribute(a.name);
23086                 continue;
23087             }
23088             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23089                 node.removeAttribute(a.name);
23090                 continue;
23091             }
23092             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23093                 cleanAttr(a.name,a.value); // fixme..
23094                 continue;
23095             }
23096             if (a.name == 'style') {
23097                 cleanStyle(a.name,a.value);
23098                 continue;
23099             }
23100             /// clean up MS crap..
23101             // tecnically this should be a list of valid class'es..
23102             
23103             
23104             if (a.name == 'class') {
23105                 if (a.value.match(/^Mso/)) {
23106                     node.className = '';
23107                 }
23108                 
23109                 if (a.value.match(/^body$/)) {
23110                     node.className = '';
23111                 }
23112                 continue;
23113             }
23114             
23115             // style cleanup!?
23116             // class cleanup?
23117             
23118         }
23119         
23120         
23121         this.cleanUpChildren(node);
23122         
23123         
23124     },
23125     
23126     /**
23127      * Clean up MS wordisms...
23128      */
23129     cleanWord : function(node)
23130     {
23131         
23132         
23133         if (!node) {
23134             this.cleanWord(this.doc.body);
23135             return;
23136         }
23137         if (node.nodeName == "#text") {
23138             // clean up silly Windows -- stuff?
23139             return; 
23140         }
23141         if (node.nodeName == "#comment") {
23142             node.parentNode.removeChild(node);
23143             // clean up silly Windows -- stuff?
23144             return; 
23145         }
23146         
23147         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23148             node.parentNode.removeChild(node);
23149             return;
23150         }
23151         
23152         // remove - but keep children..
23153         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23154             while (node.childNodes.length) {
23155                 var cn = node.childNodes[0];
23156                 node.removeChild(cn);
23157                 node.parentNode.insertBefore(cn, node);
23158             }
23159             node.parentNode.removeChild(node);
23160             this.iterateChildren(node, this.cleanWord);
23161             return;
23162         }
23163         // clean styles
23164         if (node.className.length) {
23165             
23166             var cn = node.className.split(/\W+/);
23167             var cna = [];
23168             Roo.each(cn, function(cls) {
23169                 if (cls.match(/Mso[a-zA-Z]+/)) {
23170                     return;
23171                 }
23172                 cna.push(cls);
23173             });
23174             node.className = cna.length ? cna.join(' ') : '';
23175             if (!cna.length) {
23176                 node.removeAttribute("class");
23177             }
23178         }
23179         
23180         if (node.hasAttribute("lang")) {
23181             node.removeAttribute("lang");
23182         }
23183         
23184         if (node.hasAttribute("style")) {
23185             
23186             var styles = node.getAttribute("style").split(";");
23187             var nstyle = [];
23188             Roo.each(styles, function(s) {
23189                 if (!s.match(/:/)) {
23190                     return;
23191                 }
23192                 var kv = s.split(":");
23193                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23194                     return;
23195                 }
23196                 // what ever is left... we allow.
23197                 nstyle.push(s);
23198             });
23199             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23200             if (!nstyle.length) {
23201                 node.removeAttribute('style');
23202             }
23203         }
23204         this.iterateChildren(node, this.cleanWord);
23205         
23206         
23207         
23208     },
23209     /**
23210      * iterateChildren of a Node, calling fn each time, using this as the scole..
23211      * @param {DomNode} node node to iterate children of.
23212      * @param {Function} fn method of this class to call on each item.
23213      */
23214     iterateChildren : function(node, fn)
23215     {
23216         if (!node.childNodes.length) {
23217                 return;
23218         }
23219         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23220            fn.call(this, node.childNodes[i])
23221         }
23222     },
23223     
23224     
23225     /**
23226      * cleanTableWidths.
23227      *
23228      * Quite often pasting from word etc.. results in tables with column and widths.
23229      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23230      *
23231      */
23232     cleanTableWidths : function(node)
23233     {
23234          
23235          
23236         if (!node) {
23237             this.cleanTableWidths(this.doc.body);
23238             return;
23239         }
23240         
23241         // ignore list...
23242         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23243             return; 
23244         }
23245         Roo.log(node.tagName);
23246         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23247             this.iterateChildren(node, this.cleanTableWidths);
23248             return;
23249         }
23250         if (node.hasAttribute('width')) {
23251             node.removeAttribute('width');
23252         }
23253         
23254          
23255         if (node.hasAttribute("style")) {
23256             // pretty basic...
23257             
23258             var styles = node.getAttribute("style").split(";");
23259             var nstyle = [];
23260             Roo.each(styles, function(s) {
23261                 if (!s.match(/:/)) {
23262                     return;
23263                 }
23264                 var kv = s.split(":");
23265                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23266                     return;
23267                 }
23268                 // what ever is left... we allow.
23269                 nstyle.push(s);
23270             });
23271             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23272             if (!nstyle.length) {
23273                 node.removeAttribute('style');
23274             }
23275         }
23276         
23277         this.iterateChildren(node, this.cleanTableWidths);
23278         
23279         
23280     },
23281     
23282     
23283     
23284     
23285     domToHTML : function(currentElement, depth, nopadtext) {
23286         
23287         depth = depth || 0;
23288         nopadtext = nopadtext || false;
23289     
23290         if (!currentElement) {
23291             return this.domToHTML(this.doc.body);
23292         }
23293         
23294         //Roo.log(currentElement);
23295         var j;
23296         var allText = false;
23297         var nodeName = currentElement.nodeName;
23298         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23299         
23300         if  (nodeName == '#text') {
23301             
23302             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23303         }
23304         
23305         
23306         var ret = '';
23307         if (nodeName != 'BODY') {
23308              
23309             var i = 0;
23310             // Prints the node tagName, such as <A>, <IMG>, etc
23311             if (tagName) {
23312                 var attr = [];
23313                 for(i = 0; i < currentElement.attributes.length;i++) {
23314                     // quoting?
23315                     var aname = currentElement.attributes.item(i).name;
23316                     if (!currentElement.attributes.item(i).value.length) {
23317                         continue;
23318                     }
23319                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23320                 }
23321                 
23322                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23323             } 
23324             else {
23325                 
23326                 // eack
23327             }
23328         } else {
23329             tagName = false;
23330         }
23331         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23332             return ret;
23333         }
23334         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23335             nopadtext = true;
23336         }
23337         
23338         
23339         // Traverse the tree
23340         i = 0;
23341         var currentElementChild = currentElement.childNodes.item(i);
23342         var allText = true;
23343         var innerHTML  = '';
23344         lastnode = '';
23345         while (currentElementChild) {
23346             // Formatting code (indent the tree so it looks nice on the screen)
23347             var nopad = nopadtext;
23348             if (lastnode == 'SPAN') {
23349                 nopad  = true;
23350             }
23351             // text
23352             if  (currentElementChild.nodeName == '#text') {
23353                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23354                 toadd = nopadtext ? toadd : toadd.trim();
23355                 if (!nopad && toadd.length > 80) {
23356                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23357                 }
23358                 innerHTML  += toadd;
23359                 
23360                 i++;
23361                 currentElementChild = currentElement.childNodes.item(i);
23362                 lastNode = '';
23363                 continue;
23364             }
23365             allText = false;
23366             
23367             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23368                 
23369             // Recursively traverse the tree structure of the child node
23370             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23371             lastnode = currentElementChild.nodeName;
23372             i++;
23373             currentElementChild=currentElement.childNodes.item(i);
23374         }
23375         
23376         ret += innerHTML;
23377         
23378         if (!allText) {
23379                 // The remaining code is mostly for formatting the tree
23380             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23381         }
23382         
23383         
23384         if (tagName) {
23385             ret+= "</"+tagName+">";
23386         }
23387         return ret;
23388         
23389     },
23390         
23391     applyBlacklists : function()
23392     {
23393         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23394         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23395         
23396         this.white = [];
23397         this.black = [];
23398         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23399             if (b.indexOf(tag) > -1) {
23400                 return;
23401             }
23402             this.white.push(tag);
23403             
23404         }, this);
23405         
23406         Roo.each(w, function(tag) {
23407             if (b.indexOf(tag) > -1) {
23408                 return;
23409             }
23410             if (this.white.indexOf(tag) > -1) {
23411                 return;
23412             }
23413             this.white.push(tag);
23414             
23415         }, this);
23416         
23417         
23418         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23419             if (w.indexOf(tag) > -1) {
23420                 return;
23421             }
23422             this.black.push(tag);
23423             
23424         }, this);
23425         
23426         Roo.each(b, function(tag) {
23427             if (w.indexOf(tag) > -1) {
23428                 return;
23429             }
23430             if (this.black.indexOf(tag) > -1) {
23431                 return;
23432             }
23433             this.black.push(tag);
23434             
23435         }, this);
23436         
23437         
23438         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23439         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23440         
23441         this.cwhite = [];
23442         this.cblack = [];
23443         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23444             if (b.indexOf(tag) > -1) {
23445                 return;
23446             }
23447             this.cwhite.push(tag);
23448             
23449         }, this);
23450         
23451         Roo.each(w, function(tag) {
23452             if (b.indexOf(tag) > -1) {
23453                 return;
23454             }
23455             if (this.cwhite.indexOf(tag) > -1) {
23456                 return;
23457             }
23458             this.cwhite.push(tag);
23459             
23460         }, this);
23461         
23462         
23463         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23464             if (w.indexOf(tag) > -1) {
23465                 return;
23466             }
23467             this.cblack.push(tag);
23468             
23469         }, this);
23470         
23471         Roo.each(b, function(tag) {
23472             if (w.indexOf(tag) > -1) {
23473                 return;
23474             }
23475             if (this.cblack.indexOf(tag) > -1) {
23476                 return;
23477             }
23478             this.cblack.push(tag);
23479             
23480         }, this);
23481     },
23482     
23483     setStylesheets : function(stylesheets)
23484     {
23485         if(typeof(stylesheets) == 'string'){
23486             Roo.get(this.iframe.contentDocument.head).createChild({
23487                 tag : 'link',
23488                 rel : 'stylesheet',
23489                 type : 'text/css',
23490                 href : stylesheets
23491             });
23492             
23493             return;
23494         }
23495         var _this = this;
23496      
23497         Roo.each(stylesheets, function(s) {
23498             if(!s.length){
23499                 return;
23500             }
23501             
23502             Roo.get(_this.iframe.contentDocument.head).createChild({
23503                 tag : 'link',
23504                 rel : 'stylesheet',
23505                 type : 'text/css',
23506                 href : s
23507             });
23508         });
23509
23510         
23511     },
23512     
23513     removeStylesheets : function()
23514     {
23515         var _this = this;
23516         
23517         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23518             s.remove();
23519         });
23520     },
23521     
23522     setStyle : function(style)
23523     {
23524         Roo.get(this.iframe.contentDocument.head).createChild({
23525             tag : 'style',
23526             type : 'text/css',
23527             html : style
23528         });
23529
23530         return;
23531     }
23532     
23533     // hide stuff that is not compatible
23534     /**
23535      * @event blur
23536      * @hide
23537      */
23538     /**
23539      * @event change
23540      * @hide
23541      */
23542     /**
23543      * @event focus
23544      * @hide
23545      */
23546     /**
23547      * @event specialkey
23548      * @hide
23549      */
23550     /**
23551      * @cfg {String} fieldClass @hide
23552      */
23553     /**
23554      * @cfg {String} focusClass @hide
23555      */
23556     /**
23557      * @cfg {String} autoCreate @hide
23558      */
23559     /**
23560      * @cfg {String} inputType @hide
23561      */
23562     /**
23563      * @cfg {String} invalidClass @hide
23564      */
23565     /**
23566      * @cfg {String} invalidText @hide
23567      */
23568     /**
23569      * @cfg {String} msgFx @hide
23570      */
23571     /**
23572      * @cfg {String} validateOnBlur @hide
23573      */
23574 });
23575
23576 Roo.HtmlEditorCore.white = [
23577         'area', 'br', 'img', 'input', 'hr', 'wbr',
23578         
23579        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23580        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23581        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23582        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23583        'table',   'ul',         'xmp', 
23584        
23585        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23586       'thead',   'tr', 
23587      
23588       'dir', 'menu', 'ol', 'ul', 'dl',
23589        
23590       'embed',  'object'
23591 ];
23592
23593
23594 Roo.HtmlEditorCore.black = [
23595     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23596         'applet', // 
23597         'base',   'basefont', 'bgsound', 'blink',  'body', 
23598         'frame',  'frameset', 'head',    'html',   'ilayer', 
23599         'iframe', 'layer',  'link',     'meta',    'object',   
23600         'script', 'style' ,'title',  'xml' // clean later..
23601 ];
23602 Roo.HtmlEditorCore.clean = [
23603     'script', 'style', 'title', 'xml'
23604 ];
23605 Roo.HtmlEditorCore.remove = [
23606     'font'
23607 ];
23608 // attributes..
23609
23610 Roo.HtmlEditorCore.ablack = [
23611     'on'
23612 ];
23613     
23614 Roo.HtmlEditorCore.aclean = [ 
23615     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23616 ];
23617
23618 // protocols..
23619 Roo.HtmlEditorCore.pwhite= [
23620         'http',  'https',  'mailto'
23621 ];
23622
23623 // white listed style attributes.
23624 Roo.HtmlEditorCore.cwhite= [
23625       //  'text-align', /// default is to allow most things..
23626       
23627          
23628 //        'font-size'//??
23629 ];
23630
23631 // black listed style attributes.
23632 Roo.HtmlEditorCore.cblack= [
23633       //  'font-size' -- this can be set by the project 
23634 ];
23635
23636
23637 Roo.HtmlEditorCore.swapCodes   =[ 
23638     [    8211, "--" ], 
23639     [    8212, "--" ], 
23640     [    8216,  "'" ],  
23641     [    8217, "'" ],  
23642     [    8220, '"' ],  
23643     [    8221, '"' ],  
23644     [    8226, "*" ],  
23645     [    8230, "..." ]
23646 ]; 
23647
23648     /*
23649  * - LGPL
23650  *
23651  * HtmlEditor
23652  * 
23653  */
23654
23655 /**
23656  * @class Roo.bootstrap.HtmlEditor
23657  * @extends Roo.bootstrap.TextArea
23658  * Bootstrap HtmlEditor class
23659
23660  * @constructor
23661  * Create a new HtmlEditor
23662  * @param {Object} config The config object
23663  */
23664
23665 Roo.bootstrap.HtmlEditor = function(config){
23666     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23667     if (!this.toolbars) {
23668         this.toolbars = [];
23669     }
23670     
23671     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23672     this.addEvents({
23673             /**
23674              * @event initialize
23675              * Fires when the editor is fully initialized (including the iframe)
23676              * @param {HtmlEditor} this
23677              */
23678             initialize: true,
23679             /**
23680              * @event activate
23681              * Fires when the editor is first receives the focus. Any insertion must wait
23682              * until after this event.
23683              * @param {HtmlEditor} this
23684              */
23685             activate: true,
23686              /**
23687              * @event beforesync
23688              * Fires before the textarea is updated with content from the editor iframe. Return false
23689              * to cancel the sync.
23690              * @param {HtmlEditor} this
23691              * @param {String} html
23692              */
23693             beforesync: true,
23694              /**
23695              * @event beforepush
23696              * Fires before the iframe editor is updated with content from the textarea. Return false
23697              * to cancel the push.
23698              * @param {HtmlEditor} this
23699              * @param {String} html
23700              */
23701             beforepush: true,
23702              /**
23703              * @event sync
23704              * Fires when the textarea is updated with content from the editor iframe.
23705              * @param {HtmlEditor} this
23706              * @param {String} html
23707              */
23708             sync: true,
23709              /**
23710              * @event push
23711              * Fires when the iframe editor is updated with content from the textarea.
23712              * @param {HtmlEditor} this
23713              * @param {String} html
23714              */
23715             push: true,
23716              /**
23717              * @event editmodechange
23718              * Fires when the editor switches edit modes
23719              * @param {HtmlEditor} this
23720              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23721              */
23722             editmodechange: true,
23723             /**
23724              * @event editorevent
23725              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23726              * @param {HtmlEditor} this
23727              */
23728             editorevent: true,
23729             /**
23730              * @event firstfocus
23731              * Fires when on first focus - needed by toolbars..
23732              * @param {HtmlEditor} this
23733              */
23734             firstfocus: true,
23735             /**
23736              * @event autosave
23737              * Auto save the htmlEditor value as a file into Events
23738              * @param {HtmlEditor} this
23739              */
23740             autosave: true,
23741             /**
23742              * @event savedpreview
23743              * preview the saved version of htmlEditor
23744              * @param {HtmlEditor} this
23745              */
23746             savedpreview: true
23747         });
23748 };
23749
23750
23751 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23752     
23753     
23754       /**
23755      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23756      */
23757     toolbars : false,
23758     
23759      /**
23760     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23761     */
23762     btns : [],
23763    
23764      /**
23765      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23766      *                        Roo.resizable.
23767      */
23768     resizable : false,
23769      /**
23770      * @cfg {Number} height (in pixels)
23771      */   
23772     height: 300,
23773    /**
23774      * @cfg {Number} width (in pixels)
23775      */   
23776     width: false,
23777     
23778     /**
23779      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23780      * 
23781      */
23782     stylesheets: false,
23783     
23784     // id of frame..
23785     frameId: false,
23786     
23787     // private properties
23788     validationEvent : false,
23789     deferHeight: true,
23790     initialized : false,
23791     activated : false,
23792     
23793     onFocus : Roo.emptyFn,
23794     iframePad:3,
23795     hideMode:'offsets',
23796     
23797     tbContainer : false,
23798     
23799     bodyCls : '',
23800     
23801     toolbarContainer :function() {
23802         return this.wrap.select('.x-html-editor-tb',true).first();
23803     },
23804
23805     /**
23806      * Protected method that will not generally be called directly. It
23807      * is called when the editor creates its toolbar. Override this method if you need to
23808      * add custom toolbar buttons.
23809      * @param {HtmlEditor} editor
23810      */
23811     createToolbar : function(){
23812         Roo.log('renewing');
23813         Roo.log("create toolbars");
23814         
23815         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23816         this.toolbars[0].render(this.toolbarContainer());
23817         
23818         return;
23819         
23820 //        if (!editor.toolbars || !editor.toolbars.length) {
23821 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23822 //        }
23823 //        
23824 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23825 //            editor.toolbars[i] = Roo.factory(
23826 //                    typeof(editor.toolbars[i]) == 'string' ?
23827 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23828 //                Roo.bootstrap.HtmlEditor);
23829 //            editor.toolbars[i].init(editor);
23830 //        }
23831     },
23832
23833      
23834     // private
23835     onRender : function(ct, position)
23836     {
23837        // Roo.log("Call onRender: " + this.xtype);
23838         var _t = this;
23839         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23840       
23841         this.wrap = this.inputEl().wrap({
23842             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23843         });
23844         
23845         this.editorcore.onRender(ct, position);
23846          
23847         if (this.resizable) {
23848             this.resizeEl = new Roo.Resizable(this.wrap, {
23849                 pinned : true,
23850                 wrap: true,
23851                 dynamic : true,
23852                 minHeight : this.height,
23853                 height: this.height,
23854                 handles : this.resizable,
23855                 width: this.width,
23856                 listeners : {
23857                     resize : function(r, w, h) {
23858                         _t.onResize(w,h); // -something
23859                     }
23860                 }
23861             });
23862             
23863         }
23864         this.createToolbar(this);
23865        
23866         
23867         if(!this.width && this.resizable){
23868             this.setSize(this.wrap.getSize());
23869         }
23870         if (this.resizeEl) {
23871             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23872             // should trigger onReize..
23873         }
23874         
23875     },
23876
23877     // private
23878     onResize : function(w, h)
23879     {
23880         Roo.log('resize: ' +w + ',' + h );
23881         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23882         var ew = false;
23883         var eh = false;
23884         
23885         if(this.inputEl() ){
23886             if(typeof w == 'number'){
23887                 var aw = w - this.wrap.getFrameWidth('lr');
23888                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23889                 ew = aw;
23890             }
23891             if(typeof h == 'number'){
23892                  var tbh = -11;  // fixme it needs to tool bar size!
23893                 for (var i =0; i < this.toolbars.length;i++) {
23894                     // fixme - ask toolbars for heights?
23895                     tbh += this.toolbars[i].el.getHeight();
23896                     //if (this.toolbars[i].footer) {
23897                     //    tbh += this.toolbars[i].footer.el.getHeight();
23898                     //}
23899                 }
23900               
23901                 
23902                 
23903                 
23904                 
23905                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23906                 ah -= 5; // knock a few pixes off for look..
23907                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23908                 var eh = ah;
23909             }
23910         }
23911         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23912         this.editorcore.onResize(ew,eh);
23913         
23914     },
23915
23916     /**
23917      * Toggles the editor between standard and source edit mode.
23918      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23919      */
23920     toggleSourceEdit : function(sourceEditMode)
23921     {
23922         this.editorcore.toggleSourceEdit(sourceEditMode);
23923         
23924         if(this.editorcore.sourceEditMode){
23925             Roo.log('editor - showing textarea');
23926             
23927 //            Roo.log('in');
23928 //            Roo.log(this.syncValue());
23929             this.syncValue();
23930             this.inputEl().removeClass(['hide', 'x-hidden']);
23931             this.inputEl().dom.removeAttribute('tabIndex');
23932             this.inputEl().focus();
23933         }else{
23934             Roo.log('editor - hiding textarea');
23935 //            Roo.log('out')
23936 //            Roo.log(this.pushValue()); 
23937             this.pushValue();
23938             
23939             this.inputEl().addClass(['hide', 'x-hidden']);
23940             this.inputEl().dom.setAttribute('tabIndex', -1);
23941             //this.deferFocus();
23942         }
23943          
23944         if(this.resizable){
23945             this.setSize(this.wrap.getSize());
23946         }
23947         
23948         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23949     },
23950  
23951     // private (for BoxComponent)
23952     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23953
23954     // private (for BoxComponent)
23955     getResizeEl : function(){
23956         return this.wrap;
23957     },
23958
23959     // private (for BoxComponent)
23960     getPositionEl : function(){
23961         return this.wrap;
23962     },
23963
23964     // private
23965     initEvents : function(){
23966         this.originalValue = this.getValue();
23967     },
23968
23969 //    /**
23970 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23971 //     * @method
23972 //     */
23973 //    markInvalid : Roo.emptyFn,
23974 //    /**
23975 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23976 //     * @method
23977 //     */
23978 //    clearInvalid : Roo.emptyFn,
23979
23980     setValue : function(v){
23981         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23982         this.editorcore.pushValue();
23983     },
23984
23985      
23986     // private
23987     deferFocus : function(){
23988         this.focus.defer(10, this);
23989     },
23990
23991     // doc'ed in Field
23992     focus : function(){
23993         this.editorcore.focus();
23994         
23995     },
23996       
23997
23998     // private
23999     onDestroy : function(){
24000         
24001         
24002         
24003         if(this.rendered){
24004             
24005             for (var i =0; i < this.toolbars.length;i++) {
24006                 // fixme - ask toolbars for heights?
24007                 this.toolbars[i].onDestroy();
24008             }
24009             
24010             this.wrap.dom.innerHTML = '';
24011             this.wrap.remove();
24012         }
24013     },
24014
24015     // private
24016     onFirstFocus : function(){
24017         //Roo.log("onFirstFocus");
24018         this.editorcore.onFirstFocus();
24019          for (var i =0; i < this.toolbars.length;i++) {
24020             this.toolbars[i].onFirstFocus();
24021         }
24022         
24023     },
24024     
24025     // private
24026     syncValue : function()
24027     {   
24028         this.editorcore.syncValue();
24029     },
24030     
24031     pushValue : function()
24032     {   
24033         this.editorcore.pushValue();
24034     }
24035      
24036     
24037     // hide stuff that is not compatible
24038     /**
24039      * @event blur
24040      * @hide
24041      */
24042     /**
24043      * @event change
24044      * @hide
24045      */
24046     /**
24047      * @event focus
24048      * @hide
24049      */
24050     /**
24051      * @event specialkey
24052      * @hide
24053      */
24054     /**
24055      * @cfg {String} fieldClass @hide
24056      */
24057     /**
24058      * @cfg {String} focusClass @hide
24059      */
24060     /**
24061      * @cfg {String} autoCreate @hide
24062      */
24063     /**
24064      * @cfg {String} inputType @hide
24065      */
24066      
24067     /**
24068      * @cfg {String} invalidText @hide
24069      */
24070     /**
24071      * @cfg {String} msgFx @hide
24072      */
24073     /**
24074      * @cfg {String} validateOnBlur @hide
24075      */
24076 });
24077  
24078     
24079    
24080    
24081    
24082       
24083 Roo.namespace('Roo.bootstrap.htmleditor');
24084 /**
24085  * @class Roo.bootstrap.HtmlEditorToolbar1
24086  * Basic Toolbar
24087  * 
24088  * Usage:
24089  *
24090  new Roo.bootstrap.HtmlEditor({
24091     ....
24092     toolbars : [
24093         new Roo.bootstrap.HtmlEditorToolbar1({
24094             disable : { fonts: 1 , format: 1, ..., ... , ...],
24095             btns : [ .... ]
24096         })
24097     }
24098      
24099  * 
24100  * @cfg {Object} disable List of elements to disable..
24101  * @cfg {Array} btns List of additional buttons.
24102  * 
24103  * 
24104  * NEEDS Extra CSS? 
24105  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24106  */
24107  
24108 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24109 {
24110     
24111     Roo.apply(this, config);
24112     
24113     // default disabled, based on 'good practice'..
24114     this.disable = this.disable || {};
24115     Roo.applyIf(this.disable, {
24116         fontSize : true,
24117         colors : true,
24118         specialElements : true
24119     });
24120     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24121     
24122     this.editor = config.editor;
24123     this.editorcore = config.editor.editorcore;
24124     
24125     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24126     
24127     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24128     // dont call parent... till later.
24129 }
24130 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24131      
24132     bar : true,
24133     
24134     editor : false,
24135     editorcore : false,
24136     
24137     
24138     formats : [
24139         "p" ,  
24140         "h1","h2","h3","h4","h5","h6", 
24141         "pre", "code", 
24142         "abbr", "acronym", "address", "cite", "samp", "var",
24143         'div','span'
24144     ],
24145     
24146     onRender : function(ct, position)
24147     {
24148        // Roo.log("Call onRender: " + this.xtype);
24149         
24150        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24151        Roo.log(this.el);
24152        this.el.dom.style.marginBottom = '0';
24153        var _this = this;
24154        var editorcore = this.editorcore;
24155        var editor= this.editor;
24156        
24157        var children = [];
24158        var btn = function(id,cmd , toggle, handler, html){
24159        
24160             var  event = toggle ? 'toggle' : 'click';
24161        
24162             var a = {
24163                 size : 'sm',
24164                 xtype: 'Button',
24165                 xns: Roo.bootstrap,
24166                 //glyphicon : id,
24167                 fa: id,
24168                 cmd : id || cmd,
24169                 enableToggle:toggle !== false,
24170                 html : html || '',
24171                 pressed : toggle ? false : null,
24172                 listeners : {}
24173             };
24174             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24175                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24176             };
24177             children.push(a);
24178             return a;
24179        }
24180        
24181     //    var cb_box = function...
24182         
24183         var style = {
24184                 xtype: 'Button',
24185                 size : 'sm',
24186                 xns: Roo.bootstrap,
24187                 fa : 'font',
24188                 //html : 'submit'
24189                 menu : {
24190                     xtype: 'Menu',
24191                     xns: Roo.bootstrap,
24192                     items:  []
24193                 }
24194         };
24195         Roo.each(this.formats, function(f) {
24196             style.menu.items.push({
24197                 xtype :'MenuItem',
24198                 xns: Roo.bootstrap,
24199                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24200                 tagname : f,
24201                 listeners : {
24202                     click : function()
24203                     {
24204                         editorcore.insertTag(this.tagname);
24205                         editor.focus();
24206                     }
24207                 }
24208                 
24209             });
24210         });
24211         children.push(style);   
24212         
24213         btn('bold',false,true);
24214         btn('italic',false,true);
24215         btn('align-left', 'justifyleft',true);
24216         btn('align-center', 'justifycenter',true);
24217         btn('align-right' , 'justifyright',true);
24218         btn('link', false, false, function(btn) {
24219             //Roo.log("create link?");
24220             var url = prompt(this.createLinkText, this.defaultLinkValue);
24221             if(url && url != 'http:/'+'/'){
24222                 this.editorcore.relayCmd('createlink', url);
24223             }
24224         }),
24225         btn('list','insertunorderedlist',true);
24226         btn('pencil', false,true, function(btn){
24227                 Roo.log(this);
24228                 this.toggleSourceEdit(btn.pressed);
24229         });
24230         
24231         if (this.editor.btns.length > 0) {
24232             for (var i = 0; i<this.editor.btns.length; i++) {
24233                 children.push(this.editor.btns[i]);
24234             }
24235         }
24236         
24237         /*
24238         var cog = {
24239                 xtype: 'Button',
24240                 size : 'sm',
24241                 xns: Roo.bootstrap,
24242                 glyphicon : 'cog',
24243                 //html : 'submit'
24244                 menu : {
24245                     xtype: 'Menu',
24246                     xns: Roo.bootstrap,
24247                     items:  []
24248                 }
24249         };
24250         
24251         cog.menu.items.push({
24252             xtype :'MenuItem',
24253             xns: Roo.bootstrap,
24254             html : Clean styles,
24255             tagname : f,
24256             listeners : {
24257                 click : function()
24258                 {
24259                     editorcore.insertTag(this.tagname);
24260                     editor.focus();
24261                 }
24262             }
24263             
24264         });
24265        */
24266         
24267          
24268        this.xtype = 'NavSimplebar';
24269         
24270         for(var i=0;i< children.length;i++) {
24271             
24272             this.buttons.add(this.addxtypeChild(children[i]));
24273             
24274         }
24275         
24276         editor.on('editorevent', this.updateToolbar, this);
24277     },
24278     onBtnClick : function(id)
24279     {
24280        this.editorcore.relayCmd(id);
24281        this.editorcore.focus();
24282     },
24283     
24284     /**
24285      * Protected method that will not generally be called directly. It triggers
24286      * a toolbar update by reading the markup state of the current selection in the editor.
24287      */
24288     updateToolbar: function(){
24289
24290         if(!this.editorcore.activated){
24291             this.editor.onFirstFocus(); // is this neeed?
24292             return;
24293         }
24294
24295         var btns = this.buttons; 
24296         var doc = this.editorcore.doc;
24297         btns.get('bold').setActive(doc.queryCommandState('bold'));
24298         btns.get('italic').setActive(doc.queryCommandState('italic'));
24299         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24300         
24301         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24302         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24303         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24304         
24305         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24306         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24307          /*
24308         
24309         var ans = this.editorcore.getAllAncestors();
24310         if (this.formatCombo) {
24311             
24312             
24313             var store = this.formatCombo.store;
24314             this.formatCombo.setValue("");
24315             for (var i =0; i < ans.length;i++) {
24316                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24317                     // select it..
24318                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24319                     break;
24320                 }
24321             }
24322         }
24323         
24324         
24325         
24326         // hides menus... - so this cant be on a menu...
24327         Roo.bootstrap.MenuMgr.hideAll();
24328         */
24329         Roo.bootstrap.MenuMgr.hideAll();
24330         //this.editorsyncValue();
24331     },
24332     onFirstFocus: function() {
24333         this.buttons.each(function(item){
24334            item.enable();
24335         });
24336     },
24337     toggleSourceEdit : function(sourceEditMode){
24338         
24339           
24340         if(sourceEditMode){
24341             Roo.log("disabling buttons");
24342            this.buttons.each( function(item){
24343                 if(item.cmd != 'pencil'){
24344                     item.disable();
24345                 }
24346             });
24347           
24348         }else{
24349             Roo.log("enabling buttons");
24350             if(this.editorcore.initialized){
24351                 this.buttons.each( function(item){
24352                     item.enable();
24353                 });
24354             }
24355             
24356         }
24357         Roo.log("calling toggole on editor");
24358         // tell the editor that it's been pressed..
24359         this.editor.toggleSourceEdit(sourceEditMode);
24360        
24361     }
24362 });
24363
24364
24365
24366
24367
24368 /**
24369  * @class Roo.bootstrap.Table.AbstractSelectionModel
24370  * @extends Roo.util.Observable
24371  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24372  * implemented by descendant classes.  This class should not be directly instantiated.
24373  * @constructor
24374  */
24375 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24376     this.locked = false;
24377     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24378 };
24379
24380
24381 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24382     /** @ignore Called by the grid automatically. Do not call directly. */
24383     init : function(grid){
24384         this.grid = grid;
24385         this.initEvents();
24386     },
24387
24388     /**
24389      * Locks the selections.
24390      */
24391     lock : function(){
24392         this.locked = true;
24393     },
24394
24395     /**
24396      * Unlocks the selections.
24397      */
24398     unlock : function(){
24399         this.locked = false;
24400     },
24401
24402     /**
24403      * Returns true if the selections are locked.
24404      * @return {Boolean}
24405      */
24406     isLocked : function(){
24407         return this.locked;
24408     }
24409 });
24410 /**
24411  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24412  * @class Roo.bootstrap.Table.RowSelectionModel
24413  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24414  * It supports multiple selections and keyboard selection/navigation. 
24415  * @constructor
24416  * @param {Object} config
24417  */
24418
24419 Roo.bootstrap.Table.RowSelectionModel = function(config){
24420     Roo.apply(this, config);
24421     this.selections = new Roo.util.MixedCollection(false, function(o){
24422         return o.id;
24423     });
24424
24425     this.last = false;
24426     this.lastActive = false;
24427
24428     this.addEvents({
24429         /**
24430              * @event selectionchange
24431              * Fires when the selection changes
24432              * @param {SelectionModel} this
24433              */
24434             "selectionchange" : true,
24435         /**
24436              * @event afterselectionchange
24437              * Fires after the selection changes (eg. by key press or clicking)
24438              * @param {SelectionModel} this
24439              */
24440             "afterselectionchange" : true,
24441         /**
24442              * @event beforerowselect
24443              * Fires when a row is selected being selected, return false to cancel.
24444              * @param {SelectionModel} this
24445              * @param {Number} rowIndex The selected index
24446              * @param {Boolean} keepExisting False if other selections will be cleared
24447              */
24448             "beforerowselect" : true,
24449         /**
24450              * @event rowselect
24451              * Fires when a row is selected.
24452              * @param {SelectionModel} this
24453              * @param {Number} rowIndex The selected index
24454              * @param {Roo.data.Record} r The record
24455              */
24456             "rowselect" : true,
24457         /**
24458              * @event rowdeselect
24459              * Fires when a row is deselected.
24460              * @param {SelectionModel} this
24461              * @param {Number} rowIndex The selected index
24462              */
24463         "rowdeselect" : true
24464     });
24465     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24466     this.locked = false;
24467  };
24468
24469 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24470     /**
24471      * @cfg {Boolean} singleSelect
24472      * True to allow selection of only one row at a time (defaults to false)
24473      */
24474     singleSelect : false,
24475
24476     // private
24477     initEvents : function()
24478     {
24479
24480         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24481         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24482         //}else{ // allow click to work like normal
24483          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24484         //}
24485         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24486         this.grid.on("rowclick", this.handleMouseDown, this);
24487         
24488         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24489             "up" : function(e){
24490                 if(!e.shiftKey){
24491                     this.selectPrevious(e.shiftKey);
24492                 }else if(this.last !== false && this.lastActive !== false){
24493                     var last = this.last;
24494                     this.selectRange(this.last,  this.lastActive-1);
24495                     this.grid.getView().focusRow(this.lastActive);
24496                     if(last !== false){
24497                         this.last = last;
24498                     }
24499                 }else{
24500                     this.selectFirstRow();
24501                 }
24502                 this.fireEvent("afterselectionchange", this);
24503             },
24504             "down" : function(e){
24505                 if(!e.shiftKey){
24506                     this.selectNext(e.shiftKey);
24507                 }else if(this.last !== false && this.lastActive !== false){
24508                     var last = this.last;
24509                     this.selectRange(this.last,  this.lastActive+1);
24510                     this.grid.getView().focusRow(this.lastActive);
24511                     if(last !== false){
24512                         this.last = last;
24513                     }
24514                 }else{
24515                     this.selectFirstRow();
24516                 }
24517                 this.fireEvent("afterselectionchange", this);
24518             },
24519             scope: this
24520         });
24521         this.grid.store.on('load', function(){
24522             this.selections.clear();
24523         },this);
24524         /*
24525         var view = this.grid.view;
24526         view.on("refresh", this.onRefresh, this);
24527         view.on("rowupdated", this.onRowUpdated, this);
24528         view.on("rowremoved", this.onRemove, this);
24529         */
24530     },
24531
24532     // private
24533     onRefresh : function()
24534     {
24535         var ds = this.grid.store, i, v = this.grid.view;
24536         var s = this.selections;
24537         s.each(function(r){
24538             if((i = ds.indexOfId(r.id)) != -1){
24539                 v.onRowSelect(i);
24540             }else{
24541                 s.remove(r);
24542             }
24543         });
24544     },
24545
24546     // private
24547     onRemove : function(v, index, r){
24548         this.selections.remove(r);
24549     },
24550
24551     // private
24552     onRowUpdated : function(v, index, r){
24553         if(this.isSelected(r)){
24554             v.onRowSelect(index);
24555         }
24556     },
24557
24558     /**
24559      * Select records.
24560      * @param {Array} records The records to select
24561      * @param {Boolean} keepExisting (optional) True to keep existing selections
24562      */
24563     selectRecords : function(records, keepExisting)
24564     {
24565         if(!keepExisting){
24566             this.clearSelections();
24567         }
24568             var ds = this.grid.store;
24569         for(var i = 0, len = records.length; i < len; i++){
24570             this.selectRow(ds.indexOf(records[i]), true);
24571         }
24572     },
24573
24574     /**
24575      * Gets the number of selected rows.
24576      * @return {Number}
24577      */
24578     getCount : function(){
24579         return this.selections.length;
24580     },
24581
24582     /**
24583      * Selects the first row in the grid.
24584      */
24585     selectFirstRow : function(){
24586         this.selectRow(0);
24587     },
24588
24589     /**
24590      * Select the last row.
24591      * @param {Boolean} keepExisting (optional) True to keep existing selections
24592      */
24593     selectLastRow : function(keepExisting){
24594         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24595         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24596     },
24597
24598     /**
24599      * Selects the row immediately following the last selected row.
24600      * @param {Boolean} keepExisting (optional) True to keep existing selections
24601      */
24602     selectNext : function(keepExisting)
24603     {
24604             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24605             this.selectRow(this.last+1, keepExisting);
24606             this.grid.getView().focusRow(this.last);
24607         }
24608     },
24609
24610     /**
24611      * Selects the row that precedes the last selected row.
24612      * @param {Boolean} keepExisting (optional) True to keep existing selections
24613      */
24614     selectPrevious : function(keepExisting){
24615         if(this.last){
24616             this.selectRow(this.last-1, keepExisting);
24617             this.grid.getView().focusRow(this.last);
24618         }
24619     },
24620
24621     /**
24622      * Returns the selected records
24623      * @return {Array} Array of selected records
24624      */
24625     getSelections : function(){
24626         return [].concat(this.selections.items);
24627     },
24628
24629     /**
24630      * Returns the first selected record.
24631      * @return {Record}
24632      */
24633     getSelected : function(){
24634         return this.selections.itemAt(0);
24635     },
24636
24637
24638     /**
24639      * Clears all selections.
24640      */
24641     clearSelections : function(fast)
24642     {
24643         if(this.locked) {
24644             return;
24645         }
24646         if(fast !== true){
24647                 var ds = this.grid.store;
24648             var s = this.selections;
24649             s.each(function(r){
24650                 this.deselectRow(ds.indexOfId(r.id));
24651             }, this);
24652             s.clear();
24653         }else{
24654             this.selections.clear();
24655         }
24656         this.last = false;
24657     },
24658
24659
24660     /**
24661      * Selects all rows.
24662      */
24663     selectAll : function(){
24664         if(this.locked) {
24665             return;
24666         }
24667         this.selections.clear();
24668         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24669             this.selectRow(i, true);
24670         }
24671     },
24672
24673     /**
24674      * Returns True if there is a selection.
24675      * @return {Boolean}
24676      */
24677     hasSelection : function(){
24678         return this.selections.length > 0;
24679     },
24680
24681     /**
24682      * Returns True if the specified row is selected.
24683      * @param {Number/Record} record The record or index of the record to check
24684      * @return {Boolean}
24685      */
24686     isSelected : function(index){
24687             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24688         return (r && this.selections.key(r.id) ? true : false);
24689     },
24690
24691     /**
24692      * Returns True if the specified record id is selected.
24693      * @param {String} id The id of record to check
24694      * @return {Boolean}
24695      */
24696     isIdSelected : function(id){
24697         return (this.selections.key(id) ? true : false);
24698     },
24699
24700
24701     // private
24702     handleMouseDBClick : function(e, t){
24703         
24704     },
24705     // private
24706     handleMouseDown : function(e, t)
24707     {
24708             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24709         if(this.isLocked() || rowIndex < 0 ){
24710             return;
24711         };
24712         if(e.shiftKey && this.last !== false){
24713             var last = this.last;
24714             this.selectRange(last, rowIndex, e.ctrlKey);
24715             this.last = last; // reset the last
24716             t.focus();
24717     
24718         }else{
24719             var isSelected = this.isSelected(rowIndex);
24720             //Roo.log("select row:" + rowIndex);
24721             if(isSelected){
24722                 this.deselectRow(rowIndex);
24723             } else {
24724                         this.selectRow(rowIndex, true);
24725             }
24726     
24727             /*
24728                 if(e.button !== 0 && isSelected){
24729                 alert('rowIndex 2: ' + rowIndex);
24730                     view.focusRow(rowIndex);
24731                 }else if(e.ctrlKey && isSelected){
24732                     this.deselectRow(rowIndex);
24733                 }else if(!isSelected){
24734                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24735                     view.focusRow(rowIndex);
24736                 }
24737             */
24738         }
24739         this.fireEvent("afterselectionchange", this);
24740     },
24741     // private
24742     handleDragableRowClick :  function(grid, rowIndex, e) 
24743     {
24744         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24745             this.selectRow(rowIndex, false);
24746             grid.view.focusRow(rowIndex);
24747              this.fireEvent("afterselectionchange", this);
24748         }
24749     },
24750     
24751     /**
24752      * Selects multiple rows.
24753      * @param {Array} rows Array of the indexes of the row to select
24754      * @param {Boolean} keepExisting (optional) True to keep existing selections
24755      */
24756     selectRows : function(rows, keepExisting){
24757         if(!keepExisting){
24758             this.clearSelections();
24759         }
24760         for(var i = 0, len = rows.length; i < len; i++){
24761             this.selectRow(rows[i], true);
24762         }
24763     },
24764
24765     /**
24766      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24767      * @param {Number} startRow The index of the first row in the range
24768      * @param {Number} endRow The index of the last row in the range
24769      * @param {Boolean} keepExisting (optional) True to retain existing selections
24770      */
24771     selectRange : function(startRow, endRow, keepExisting){
24772         if(this.locked) {
24773             return;
24774         }
24775         if(!keepExisting){
24776             this.clearSelections();
24777         }
24778         if(startRow <= endRow){
24779             for(var i = startRow; i <= endRow; i++){
24780                 this.selectRow(i, true);
24781             }
24782         }else{
24783             for(var i = startRow; i >= endRow; i--){
24784                 this.selectRow(i, true);
24785             }
24786         }
24787     },
24788
24789     /**
24790      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24791      * @param {Number} startRow The index of the first row in the range
24792      * @param {Number} endRow The index of the last row in the range
24793      */
24794     deselectRange : function(startRow, endRow, preventViewNotify){
24795         if(this.locked) {
24796             return;
24797         }
24798         for(var i = startRow; i <= endRow; i++){
24799             this.deselectRow(i, preventViewNotify);
24800         }
24801     },
24802
24803     /**
24804      * Selects a row.
24805      * @param {Number} row The index of the row to select
24806      * @param {Boolean} keepExisting (optional) True to keep existing selections
24807      */
24808     selectRow : function(index, keepExisting, preventViewNotify)
24809     {
24810             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24811             return;
24812         }
24813         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24814             if(!keepExisting || this.singleSelect){
24815                 this.clearSelections();
24816             }
24817             
24818             var r = this.grid.store.getAt(index);
24819             //console.log('selectRow - record id :' + r.id);
24820             
24821             this.selections.add(r);
24822             this.last = this.lastActive = index;
24823             if(!preventViewNotify){
24824                 var proxy = new Roo.Element(
24825                                 this.grid.getRowDom(index)
24826                 );
24827                 proxy.addClass('bg-info info');
24828             }
24829             this.fireEvent("rowselect", this, index, r);
24830             this.fireEvent("selectionchange", this);
24831         }
24832     },
24833
24834     /**
24835      * Deselects a row.
24836      * @param {Number} row The index of the row to deselect
24837      */
24838     deselectRow : function(index, preventViewNotify)
24839     {
24840         if(this.locked) {
24841             return;
24842         }
24843         if(this.last == index){
24844             this.last = false;
24845         }
24846         if(this.lastActive == index){
24847             this.lastActive = false;
24848         }
24849         
24850         var r = this.grid.store.getAt(index);
24851         if (!r) {
24852             return;
24853         }
24854         
24855         this.selections.remove(r);
24856         //.console.log('deselectRow - record id :' + r.id);
24857         if(!preventViewNotify){
24858         
24859             var proxy = new Roo.Element(
24860                 this.grid.getRowDom(index)
24861             );
24862             proxy.removeClass('bg-info info');
24863         }
24864         this.fireEvent("rowdeselect", this, index);
24865         this.fireEvent("selectionchange", this);
24866     },
24867
24868     // private
24869     restoreLast : function(){
24870         if(this._last){
24871             this.last = this._last;
24872         }
24873     },
24874
24875     // private
24876     acceptsNav : function(row, col, cm){
24877         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24878     },
24879
24880     // private
24881     onEditorKey : function(field, e){
24882         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24883         if(k == e.TAB){
24884             e.stopEvent();
24885             ed.completeEdit();
24886             if(e.shiftKey){
24887                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24888             }else{
24889                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24890             }
24891         }else if(k == e.ENTER && !e.ctrlKey){
24892             e.stopEvent();
24893             ed.completeEdit();
24894             if(e.shiftKey){
24895                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24896             }else{
24897                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24898             }
24899         }else if(k == e.ESC){
24900             ed.cancelEdit();
24901         }
24902         if(newCell){
24903             g.startEditing(newCell[0], newCell[1]);
24904         }
24905     }
24906 });
24907 /*
24908  * Based on:
24909  * Ext JS Library 1.1.1
24910  * Copyright(c) 2006-2007, Ext JS, LLC.
24911  *
24912  * Originally Released Under LGPL - original licence link has changed is not relivant.
24913  *
24914  * Fork - LGPL
24915  * <script type="text/javascript">
24916  */
24917  
24918 /**
24919  * @class Roo.bootstrap.PagingToolbar
24920  * @extends Roo.bootstrap.NavSimplebar
24921  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24922  * @constructor
24923  * Create a new PagingToolbar
24924  * @param {Object} config The config object
24925  * @param {Roo.data.Store} store
24926  */
24927 Roo.bootstrap.PagingToolbar = function(config)
24928 {
24929     // old args format still supported... - xtype is prefered..
24930         // created from xtype...
24931     
24932     this.ds = config.dataSource;
24933     
24934     if (config.store && !this.ds) {
24935         this.store= Roo.factory(config.store, Roo.data);
24936         this.ds = this.store;
24937         this.ds.xmodule = this.xmodule || false;
24938     }
24939     
24940     this.toolbarItems = [];
24941     if (config.items) {
24942         this.toolbarItems = config.items;
24943     }
24944     
24945     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24946     
24947     this.cursor = 0;
24948     
24949     if (this.ds) { 
24950         this.bind(this.ds);
24951     }
24952     
24953     if (Roo.bootstrap.version == 4) {
24954         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24955     } else {
24956         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24957     }
24958     
24959 };
24960
24961 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24962     /**
24963      * @cfg {Roo.data.Store} dataSource
24964      * The underlying data store providing the paged data
24965      */
24966     /**
24967      * @cfg {String/HTMLElement/Element} container
24968      * container The id or element that will contain the toolbar
24969      */
24970     /**
24971      * @cfg {Boolean} displayInfo
24972      * True to display the displayMsg (defaults to false)
24973      */
24974     /**
24975      * @cfg {Number} pageSize
24976      * The number of records to display per page (defaults to 20)
24977      */
24978     pageSize: 20,
24979     /**
24980      * @cfg {String} displayMsg
24981      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24982      */
24983     displayMsg : 'Displaying {0} - {1} of {2}',
24984     /**
24985      * @cfg {String} emptyMsg
24986      * The message to display when no records are found (defaults to "No data to display")
24987      */
24988     emptyMsg : 'No data to display',
24989     /**
24990      * Customizable piece of the default paging text (defaults to "Page")
24991      * @type String
24992      */
24993     beforePageText : "Page",
24994     /**
24995      * Customizable piece of the default paging text (defaults to "of %0")
24996      * @type String
24997      */
24998     afterPageText : "of {0}",
24999     /**
25000      * Customizable piece of the default paging text (defaults to "First Page")
25001      * @type String
25002      */
25003     firstText : "First Page",
25004     /**
25005      * Customizable piece of the default paging text (defaults to "Previous Page")
25006      * @type String
25007      */
25008     prevText : "Previous Page",
25009     /**
25010      * Customizable piece of the default paging text (defaults to "Next Page")
25011      * @type String
25012      */
25013     nextText : "Next Page",
25014     /**
25015      * Customizable piece of the default paging text (defaults to "Last Page")
25016      * @type String
25017      */
25018     lastText : "Last Page",
25019     /**
25020      * Customizable piece of the default paging text (defaults to "Refresh")
25021      * @type String
25022      */
25023     refreshText : "Refresh",
25024
25025     buttons : false,
25026     // private
25027     onRender : function(ct, position) 
25028     {
25029         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25030         this.navgroup.parentId = this.id;
25031         this.navgroup.onRender(this.el, null);
25032         // add the buttons to the navgroup
25033         
25034         if(this.displayInfo){
25035             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25036             this.displayEl = this.el.select('.x-paging-info', true).first();
25037 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25038 //            this.displayEl = navel.el.select('span',true).first();
25039         }
25040         
25041         var _this = this;
25042         
25043         if(this.buttons){
25044             Roo.each(_this.buttons, function(e){ // this might need to use render????
25045                Roo.factory(e).render(_this.el);
25046             });
25047         }
25048             
25049         Roo.each(_this.toolbarItems, function(e) {
25050             _this.navgroup.addItem(e);
25051         });
25052         
25053         
25054         this.first = this.navgroup.addItem({
25055             tooltip: this.firstText,
25056             cls: "prev btn-outline-secondary",
25057             html : ' <i class="fa fa-step-backward"></i>',
25058             disabled: true,
25059             preventDefault: true,
25060             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25061         });
25062         
25063         this.prev =  this.navgroup.addItem({
25064             tooltip: this.prevText,
25065             cls: "prev btn-outline-secondary",
25066             html : ' <i class="fa fa-backward"></i>',
25067             disabled: true,
25068             preventDefault: true,
25069             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25070         });
25071     //this.addSeparator();
25072         
25073         
25074         var field = this.navgroup.addItem( {
25075             tagtype : 'span',
25076             cls : 'x-paging-position  btn-outline-secondary',
25077              disabled: true,
25078             html : this.beforePageText  +
25079                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25080                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25081          } ); //?? escaped?
25082         
25083         this.field = field.el.select('input', true).first();
25084         this.field.on("keydown", this.onPagingKeydown, this);
25085         this.field.on("focus", function(){this.dom.select();});
25086     
25087     
25088         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25089         //this.field.setHeight(18);
25090         //this.addSeparator();
25091         this.next = this.navgroup.addItem({
25092             tooltip: this.nextText,
25093             cls: "next btn-outline-secondary",
25094             html : ' <i class="fa fa-forward"></i>',
25095             disabled: true,
25096             preventDefault: true,
25097             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25098         });
25099         this.last = this.navgroup.addItem({
25100             tooltip: this.lastText,
25101             html : ' <i class="fa fa-step-forward"></i>',
25102             cls: "next btn-outline-secondary",
25103             disabled: true,
25104             preventDefault: true,
25105             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25106         });
25107     //this.addSeparator();
25108         this.loading = this.navgroup.addItem({
25109             tooltip: this.refreshText,
25110             cls: "btn-outline-secondary",
25111             html : ' <i class="fa fa-refresh"></i>',
25112             preventDefault: true,
25113             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25114         });
25115         
25116     },
25117
25118     // private
25119     updateInfo : function(){
25120         if(this.displayEl){
25121             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25122             var msg = count == 0 ?
25123                 this.emptyMsg :
25124                 String.format(
25125                     this.displayMsg,
25126                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25127                 );
25128             this.displayEl.update(msg);
25129         }
25130     },
25131
25132     // private
25133     onLoad : function(ds, r, o)
25134     {
25135         this.cursor = o.params.start ? o.params.start : 0;
25136         
25137         var d = this.getPageData(),
25138             ap = d.activePage,
25139             ps = d.pages;
25140         
25141         
25142         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25143         this.field.dom.value = ap;
25144         this.first.setDisabled(ap == 1);
25145         this.prev.setDisabled(ap == 1);
25146         this.next.setDisabled(ap == ps);
25147         this.last.setDisabled(ap == ps);
25148         this.loading.enable();
25149         this.updateInfo();
25150     },
25151
25152     // private
25153     getPageData : function(){
25154         var total = this.ds.getTotalCount();
25155         return {
25156             total : total,
25157             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25158             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25159         };
25160     },
25161
25162     // private
25163     onLoadError : function(){
25164         this.loading.enable();
25165     },
25166
25167     // private
25168     onPagingKeydown : function(e){
25169         var k = e.getKey();
25170         var d = this.getPageData();
25171         if(k == e.RETURN){
25172             var v = this.field.dom.value, pageNum;
25173             if(!v || isNaN(pageNum = parseInt(v, 10))){
25174                 this.field.dom.value = d.activePage;
25175                 return;
25176             }
25177             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25178             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25179             e.stopEvent();
25180         }
25181         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))
25182         {
25183           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25184           this.field.dom.value = pageNum;
25185           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25186           e.stopEvent();
25187         }
25188         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25189         {
25190           var v = this.field.dom.value, pageNum; 
25191           var increment = (e.shiftKey) ? 10 : 1;
25192           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25193                 increment *= -1;
25194           }
25195           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25196             this.field.dom.value = d.activePage;
25197             return;
25198           }
25199           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25200           {
25201             this.field.dom.value = parseInt(v, 10) + increment;
25202             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25203             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25204           }
25205           e.stopEvent();
25206         }
25207     },
25208
25209     // private
25210     beforeLoad : function(){
25211         if(this.loading){
25212             this.loading.disable();
25213         }
25214     },
25215
25216     // private
25217     onClick : function(which){
25218         
25219         var ds = this.ds;
25220         if (!ds) {
25221             return;
25222         }
25223         
25224         switch(which){
25225             case "first":
25226                 ds.load({params:{start: 0, limit: this.pageSize}});
25227             break;
25228             case "prev":
25229                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25230             break;
25231             case "next":
25232                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25233             break;
25234             case "last":
25235                 var total = ds.getTotalCount();
25236                 var extra = total % this.pageSize;
25237                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25238                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25239             break;
25240             case "refresh":
25241                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25242             break;
25243         }
25244     },
25245
25246     /**
25247      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25248      * @param {Roo.data.Store} store The data store to unbind
25249      */
25250     unbind : function(ds){
25251         ds.un("beforeload", this.beforeLoad, this);
25252         ds.un("load", this.onLoad, this);
25253         ds.un("loadexception", this.onLoadError, this);
25254         ds.un("remove", this.updateInfo, this);
25255         ds.un("add", this.updateInfo, this);
25256         this.ds = undefined;
25257     },
25258
25259     /**
25260      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25261      * @param {Roo.data.Store} store The data store to bind
25262      */
25263     bind : function(ds){
25264         ds.on("beforeload", this.beforeLoad, this);
25265         ds.on("load", this.onLoad, this);
25266         ds.on("loadexception", this.onLoadError, this);
25267         ds.on("remove", this.updateInfo, this);
25268         ds.on("add", this.updateInfo, this);
25269         this.ds = ds;
25270     }
25271 });/*
25272  * - LGPL
25273  *
25274  * element
25275  * 
25276  */
25277
25278 /**
25279  * @class Roo.bootstrap.MessageBar
25280  * @extends Roo.bootstrap.Component
25281  * Bootstrap MessageBar class
25282  * @cfg {String} html contents of the MessageBar
25283  * @cfg {String} weight (info | success | warning | danger) default info
25284  * @cfg {String} beforeClass insert the bar before the given class
25285  * @cfg {Boolean} closable (true | false) default false
25286  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25287  * 
25288  * @constructor
25289  * Create a new Element
25290  * @param {Object} config The config object
25291  */
25292
25293 Roo.bootstrap.MessageBar = function(config){
25294     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25295 };
25296
25297 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25298     
25299     html: '',
25300     weight: 'info',
25301     closable: false,
25302     fixed: false,
25303     beforeClass: 'bootstrap-sticky-wrap',
25304     
25305     getAutoCreate : function(){
25306         
25307         var cfg = {
25308             tag: 'div',
25309             cls: 'alert alert-dismissable alert-' + this.weight,
25310             cn: [
25311                 {
25312                     tag: 'span',
25313                     cls: 'message',
25314                     html: this.html || ''
25315                 }
25316             ]
25317         };
25318         
25319         if(this.fixed){
25320             cfg.cls += ' alert-messages-fixed';
25321         }
25322         
25323         if(this.closable){
25324             cfg.cn.push({
25325                 tag: 'button',
25326                 cls: 'close',
25327                 html: 'x'
25328             });
25329         }
25330         
25331         return cfg;
25332     },
25333     
25334     onRender : function(ct, position)
25335     {
25336         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25337         
25338         if(!this.el){
25339             var cfg = Roo.apply({},  this.getAutoCreate());
25340             cfg.id = Roo.id();
25341             
25342             if (this.cls) {
25343                 cfg.cls += ' ' + this.cls;
25344             }
25345             if (this.style) {
25346                 cfg.style = this.style;
25347             }
25348             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25349             
25350             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25351         }
25352         
25353         this.el.select('>button.close').on('click', this.hide, this);
25354         
25355     },
25356     
25357     show : function()
25358     {
25359         if (!this.rendered) {
25360             this.render();
25361         }
25362         
25363         this.el.show();
25364         
25365         this.fireEvent('show', this);
25366         
25367     },
25368     
25369     hide : function()
25370     {
25371         if (!this.rendered) {
25372             this.render();
25373         }
25374         
25375         this.el.hide();
25376         
25377         this.fireEvent('hide', this);
25378     },
25379     
25380     update : function()
25381     {
25382 //        var e = this.el.dom.firstChild;
25383 //        
25384 //        if(this.closable){
25385 //            e = e.nextSibling;
25386 //        }
25387 //        
25388 //        e.data = this.html || '';
25389
25390         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25391     }
25392    
25393 });
25394
25395  
25396
25397      /*
25398  * - LGPL
25399  *
25400  * Graph
25401  * 
25402  */
25403
25404
25405 /**
25406  * @class Roo.bootstrap.Graph
25407  * @extends Roo.bootstrap.Component
25408  * Bootstrap Graph class
25409 > Prameters
25410  -sm {number} sm 4
25411  -md {number} md 5
25412  @cfg {String} graphtype  bar | vbar | pie
25413  @cfg {number} g_x coodinator | centre x (pie)
25414  @cfg {number} g_y coodinator | centre y (pie)
25415  @cfg {number} g_r radius (pie)
25416  @cfg {number} g_height height of the chart (respected by all elements in the set)
25417  @cfg {number} g_width width of the chart (respected by all elements in the set)
25418  @cfg {Object} title The title of the chart
25419     
25420  -{Array}  values
25421  -opts (object) options for the chart 
25422      o {
25423      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25424      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25425      o vgutter (number)
25426      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.
25427      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25428      o to
25429      o stretch (boolean)
25430      o }
25431  -opts (object) options for the pie
25432      o{
25433      o cut
25434      o startAngle (number)
25435      o endAngle (number)
25436      } 
25437  *
25438  * @constructor
25439  * Create a new Input
25440  * @param {Object} config The config object
25441  */
25442
25443 Roo.bootstrap.Graph = function(config){
25444     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25445     
25446     this.addEvents({
25447         // img events
25448         /**
25449          * @event click
25450          * The img click event for the img.
25451          * @param {Roo.EventObject} e
25452          */
25453         "click" : true
25454     });
25455 };
25456
25457 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25458     
25459     sm: 4,
25460     md: 5,
25461     graphtype: 'bar',
25462     g_height: 250,
25463     g_width: 400,
25464     g_x: 50,
25465     g_y: 50,
25466     g_r: 30,
25467     opts:{
25468         //g_colors: this.colors,
25469         g_type: 'soft',
25470         g_gutter: '20%'
25471
25472     },
25473     title : false,
25474
25475     getAutoCreate : function(){
25476         
25477         var cfg = {
25478             tag: 'div',
25479             html : null
25480         };
25481         
25482         
25483         return  cfg;
25484     },
25485
25486     onRender : function(ct,position){
25487         
25488         
25489         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25490         
25491         if (typeof(Raphael) == 'undefined') {
25492             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25493             return;
25494         }
25495         
25496         this.raphael = Raphael(this.el.dom);
25497         
25498                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25499                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25500                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25501                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25502                 /*
25503                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25504                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25505                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25506                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25507                 
25508                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25509                 r.barchart(330, 10, 300, 220, data1);
25510                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25511                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25512                 */
25513                 
25514                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25515                 // r.barchart(30, 30, 560, 250,  xdata, {
25516                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25517                 //     axis : "0 0 1 1",
25518                 //     axisxlabels :  xdata
25519                 //     //yvalues : cols,
25520                    
25521                 // });
25522 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25523 //        
25524 //        this.load(null,xdata,{
25525 //                axis : "0 0 1 1",
25526 //                axisxlabels :  xdata
25527 //                });
25528
25529     },
25530
25531     load : function(graphtype,xdata,opts)
25532     {
25533         this.raphael.clear();
25534         if(!graphtype) {
25535             graphtype = this.graphtype;
25536         }
25537         if(!opts){
25538             opts = this.opts;
25539         }
25540         var r = this.raphael,
25541             fin = function () {
25542                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25543             },
25544             fout = function () {
25545                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25546             },
25547             pfin = function() {
25548                 this.sector.stop();
25549                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25550
25551                 if (this.label) {
25552                     this.label[0].stop();
25553                     this.label[0].attr({ r: 7.5 });
25554                     this.label[1].attr({ "font-weight": 800 });
25555                 }
25556             },
25557             pfout = function() {
25558                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25559
25560                 if (this.label) {
25561                     this.label[0].animate({ r: 5 }, 500, "bounce");
25562                     this.label[1].attr({ "font-weight": 400 });
25563                 }
25564             };
25565
25566         switch(graphtype){
25567             case 'bar':
25568                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25569                 break;
25570             case 'hbar':
25571                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25572                 break;
25573             case 'pie':
25574 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25575 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25576 //            
25577                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25578                 
25579                 break;
25580
25581         }
25582         
25583         if(this.title){
25584             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25585         }
25586         
25587     },
25588     
25589     setTitle: function(o)
25590     {
25591         this.title = o;
25592     },
25593     
25594     initEvents: function() {
25595         
25596         if(!this.href){
25597             this.el.on('click', this.onClick, this);
25598         }
25599     },
25600     
25601     onClick : function(e)
25602     {
25603         Roo.log('img onclick');
25604         this.fireEvent('click', this, e);
25605     }
25606    
25607 });
25608
25609  
25610 /*
25611  * - LGPL
25612  *
25613  * numberBox
25614  * 
25615  */
25616 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25617
25618 /**
25619  * @class Roo.bootstrap.dash.NumberBox
25620  * @extends Roo.bootstrap.Component
25621  * Bootstrap NumberBox class
25622  * @cfg {String} headline Box headline
25623  * @cfg {String} content Box content
25624  * @cfg {String} icon Box icon
25625  * @cfg {String} footer Footer text
25626  * @cfg {String} fhref Footer href
25627  * 
25628  * @constructor
25629  * Create a new NumberBox
25630  * @param {Object} config The config object
25631  */
25632
25633
25634 Roo.bootstrap.dash.NumberBox = function(config){
25635     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25636     
25637 };
25638
25639 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25640     
25641     headline : '',
25642     content : '',
25643     icon : '',
25644     footer : '',
25645     fhref : '',
25646     ficon : '',
25647     
25648     getAutoCreate : function(){
25649         
25650         var cfg = {
25651             tag : 'div',
25652             cls : 'small-box ',
25653             cn : [
25654                 {
25655                     tag : 'div',
25656                     cls : 'inner',
25657                     cn :[
25658                         {
25659                             tag : 'h3',
25660                             cls : 'roo-headline',
25661                             html : this.headline
25662                         },
25663                         {
25664                             tag : 'p',
25665                             cls : 'roo-content',
25666                             html : this.content
25667                         }
25668                     ]
25669                 }
25670             ]
25671         };
25672         
25673         if(this.icon){
25674             cfg.cn.push({
25675                 tag : 'div',
25676                 cls : 'icon',
25677                 cn :[
25678                     {
25679                         tag : 'i',
25680                         cls : 'ion ' + this.icon
25681                     }
25682                 ]
25683             });
25684         }
25685         
25686         if(this.footer){
25687             var footer = {
25688                 tag : 'a',
25689                 cls : 'small-box-footer',
25690                 href : this.fhref || '#',
25691                 html : this.footer
25692             };
25693             
25694             cfg.cn.push(footer);
25695             
25696         }
25697         
25698         return  cfg;
25699     },
25700
25701     onRender : function(ct,position){
25702         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25703
25704
25705        
25706                 
25707     },
25708
25709     setHeadline: function (value)
25710     {
25711         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25712     },
25713     
25714     setFooter: function (value, href)
25715     {
25716         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25717         
25718         if(href){
25719             this.el.select('a.small-box-footer',true).first().attr('href', href);
25720         }
25721         
25722     },
25723
25724     setContent: function (value)
25725     {
25726         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25727     },
25728
25729     initEvents: function() 
25730     {   
25731         
25732     }
25733     
25734 });
25735
25736  
25737 /*
25738  * - LGPL
25739  *
25740  * TabBox
25741  * 
25742  */
25743 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25744
25745 /**
25746  * @class Roo.bootstrap.dash.TabBox
25747  * @extends Roo.bootstrap.Component
25748  * Bootstrap TabBox class
25749  * @cfg {String} title Title of the TabBox
25750  * @cfg {String} icon Icon of the TabBox
25751  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25752  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25753  * 
25754  * @constructor
25755  * Create a new TabBox
25756  * @param {Object} config The config object
25757  */
25758
25759
25760 Roo.bootstrap.dash.TabBox = function(config){
25761     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25762     this.addEvents({
25763         // raw events
25764         /**
25765          * @event addpane
25766          * When a pane is added
25767          * @param {Roo.bootstrap.dash.TabPane} pane
25768          */
25769         "addpane" : true,
25770         /**
25771          * @event activatepane
25772          * When a pane is activated
25773          * @param {Roo.bootstrap.dash.TabPane} pane
25774          */
25775         "activatepane" : true
25776         
25777          
25778     });
25779     
25780     this.panes = [];
25781 };
25782
25783 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25784
25785     title : '',
25786     icon : false,
25787     showtabs : true,
25788     tabScrollable : false,
25789     
25790     getChildContainer : function()
25791     {
25792         return this.el.select('.tab-content', true).first();
25793     },
25794     
25795     getAutoCreate : function(){
25796         
25797         var header = {
25798             tag: 'li',
25799             cls: 'pull-left header',
25800             html: this.title,
25801             cn : []
25802         };
25803         
25804         if(this.icon){
25805             header.cn.push({
25806                 tag: 'i',
25807                 cls: 'fa ' + this.icon
25808             });
25809         }
25810         
25811         var h = {
25812             tag: 'ul',
25813             cls: 'nav nav-tabs pull-right',
25814             cn: [
25815                 header
25816             ]
25817         };
25818         
25819         if(this.tabScrollable){
25820             h = {
25821                 tag: 'div',
25822                 cls: 'tab-header',
25823                 cn: [
25824                     {
25825                         tag: 'ul',
25826                         cls: 'nav nav-tabs pull-right',
25827                         cn: [
25828                             header
25829                         ]
25830                     }
25831                 ]
25832             };
25833         }
25834         
25835         var cfg = {
25836             tag: 'div',
25837             cls: 'nav-tabs-custom',
25838             cn: [
25839                 h,
25840                 {
25841                     tag: 'div',
25842                     cls: 'tab-content no-padding',
25843                     cn: []
25844                 }
25845             ]
25846         };
25847
25848         return  cfg;
25849     },
25850     initEvents : function()
25851     {
25852         //Roo.log('add add pane handler');
25853         this.on('addpane', this.onAddPane, this);
25854     },
25855      /**
25856      * Updates the box title
25857      * @param {String} html to set the title to.
25858      */
25859     setTitle : function(value)
25860     {
25861         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25862     },
25863     onAddPane : function(pane)
25864     {
25865         this.panes.push(pane);
25866         //Roo.log('addpane');
25867         //Roo.log(pane);
25868         // tabs are rendere left to right..
25869         if(!this.showtabs){
25870             return;
25871         }
25872         
25873         var ctr = this.el.select('.nav-tabs', true).first();
25874          
25875          
25876         var existing = ctr.select('.nav-tab',true);
25877         var qty = existing.getCount();;
25878         
25879         
25880         var tab = ctr.createChild({
25881             tag : 'li',
25882             cls : 'nav-tab' + (qty ? '' : ' active'),
25883             cn : [
25884                 {
25885                     tag : 'a',
25886                     href:'#',
25887                     html : pane.title
25888                 }
25889             ]
25890         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25891         pane.tab = tab;
25892         
25893         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25894         if (!qty) {
25895             pane.el.addClass('active');
25896         }
25897         
25898                 
25899     },
25900     onTabClick : function(ev,un,ob,pane)
25901     {
25902         //Roo.log('tab - prev default');
25903         ev.preventDefault();
25904         
25905         
25906         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25907         pane.tab.addClass('active');
25908         //Roo.log(pane.title);
25909         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25910         // technically we should have a deactivate event.. but maybe add later.
25911         // and it should not de-activate the selected tab...
25912         this.fireEvent('activatepane', pane);
25913         pane.el.addClass('active');
25914         pane.fireEvent('activate');
25915         
25916         
25917     },
25918     
25919     getActivePane : function()
25920     {
25921         var r = false;
25922         Roo.each(this.panes, function(p) {
25923             if(p.el.hasClass('active')){
25924                 r = p;
25925                 return false;
25926             }
25927             
25928             return;
25929         });
25930         
25931         return r;
25932     }
25933     
25934     
25935 });
25936
25937  
25938 /*
25939  * - LGPL
25940  *
25941  * Tab pane
25942  * 
25943  */
25944 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25945 /**
25946  * @class Roo.bootstrap.TabPane
25947  * @extends Roo.bootstrap.Component
25948  * Bootstrap TabPane class
25949  * @cfg {Boolean} active (false | true) Default false
25950  * @cfg {String} title title of panel
25951
25952  * 
25953  * @constructor
25954  * Create a new TabPane
25955  * @param {Object} config The config object
25956  */
25957
25958 Roo.bootstrap.dash.TabPane = function(config){
25959     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25960     
25961     this.addEvents({
25962         // raw events
25963         /**
25964          * @event activate
25965          * When a pane is activated
25966          * @param {Roo.bootstrap.dash.TabPane} pane
25967          */
25968         "activate" : true
25969          
25970     });
25971 };
25972
25973 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25974     
25975     active : false,
25976     title : '',
25977     
25978     // the tabBox that this is attached to.
25979     tab : false,
25980      
25981     getAutoCreate : function() 
25982     {
25983         var cfg = {
25984             tag: 'div',
25985             cls: 'tab-pane'
25986         };
25987         
25988         if(this.active){
25989             cfg.cls += ' active';
25990         }
25991         
25992         return cfg;
25993     },
25994     initEvents  : function()
25995     {
25996         //Roo.log('trigger add pane handler');
25997         this.parent().fireEvent('addpane', this)
25998     },
25999     
26000      /**
26001      * Updates the tab title 
26002      * @param {String} html to set the title to.
26003      */
26004     setTitle: function(str)
26005     {
26006         if (!this.tab) {
26007             return;
26008         }
26009         this.title = str;
26010         this.tab.select('a', true).first().dom.innerHTML = str;
26011         
26012     }
26013     
26014     
26015     
26016 });
26017
26018  
26019
26020
26021  /*
26022  * - LGPL
26023  *
26024  * menu
26025  * 
26026  */
26027 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26028
26029 /**
26030  * @class Roo.bootstrap.menu.Menu
26031  * @extends Roo.bootstrap.Component
26032  * Bootstrap Menu class - container for Menu
26033  * @cfg {String} html Text of the menu
26034  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26035  * @cfg {String} icon Font awesome icon
26036  * @cfg {String} pos Menu align to (top | bottom) default bottom
26037  * 
26038  * 
26039  * @constructor
26040  * Create a new Menu
26041  * @param {Object} config The config object
26042  */
26043
26044
26045 Roo.bootstrap.menu.Menu = function(config){
26046     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26047     
26048     this.addEvents({
26049         /**
26050          * @event beforeshow
26051          * Fires before this menu is displayed
26052          * @param {Roo.bootstrap.menu.Menu} this
26053          */
26054         beforeshow : true,
26055         /**
26056          * @event beforehide
26057          * Fires before this menu is hidden
26058          * @param {Roo.bootstrap.menu.Menu} this
26059          */
26060         beforehide : true,
26061         /**
26062          * @event show
26063          * Fires after this menu is displayed
26064          * @param {Roo.bootstrap.menu.Menu} this
26065          */
26066         show : true,
26067         /**
26068          * @event hide
26069          * Fires after this menu is hidden
26070          * @param {Roo.bootstrap.menu.Menu} this
26071          */
26072         hide : true,
26073         /**
26074          * @event click
26075          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26076          * @param {Roo.bootstrap.menu.Menu} this
26077          * @param {Roo.EventObject} e
26078          */
26079         click : true
26080     });
26081     
26082 };
26083
26084 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26085     
26086     submenu : false,
26087     html : '',
26088     weight : 'default',
26089     icon : false,
26090     pos : 'bottom',
26091     
26092     
26093     getChildContainer : function() {
26094         if(this.isSubMenu){
26095             return this.el;
26096         }
26097         
26098         return this.el.select('ul.dropdown-menu', true).first();  
26099     },
26100     
26101     getAutoCreate : function()
26102     {
26103         var text = [
26104             {
26105                 tag : 'span',
26106                 cls : 'roo-menu-text',
26107                 html : this.html
26108             }
26109         ];
26110         
26111         if(this.icon){
26112             text.unshift({
26113                 tag : 'i',
26114                 cls : 'fa ' + this.icon
26115             })
26116         }
26117         
26118         
26119         var cfg = {
26120             tag : 'div',
26121             cls : 'btn-group',
26122             cn : [
26123                 {
26124                     tag : 'button',
26125                     cls : 'dropdown-button btn btn-' + this.weight,
26126                     cn : text
26127                 },
26128                 {
26129                     tag : 'button',
26130                     cls : 'dropdown-toggle btn btn-' + this.weight,
26131                     cn : [
26132                         {
26133                             tag : 'span',
26134                             cls : 'caret'
26135                         }
26136                     ]
26137                 },
26138                 {
26139                     tag : 'ul',
26140                     cls : 'dropdown-menu'
26141                 }
26142             ]
26143             
26144         };
26145         
26146         if(this.pos == 'top'){
26147             cfg.cls += ' dropup';
26148         }
26149         
26150         if(this.isSubMenu){
26151             cfg = {
26152                 tag : 'ul',
26153                 cls : 'dropdown-menu'
26154             }
26155         }
26156         
26157         return cfg;
26158     },
26159     
26160     onRender : function(ct, position)
26161     {
26162         this.isSubMenu = ct.hasClass('dropdown-submenu');
26163         
26164         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26165     },
26166     
26167     initEvents : function() 
26168     {
26169         if(this.isSubMenu){
26170             return;
26171         }
26172         
26173         this.hidden = true;
26174         
26175         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26176         this.triggerEl.on('click', this.onTriggerPress, this);
26177         
26178         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26179         this.buttonEl.on('click', this.onClick, this);
26180         
26181     },
26182     
26183     list : function()
26184     {
26185         if(this.isSubMenu){
26186             return this.el;
26187         }
26188         
26189         return this.el.select('ul.dropdown-menu', true).first();
26190     },
26191     
26192     onClick : function(e)
26193     {
26194         this.fireEvent("click", this, e);
26195     },
26196     
26197     onTriggerPress  : function(e)
26198     {   
26199         if (this.isVisible()) {
26200             this.hide();
26201         } else {
26202             this.show();
26203         }
26204     },
26205     
26206     isVisible : function(){
26207         return !this.hidden;
26208     },
26209     
26210     show : function()
26211     {
26212         this.fireEvent("beforeshow", this);
26213         
26214         this.hidden = false;
26215         this.el.addClass('open');
26216         
26217         Roo.get(document).on("mouseup", this.onMouseUp, this);
26218         
26219         this.fireEvent("show", this);
26220         
26221         
26222     },
26223     
26224     hide : function()
26225     {
26226         this.fireEvent("beforehide", this);
26227         
26228         this.hidden = true;
26229         this.el.removeClass('open');
26230         
26231         Roo.get(document).un("mouseup", this.onMouseUp);
26232         
26233         this.fireEvent("hide", this);
26234     },
26235     
26236     onMouseUp : function()
26237     {
26238         this.hide();
26239     }
26240     
26241 });
26242
26243  
26244  /*
26245  * - LGPL
26246  *
26247  * menu item
26248  * 
26249  */
26250 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26251
26252 /**
26253  * @class Roo.bootstrap.menu.Item
26254  * @extends Roo.bootstrap.Component
26255  * Bootstrap MenuItem class
26256  * @cfg {Boolean} submenu (true | false) default false
26257  * @cfg {String} html text of the item
26258  * @cfg {String} href the link
26259  * @cfg {Boolean} disable (true | false) default false
26260  * @cfg {Boolean} preventDefault (true | false) default true
26261  * @cfg {String} icon Font awesome icon
26262  * @cfg {String} pos Submenu align to (left | right) default right 
26263  * 
26264  * 
26265  * @constructor
26266  * Create a new Item
26267  * @param {Object} config The config object
26268  */
26269
26270
26271 Roo.bootstrap.menu.Item = function(config){
26272     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26273     this.addEvents({
26274         /**
26275          * @event mouseover
26276          * Fires when the mouse is hovering over this menu
26277          * @param {Roo.bootstrap.menu.Item} this
26278          * @param {Roo.EventObject} e
26279          */
26280         mouseover : true,
26281         /**
26282          * @event mouseout
26283          * Fires when the mouse exits this menu
26284          * @param {Roo.bootstrap.menu.Item} this
26285          * @param {Roo.EventObject} e
26286          */
26287         mouseout : true,
26288         // raw events
26289         /**
26290          * @event click
26291          * The raw click event for the entire grid.
26292          * @param {Roo.EventObject} e
26293          */
26294         click : true
26295     });
26296 };
26297
26298 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26299     
26300     submenu : false,
26301     href : '',
26302     html : '',
26303     preventDefault: true,
26304     disable : false,
26305     icon : false,
26306     pos : 'right',
26307     
26308     getAutoCreate : function()
26309     {
26310         var text = [
26311             {
26312                 tag : 'span',
26313                 cls : 'roo-menu-item-text',
26314                 html : this.html
26315             }
26316         ];
26317         
26318         if(this.icon){
26319             text.unshift({
26320                 tag : 'i',
26321                 cls : 'fa ' + this.icon
26322             })
26323         }
26324         
26325         var cfg = {
26326             tag : 'li',
26327             cn : [
26328                 {
26329                     tag : 'a',
26330                     href : this.href || '#',
26331                     cn : text
26332                 }
26333             ]
26334         };
26335         
26336         if(this.disable){
26337             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26338         }
26339         
26340         if(this.submenu){
26341             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26342             
26343             if(this.pos == 'left'){
26344                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26345             }
26346         }
26347         
26348         return cfg;
26349     },
26350     
26351     initEvents : function() 
26352     {
26353         this.el.on('mouseover', this.onMouseOver, this);
26354         this.el.on('mouseout', this.onMouseOut, this);
26355         
26356         this.el.select('a', true).first().on('click', this.onClick, this);
26357         
26358     },
26359     
26360     onClick : function(e)
26361     {
26362         if(this.preventDefault){
26363             e.preventDefault();
26364         }
26365         
26366         this.fireEvent("click", this, e);
26367     },
26368     
26369     onMouseOver : function(e)
26370     {
26371         if(this.submenu && this.pos == 'left'){
26372             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26373         }
26374         
26375         this.fireEvent("mouseover", this, e);
26376     },
26377     
26378     onMouseOut : function(e)
26379     {
26380         this.fireEvent("mouseout", this, e);
26381     }
26382 });
26383
26384  
26385
26386  /*
26387  * - LGPL
26388  *
26389  * menu separator
26390  * 
26391  */
26392 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26393
26394 /**
26395  * @class Roo.bootstrap.menu.Separator
26396  * @extends Roo.bootstrap.Component
26397  * Bootstrap Separator class
26398  * 
26399  * @constructor
26400  * Create a new Separator
26401  * @param {Object} config The config object
26402  */
26403
26404
26405 Roo.bootstrap.menu.Separator = function(config){
26406     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26407 };
26408
26409 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26410     
26411     getAutoCreate : function(){
26412         var cfg = {
26413             tag : 'li',
26414             cls: 'divider'
26415         };
26416         
26417         return cfg;
26418     }
26419    
26420 });
26421
26422  
26423
26424  /*
26425  * - LGPL
26426  *
26427  * Tooltip
26428  * 
26429  */
26430
26431 /**
26432  * @class Roo.bootstrap.Tooltip
26433  * Bootstrap Tooltip class
26434  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26435  * to determine which dom element triggers the tooltip.
26436  * 
26437  * It needs to add support for additional attributes like tooltip-position
26438  * 
26439  * @constructor
26440  * Create a new Toolti
26441  * @param {Object} config The config object
26442  */
26443
26444 Roo.bootstrap.Tooltip = function(config){
26445     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26446     
26447     this.alignment = Roo.bootstrap.Tooltip.alignment;
26448     
26449     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26450         this.alignment = config.alignment;
26451     }
26452     
26453 };
26454
26455 Roo.apply(Roo.bootstrap.Tooltip, {
26456     /**
26457      * @function init initialize tooltip monitoring.
26458      * @static
26459      */
26460     currentEl : false,
26461     currentTip : false,
26462     currentRegion : false,
26463     
26464     //  init : delay?
26465     
26466     init : function()
26467     {
26468         Roo.get(document).on('mouseover', this.enter ,this);
26469         Roo.get(document).on('mouseout', this.leave, this);
26470          
26471         
26472         this.currentTip = new Roo.bootstrap.Tooltip();
26473     },
26474     
26475     enter : function(ev)
26476     {
26477         var dom = ev.getTarget();
26478         
26479         //Roo.log(['enter',dom]);
26480         var el = Roo.fly(dom);
26481         if (this.currentEl) {
26482             //Roo.log(dom);
26483             //Roo.log(this.currentEl);
26484             //Roo.log(this.currentEl.contains(dom));
26485             if (this.currentEl == el) {
26486                 return;
26487             }
26488             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26489                 return;
26490             }
26491
26492         }
26493         
26494         if (this.currentTip.el) {
26495             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26496         }    
26497         //Roo.log(ev);
26498         
26499         if(!el || el.dom == document){
26500             return;
26501         }
26502         
26503         var bindEl = el;
26504         
26505         // you can not look for children, as if el is the body.. then everythign is the child..
26506         if (!el.attr('tooltip')) { //
26507             if (!el.select("[tooltip]").elements.length) {
26508                 return;
26509             }
26510             // is the mouse over this child...?
26511             bindEl = el.select("[tooltip]").first();
26512             var xy = ev.getXY();
26513             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26514                 //Roo.log("not in region.");
26515                 return;
26516             }
26517             //Roo.log("child element over..");
26518             
26519         }
26520         this.currentEl = bindEl;
26521         this.currentTip.bind(bindEl);
26522         this.currentRegion = Roo.lib.Region.getRegion(dom);
26523         this.currentTip.enter();
26524         
26525     },
26526     leave : function(ev)
26527     {
26528         var dom = ev.getTarget();
26529         //Roo.log(['leave',dom]);
26530         if (!this.currentEl) {
26531             return;
26532         }
26533         
26534         
26535         if (dom != this.currentEl.dom) {
26536             return;
26537         }
26538         var xy = ev.getXY();
26539         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26540             return;
26541         }
26542         // only activate leave if mouse cursor is outside... bounding box..
26543         
26544         
26545         
26546         
26547         if (this.currentTip) {
26548             this.currentTip.leave();
26549         }
26550         //Roo.log('clear currentEl');
26551         this.currentEl = false;
26552         
26553         
26554     },
26555     alignment : {
26556         'left' : ['r-l', [-2,0], 'right'],
26557         'right' : ['l-r', [2,0], 'left'],
26558         'bottom' : ['t-b', [0,2], 'top'],
26559         'top' : [ 'b-t', [0,-2], 'bottom']
26560     }
26561     
26562 });
26563
26564
26565 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26566     
26567     
26568     bindEl : false,
26569     
26570     delay : null, // can be { show : 300 , hide: 500}
26571     
26572     timeout : null,
26573     
26574     hoverState : null, //???
26575     
26576     placement : 'bottom', 
26577     
26578     alignment : false,
26579     
26580     getAutoCreate : function(){
26581     
26582         var cfg = {
26583            cls : 'tooltip',
26584            role : 'tooltip',
26585            cn : [
26586                 {
26587                     cls : 'tooltip-arrow'
26588                 },
26589                 {
26590                     cls : 'tooltip-inner'
26591                 }
26592            ]
26593         };
26594         
26595         return cfg;
26596     },
26597     bind : function(el)
26598     {
26599         this.bindEl = el;
26600     },
26601       
26602     
26603     enter : function () {
26604        
26605         if (this.timeout != null) {
26606             clearTimeout(this.timeout);
26607         }
26608         
26609         this.hoverState = 'in';
26610          //Roo.log("enter - show");
26611         if (!this.delay || !this.delay.show) {
26612             this.show();
26613             return;
26614         }
26615         var _t = this;
26616         this.timeout = setTimeout(function () {
26617             if (_t.hoverState == 'in') {
26618                 _t.show();
26619             }
26620         }, this.delay.show);
26621     },
26622     leave : function()
26623     {
26624         clearTimeout(this.timeout);
26625     
26626         this.hoverState = 'out';
26627          if (!this.delay || !this.delay.hide) {
26628             this.hide();
26629             return;
26630         }
26631        
26632         var _t = this;
26633         this.timeout = setTimeout(function () {
26634             //Roo.log("leave - timeout");
26635             
26636             if (_t.hoverState == 'out') {
26637                 _t.hide();
26638                 Roo.bootstrap.Tooltip.currentEl = false;
26639             }
26640         }, delay);
26641     },
26642     
26643     show : function (msg)
26644     {
26645         if (!this.el) {
26646             this.render(document.body);
26647         }
26648         // set content.
26649         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26650         
26651         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26652         
26653         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26654         
26655         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26656         
26657         var placement = typeof this.placement == 'function' ?
26658             this.placement.call(this, this.el, on_el) :
26659             this.placement;
26660             
26661         var autoToken = /\s?auto?\s?/i;
26662         var autoPlace = autoToken.test(placement);
26663         if (autoPlace) {
26664             placement = placement.replace(autoToken, '') || 'top';
26665         }
26666         
26667         //this.el.detach()
26668         //this.el.setXY([0,0]);
26669         this.el.show();
26670         //this.el.dom.style.display='block';
26671         
26672         //this.el.appendTo(on_el);
26673         
26674         var p = this.getPosition();
26675         var box = this.el.getBox();
26676         
26677         if (autoPlace) {
26678             // fixme..
26679         }
26680         
26681         var align = this.alignment[placement];
26682         
26683         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26684         
26685         if(placement == 'top' || placement == 'bottom'){
26686             if(xy[0] < 0){
26687                 placement = 'right';
26688             }
26689             
26690             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26691                 placement = 'left';
26692             }
26693             
26694             var scroll = Roo.select('body', true).first().getScroll();
26695             
26696             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26697                 placement = 'top';
26698             }
26699             
26700             align = this.alignment[placement];
26701         }
26702         
26703         this.el.alignTo(this.bindEl, align[0],align[1]);
26704         //var arrow = this.el.select('.arrow',true).first();
26705         //arrow.set(align[2], 
26706         
26707         this.el.addClass(placement);
26708         
26709         this.el.addClass('in fade');
26710         
26711         this.hoverState = null;
26712         
26713         if (this.el.hasClass('fade')) {
26714             // fade it?
26715         }
26716         
26717     },
26718     hide : function()
26719     {
26720          
26721         if (!this.el) {
26722             return;
26723         }
26724         //this.el.setXY([0,0]);
26725         this.el.removeClass('in');
26726         //this.el.hide();
26727         
26728     }
26729     
26730 });
26731  
26732
26733  /*
26734  * - LGPL
26735  *
26736  * Location Picker
26737  * 
26738  */
26739
26740 /**
26741  * @class Roo.bootstrap.LocationPicker
26742  * @extends Roo.bootstrap.Component
26743  * Bootstrap LocationPicker class
26744  * @cfg {Number} latitude Position when init default 0
26745  * @cfg {Number} longitude Position when init default 0
26746  * @cfg {Number} zoom default 15
26747  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26748  * @cfg {Boolean} mapTypeControl default false
26749  * @cfg {Boolean} disableDoubleClickZoom default false
26750  * @cfg {Boolean} scrollwheel default true
26751  * @cfg {Boolean} streetViewControl default false
26752  * @cfg {Number} radius default 0
26753  * @cfg {String} locationName
26754  * @cfg {Boolean} draggable default true
26755  * @cfg {Boolean} enableAutocomplete default false
26756  * @cfg {Boolean} enableReverseGeocode default true
26757  * @cfg {String} markerTitle
26758  * 
26759  * @constructor
26760  * Create a new LocationPicker
26761  * @param {Object} config The config object
26762  */
26763
26764
26765 Roo.bootstrap.LocationPicker = function(config){
26766     
26767     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26768     
26769     this.addEvents({
26770         /**
26771          * @event initial
26772          * Fires when the picker initialized.
26773          * @param {Roo.bootstrap.LocationPicker} this
26774          * @param {Google Location} location
26775          */
26776         initial : true,
26777         /**
26778          * @event positionchanged
26779          * Fires when the picker position changed.
26780          * @param {Roo.bootstrap.LocationPicker} this
26781          * @param {Google Location} location
26782          */
26783         positionchanged : true,
26784         /**
26785          * @event resize
26786          * Fires when the map resize.
26787          * @param {Roo.bootstrap.LocationPicker} this
26788          */
26789         resize : true,
26790         /**
26791          * @event show
26792          * Fires when the map show.
26793          * @param {Roo.bootstrap.LocationPicker} this
26794          */
26795         show : true,
26796         /**
26797          * @event hide
26798          * Fires when the map hide.
26799          * @param {Roo.bootstrap.LocationPicker} this
26800          */
26801         hide : true,
26802         /**
26803          * @event mapClick
26804          * Fires when click the map.
26805          * @param {Roo.bootstrap.LocationPicker} this
26806          * @param {Map event} e
26807          */
26808         mapClick : true,
26809         /**
26810          * @event mapRightClick
26811          * Fires when right click the map.
26812          * @param {Roo.bootstrap.LocationPicker} this
26813          * @param {Map event} e
26814          */
26815         mapRightClick : true,
26816         /**
26817          * @event markerClick
26818          * Fires when click the marker.
26819          * @param {Roo.bootstrap.LocationPicker} this
26820          * @param {Map event} e
26821          */
26822         markerClick : true,
26823         /**
26824          * @event markerRightClick
26825          * Fires when right click the marker.
26826          * @param {Roo.bootstrap.LocationPicker} this
26827          * @param {Map event} e
26828          */
26829         markerRightClick : true,
26830         /**
26831          * @event OverlayViewDraw
26832          * Fires when OverlayView Draw
26833          * @param {Roo.bootstrap.LocationPicker} this
26834          */
26835         OverlayViewDraw : true,
26836         /**
26837          * @event OverlayViewOnAdd
26838          * Fires when OverlayView Draw
26839          * @param {Roo.bootstrap.LocationPicker} this
26840          */
26841         OverlayViewOnAdd : true,
26842         /**
26843          * @event OverlayViewOnRemove
26844          * Fires when OverlayView Draw
26845          * @param {Roo.bootstrap.LocationPicker} this
26846          */
26847         OverlayViewOnRemove : true,
26848         /**
26849          * @event OverlayViewShow
26850          * Fires when OverlayView Draw
26851          * @param {Roo.bootstrap.LocationPicker} this
26852          * @param {Pixel} cpx
26853          */
26854         OverlayViewShow : true,
26855         /**
26856          * @event OverlayViewHide
26857          * Fires when OverlayView Draw
26858          * @param {Roo.bootstrap.LocationPicker} this
26859          */
26860         OverlayViewHide : true,
26861         /**
26862          * @event loadexception
26863          * Fires when load google lib failed.
26864          * @param {Roo.bootstrap.LocationPicker} this
26865          */
26866         loadexception : true
26867     });
26868         
26869 };
26870
26871 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26872     
26873     gMapContext: false,
26874     
26875     latitude: 0,
26876     longitude: 0,
26877     zoom: 15,
26878     mapTypeId: false,
26879     mapTypeControl: false,
26880     disableDoubleClickZoom: false,
26881     scrollwheel: true,
26882     streetViewControl: false,
26883     radius: 0,
26884     locationName: '',
26885     draggable: true,
26886     enableAutocomplete: false,
26887     enableReverseGeocode: true,
26888     markerTitle: '',
26889     
26890     getAutoCreate: function()
26891     {
26892
26893         var cfg = {
26894             tag: 'div',
26895             cls: 'roo-location-picker'
26896         };
26897         
26898         return cfg
26899     },
26900     
26901     initEvents: function(ct, position)
26902     {       
26903         if(!this.el.getWidth() || this.isApplied()){
26904             return;
26905         }
26906         
26907         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26908         
26909         this.initial();
26910     },
26911     
26912     initial: function()
26913     {
26914         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26915             this.fireEvent('loadexception', this);
26916             return;
26917         }
26918         
26919         if(!this.mapTypeId){
26920             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26921         }
26922         
26923         this.gMapContext = this.GMapContext();
26924         
26925         this.initOverlayView();
26926         
26927         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26928         
26929         var _this = this;
26930                 
26931         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26932             _this.setPosition(_this.gMapContext.marker.position);
26933         });
26934         
26935         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26936             _this.fireEvent('mapClick', this, event);
26937             
26938         });
26939
26940         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26941             _this.fireEvent('mapRightClick', this, event);
26942             
26943         });
26944         
26945         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26946             _this.fireEvent('markerClick', this, event);
26947             
26948         });
26949
26950         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26951             _this.fireEvent('markerRightClick', this, event);
26952             
26953         });
26954         
26955         this.setPosition(this.gMapContext.location);
26956         
26957         this.fireEvent('initial', this, this.gMapContext.location);
26958     },
26959     
26960     initOverlayView: function()
26961     {
26962         var _this = this;
26963         
26964         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26965             
26966             draw: function()
26967             {
26968                 _this.fireEvent('OverlayViewDraw', _this);
26969             },
26970             
26971             onAdd: function()
26972             {
26973                 _this.fireEvent('OverlayViewOnAdd', _this);
26974             },
26975             
26976             onRemove: function()
26977             {
26978                 _this.fireEvent('OverlayViewOnRemove', _this);
26979             },
26980             
26981             show: function(cpx)
26982             {
26983                 _this.fireEvent('OverlayViewShow', _this, cpx);
26984             },
26985             
26986             hide: function()
26987             {
26988                 _this.fireEvent('OverlayViewHide', _this);
26989             }
26990             
26991         });
26992     },
26993     
26994     fromLatLngToContainerPixel: function(event)
26995     {
26996         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26997     },
26998     
26999     isApplied: function() 
27000     {
27001         return this.getGmapContext() == false ? false : true;
27002     },
27003     
27004     getGmapContext: function() 
27005     {
27006         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27007     },
27008     
27009     GMapContext: function() 
27010     {
27011         var position = new google.maps.LatLng(this.latitude, this.longitude);
27012         
27013         var _map = new google.maps.Map(this.el.dom, {
27014             center: position,
27015             zoom: this.zoom,
27016             mapTypeId: this.mapTypeId,
27017             mapTypeControl: this.mapTypeControl,
27018             disableDoubleClickZoom: this.disableDoubleClickZoom,
27019             scrollwheel: this.scrollwheel,
27020             streetViewControl: this.streetViewControl,
27021             locationName: this.locationName,
27022             draggable: this.draggable,
27023             enableAutocomplete: this.enableAutocomplete,
27024             enableReverseGeocode: this.enableReverseGeocode
27025         });
27026         
27027         var _marker = new google.maps.Marker({
27028             position: position,
27029             map: _map,
27030             title: this.markerTitle,
27031             draggable: this.draggable
27032         });
27033         
27034         return {
27035             map: _map,
27036             marker: _marker,
27037             circle: null,
27038             location: position,
27039             radius: this.radius,
27040             locationName: this.locationName,
27041             addressComponents: {
27042                 formatted_address: null,
27043                 addressLine1: null,
27044                 addressLine2: null,
27045                 streetName: null,
27046                 streetNumber: null,
27047                 city: null,
27048                 district: null,
27049                 state: null,
27050                 stateOrProvince: null
27051             },
27052             settings: this,
27053             domContainer: this.el.dom,
27054             geodecoder: new google.maps.Geocoder()
27055         };
27056     },
27057     
27058     drawCircle: function(center, radius, options) 
27059     {
27060         if (this.gMapContext.circle != null) {
27061             this.gMapContext.circle.setMap(null);
27062         }
27063         if (radius > 0) {
27064             radius *= 1;
27065             options = Roo.apply({}, options, {
27066                 strokeColor: "#0000FF",
27067                 strokeOpacity: .35,
27068                 strokeWeight: 2,
27069                 fillColor: "#0000FF",
27070                 fillOpacity: .2
27071             });
27072             
27073             options.map = this.gMapContext.map;
27074             options.radius = radius;
27075             options.center = center;
27076             this.gMapContext.circle = new google.maps.Circle(options);
27077             return this.gMapContext.circle;
27078         }
27079         
27080         return null;
27081     },
27082     
27083     setPosition: function(location) 
27084     {
27085         this.gMapContext.location = location;
27086         this.gMapContext.marker.setPosition(location);
27087         this.gMapContext.map.panTo(location);
27088         this.drawCircle(location, this.gMapContext.radius, {});
27089         
27090         var _this = this;
27091         
27092         if (this.gMapContext.settings.enableReverseGeocode) {
27093             this.gMapContext.geodecoder.geocode({
27094                 latLng: this.gMapContext.location
27095             }, function(results, status) {
27096                 
27097                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27098                     _this.gMapContext.locationName = results[0].formatted_address;
27099                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27100                     
27101                     _this.fireEvent('positionchanged', this, location);
27102                 }
27103             });
27104             
27105             return;
27106         }
27107         
27108         this.fireEvent('positionchanged', this, location);
27109     },
27110     
27111     resize: function()
27112     {
27113         google.maps.event.trigger(this.gMapContext.map, "resize");
27114         
27115         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27116         
27117         this.fireEvent('resize', this);
27118     },
27119     
27120     setPositionByLatLng: function(latitude, longitude)
27121     {
27122         this.setPosition(new google.maps.LatLng(latitude, longitude));
27123     },
27124     
27125     getCurrentPosition: function() 
27126     {
27127         return {
27128             latitude: this.gMapContext.location.lat(),
27129             longitude: this.gMapContext.location.lng()
27130         };
27131     },
27132     
27133     getAddressName: function() 
27134     {
27135         return this.gMapContext.locationName;
27136     },
27137     
27138     getAddressComponents: function() 
27139     {
27140         return this.gMapContext.addressComponents;
27141     },
27142     
27143     address_component_from_google_geocode: function(address_components) 
27144     {
27145         var result = {};
27146         
27147         for (var i = 0; i < address_components.length; i++) {
27148             var component = address_components[i];
27149             if (component.types.indexOf("postal_code") >= 0) {
27150                 result.postalCode = component.short_name;
27151             } else if (component.types.indexOf("street_number") >= 0) {
27152                 result.streetNumber = component.short_name;
27153             } else if (component.types.indexOf("route") >= 0) {
27154                 result.streetName = component.short_name;
27155             } else if (component.types.indexOf("neighborhood") >= 0) {
27156                 result.city = component.short_name;
27157             } else if (component.types.indexOf("locality") >= 0) {
27158                 result.city = component.short_name;
27159             } else if (component.types.indexOf("sublocality") >= 0) {
27160                 result.district = component.short_name;
27161             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27162                 result.stateOrProvince = component.short_name;
27163             } else if (component.types.indexOf("country") >= 0) {
27164                 result.country = component.short_name;
27165             }
27166         }
27167         
27168         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27169         result.addressLine2 = "";
27170         return result;
27171     },
27172     
27173     setZoomLevel: function(zoom)
27174     {
27175         this.gMapContext.map.setZoom(zoom);
27176     },
27177     
27178     show: function()
27179     {
27180         if(!this.el){
27181             return;
27182         }
27183         
27184         this.el.show();
27185         
27186         this.resize();
27187         
27188         this.fireEvent('show', this);
27189     },
27190     
27191     hide: function()
27192     {
27193         if(!this.el){
27194             return;
27195         }
27196         
27197         this.el.hide();
27198         
27199         this.fireEvent('hide', this);
27200     }
27201     
27202 });
27203
27204 Roo.apply(Roo.bootstrap.LocationPicker, {
27205     
27206     OverlayView : function(map, options)
27207     {
27208         options = options || {};
27209         
27210         this.setMap(map);
27211     }
27212     
27213     
27214 });/*
27215  * - LGPL
27216  *
27217  * Alert
27218  * 
27219  */
27220
27221 /**
27222  * @class Roo.bootstrap.Alert
27223  * @extends Roo.bootstrap.Component
27224  * Bootstrap Alert class
27225  * @cfg {String} title The title of alert
27226  * @cfg {String} html The content of alert
27227  * @cfg {String} weight (  success | info | warning | danger )
27228  * @cfg {String} faicon font-awesomeicon
27229  * 
27230  * @constructor
27231  * Create a new alert
27232  * @param {Object} config The config object
27233  */
27234
27235
27236 Roo.bootstrap.Alert = function(config){
27237     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27238     
27239 };
27240
27241 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27242     
27243     title: '',
27244     html: '',
27245     weight: false,
27246     faicon: false,
27247     
27248     getAutoCreate : function()
27249     {
27250         
27251         var cfg = {
27252             tag : 'div',
27253             cls : 'alert',
27254             cn : [
27255                 {
27256                     tag : 'i',
27257                     cls : 'roo-alert-icon'
27258                     
27259                 },
27260                 {
27261                     tag : 'b',
27262                     cls : 'roo-alert-title',
27263                     html : this.title
27264                 },
27265                 {
27266                     tag : 'span',
27267                     cls : 'roo-alert-text',
27268                     html : this.html
27269                 }
27270             ]
27271         };
27272         
27273         if(this.faicon){
27274             cfg.cn[0].cls += ' fa ' + this.faicon;
27275         }
27276         
27277         if(this.weight){
27278             cfg.cls += ' alert-' + this.weight;
27279         }
27280         
27281         return cfg;
27282     },
27283     
27284     initEvents: function() 
27285     {
27286         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27287     },
27288     
27289     setTitle : function(str)
27290     {
27291         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27292     },
27293     
27294     setText : function(str)
27295     {
27296         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27297     },
27298     
27299     setWeight : function(weight)
27300     {
27301         if(this.weight){
27302             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27303         }
27304         
27305         this.weight = weight;
27306         
27307         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27308     },
27309     
27310     setIcon : function(icon)
27311     {
27312         if(this.faicon){
27313             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27314         }
27315         
27316         this.faicon = icon;
27317         
27318         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27319     },
27320     
27321     hide: function() 
27322     {
27323         this.el.hide();   
27324     },
27325     
27326     show: function() 
27327     {  
27328         this.el.show();   
27329     }
27330     
27331 });
27332
27333  
27334 /*
27335 * Licence: LGPL
27336 */
27337
27338 /**
27339  * @class Roo.bootstrap.UploadCropbox
27340  * @extends Roo.bootstrap.Component
27341  * Bootstrap UploadCropbox class
27342  * @cfg {String} emptyText show when image has been loaded
27343  * @cfg {String} rotateNotify show when image too small to rotate
27344  * @cfg {Number} errorTimeout default 3000
27345  * @cfg {Number} minWidth default 300
27346  * @cfg {Number} minHeight default 300
27347  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27348  * @cfg {Boolean} isDocument (true|false) default false
27349  * @cfg {String} url action url
27350  * @cfg {String} paramName default 'imageUpload'
27351  * @cfg {String} method default POST
27352  * @cfg {Boolean} loadMask (true|false) default true
27353  * @cfg {Boolean} loadingText default 'Loading...'
27354  * 
27355  * @constructor
27356  * Create a new UploadCropbox
27357  * @param {Object} config The config object
27358  */
27359
27360 Roo.bootstrap.UploadCropbox = function(config){
27361     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27362     
27363     this.addEvents({
27364         /**
27365          * @event beforeselectfile
27366          * Fire before select file
27367          * @param {Roo.bootstrap.UploadCropbox} this
27368          */
27369         "beforeselectfile" : true,
27370         /**
27371          * @event initial
27372          * Fire after initEvent
27373          * @param {Roo.bootstrap.UploadCropbox} this
27374          */
27375         "initial" : true,
27376         /**
27377          * @event crop
27378          * Fire after initEvent
27379          * @param {Roo.bootstrap.UploadCropbox} this
27380          * @param {String} data
27381          */
27382         "crop" : true,
27383         /**
27384          * @event prepare
27385          * Fire when preparing the file data
27386          * @param {Roo.bootstrap.UploadCropbox} this
27387          * @param {Object} file
27388          */
27389         "prepare" : true,
27390         /**
27391          * @event exception
27392          * Fire when get exception
27393          * @param {Roo.bootstrap.UploadCropbox} this
27394          * @param {XMLHttpRequest} xhr
27395          */
27396         "exception" : true,
27397         /**
27398          * @event beforeloadcanvas
27399          * Fire before load the canvas
27400          * @param {Roo.bootstrap.UploadCropbox} this
27401          * @param {String} src
27402          */
27403         "beforeloadcanvas" : true,
27404         /**
27405          * @event trash
27406          * Fire when trash image
27407          * @param {Roo.bootstrap.UploadCropbox} this
27408          */
27409         "trash" : true,
27410         /**
27411          * @event download
27412          * Fire when download the image
27413          * @param {Roo.bootstrap.UploadCropbox} this
27414          */
27415         "download" : true,
27416         /**
27417          * @event footerbuttonclick
27418          * Fire when footerbuttonclick
27419          * @param {Roo.bootstrap.UploadCropbox} this
27420          * @param {String} type
27421          */
27422         "footerbuttonclick" : true,
27423         /**
27424          * @event resize
27425          * Fire when resize
27426          * @param {Roo.bootstrap.UploadCropbox} this
27427          */
27428         "resize" : true,
27429         /**
27430          * @event rotate
27431          * Fire when rotate the image
27432          * @param {Roo.bootstrap.UploadCropbox} this
27433          * @param {String} pos
27434          */
27435         "rotate" : true,
27436         /**
27437          * @event inspect
27438          * Fire when inspect the file
27439          * @param {Roo.bootstrap.UploadCropbox} this
27440          * @param {Object} file
27441          */
27442         "inspect" : true,
27443         /**
27444          * @event upload
27445          * Fire when xhr upload the file
27446          * @param {Roo.bootstrap.UploadCropbox} this
27447          * @param {Object} data
27448          */
27449         "upload" : true,
27450         /**
27451          * @event arrange
27452          * Fire when arrange the file data
27453          * @param {Roo.bootstrap.UploadCropbox} this
27454          * @param {Object} formData
27455          */
27456         "arrange" : true
27457     });
27458     
27459     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27460 };
27461
27462 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27463     
27464     emptyText : 'Click to upload image',
27465     rotateNotify : 'Image is too small to rotate',
27466     errorTimeout : 3000,
27467     scale : 0,
27468     baseScale : 1,
27469     rotate : 0,
27470     dragable : false,
27471     pinching : false,
27472     mouseX : 0,
27473     mouseY : 0,
27474     cropData : false,
27475     minWidth : 300,
27476     minHeight : 300,
27477     file : false,
27478     exif : {},
27479     baseRotate : 1,
27480     cropType : 'image/jpeg',
27481     buttons : false,
27482     canvasLoaded : false,
27483     isDocument : false,
27484     method : 'POST',
27485     paramName : 'imageUpload',
27486     loadMask : true,
27487     loadingText : 'Loading...',
27488     maskEl : false,
27489     
27490     getAutoCreate : function()
27491     {
27492         var cfg = {
27493             tag : 'div',
27494             cls : 'roo-upload-cropbox',
27495             cn : [
27496                 {
27497                     tag : 'input',
27498                     cls : 'roo-upload-cropbox-selector',
27499                     type : 'file'
27500                 },
27501                 {
27502                     tag : 'div',
27503                     cls : 'roo-upload-cropbox-body',
27504                     style : 'cursor:pointer',
27505                     cn : [
27506                         {
27507                             tag : 'div',
27508                             cls : 'roo-upload-cropbox-preview'
27509                         },
27510                         {
27511                             tag : 'div',
27512                             cls : 'roo-upload-cropbox-thumb'
27513                         },
27514                         {
27515                             tag : 'div',
27516                             cls : 'roo-upload-cropbox-empty-notify',
27517                             html : this.emptyText
27518                         },
27519                         {
27520                             tag : 'div',
27521                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27522                             html : this.rotateNotify
27523                         }
27524                     ]
27525                 },
27526                 {
27527                     tag : 'div',
27528                     cls : 'roo-upload-cropbox-footer',
27529                     cn : {
27530                         tag : 'div',
27531                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27532                         cn : []
27533                     }
27534                 }
27535             ]
27536         };
27537         
27538         return cfg;
27539     },
27540     
27541     onRender : function(ct, position)
27542     {
27543         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27544         
27545         if (this.buttons.length) {
27546             
27547             Roo.each(this.buttons, function(bb) {
27548                 
27549                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27550                 
27551                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27552                 
27553             }, this);
27554         }
27555         
27556         if(this.loadMask){
27557             this.maskEl = this.el;
27558         }
27559     },
27560     
27561     initEvents : function()
27562     {
27563         this.urlAPI = (window.createObjectURL && window) || 
27564                                 (window.URL && URL.revokeObjectURL && URL) || 
27565                                 (window.webkitURL && webkitURL);
27566                         
27567         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27568         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27569         
27570         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27571         this.selectorEl.hide();
27572         
27573         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27574         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27575         
27576         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27577         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27578         this.thumbEl.hide();
27579         
27580         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27581         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27582         
27583         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27584         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27585         this.errorEl.hide();
27586         
27587         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27588         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27589         this.footerEl.hide();
27590         
27591         this.setThumbBoxSize();
27592         
27593         this.bind();
27594         
27595         this.resize();
27596         
27597         this.fireEvent('initial', this);
27598     },
27599
27600     bind : function()
27601     {
27602         var _this = this;
27603         
27604         window.addEventListener("resize", function() { _this.resize(); } );
27605         
27606         this.bodyEl.on('click', this.beforeSelectFile, this);
27607         
27608         if(Roo.isTouch){
27609             this.bodyEl.on('touchstart', this.onTouchStart, this);
27610             this.bodyEl.on('touchmove', this.onTouchMove, this);
27611             this.bodyEl.on('touchend', this.onTouchEnd, this);
27612         }
27613         
27614         if(!Roo.isTouch){
27615             this.bodyEl.on('mousedown', this.onMouseDown, this);
27616             this.bodyEl.on('mousemove', this.onMouseMove, this);
27617             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27618             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27619             Roo.get(document).on('mouseup', this.onMouseUp, this);
27620         }
27621         
27622         this.selectorEl.on('change', this.onFileSelected, this);
27623     },
27624     
27625     reset : function()
27626     {    
27627         this.scale = 0;
27628         this.baseScale = 1;
27629         this.rotate = 0;
27630         this.baseRotate = 1;
27631         this.dragable = false;
27632         this.pinching = false;
27633         this.mouseX = 0;
27634         this.mouseY = 0;
27635         this.cropData = false;
27636         this.notifyEl.dom.innerHTML = this.emptyText;
27637         
27638         this.selectorEl.dom.value = '';
27639         
27640     },
27641     
27642     resize : function()
27643     {
27644         if(this.fireEvent('resize', this) != false){
27645             this.setThumbBoxPosition();
27646             this.setCanvasPosition();
27647         }
27648     },
27649     
27650     onFooterButtonClick : function(e, el, o, type)
27651     {
27652         switch (type) {
27653             case 'rotate-left' :
27654                 this.onRotateLeft(e);
27655                 break;
27656             case 'rotate-right' :
27657                 this.onRotateRight(e);
27658                 break;
27659             case 'picture' :
27660                 this.beforeSelectFile(e);
27661                 break;
27662             case 'trash' :
27663                 this.trash(e);
27664                 break;
27665             case 'crop' :
27666                 this.crop(e);
27667                 break;
27668             case 'download' :
27669                 this.download(e);
27670                 break;
27671             default :
27672                 break;
27673         }
27674         
27675         this.fireEvent('footerbuttonclick', this, type);
27676     },
27677     
27678     beforeSelectFile : function(e)
27679     {
27680         e.preventDefault();
27681         
27682         if(this.fireEvent('beforeselectfile', this) != false){
27683             this.selectorEl.dom.click();
27684         }
27685     },
27686     
27687     onFileSelected : function(e)
27688     {
27689         e.preventDefault();
27690         
27691         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27692             return;
27693         }
27694         
27695         var file = this.selectorEl.dom.files[0];
27696         
27697         if(this.fireEvent('inspect', this, file) != false){
27698             this.prepare(file);
27699         }
27700         
27701     },
27702     
27703     trash : function(e)
27704     {
27705         this.fireEvent('trash', this);
27706     },
27707     
27708     download : function(e)
27709     {
27710         this.fireEvent('download', this);
27711     },
27712     
27713     loadCanvas : function(src)
27714     {   
27715         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27716             
27717             this.reset();
27718             
27719             this.imageEl = document.createElement('img');
27720             
27721             var _this = this;
27722             
27723             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27724             
27725             this.imageEl.src = src;
27726         }
27727     },
27728     
27729     onLoadCanvas : function()
27730     {   
27731         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27732         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27733         
27734         this.bodyEl.un('click', this.beforeSelectFile, this);
27735         
27736         this.notifyEl.hide();
27737         this.thumbEl.show();
27738         this.footerEl.show();
27739         
27740         this.baseRotateLevel();
27741         
27742         if(this.isDocument){
27743             this.setThumbBoxSize();
27744         }
27745         
27746         this.setThumbBoxPosition();
27747         
27748         this.baseScaleLevel();
27749         
27750         this.draw();
27751         
27752         this.resize();
27753         
27754         this.canvasLoaded = true;
27755         
27756         if(this.loadMask){
27757             this.maskEl.unmask();
27758         }
27759         
27760     },
27761     
27762     setCanvasPosition : function()
27763     {   
27764         if(!this.canvasEl){
27765             return;
27766         }
27767         
27768         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27769         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27770         
27771         this.previewEl.setLeft(pw);
27772         this.previewEl.setTop(ph);
27773         
27774     },
27775     
27776     onMouseDown : function(e)
27777     {   
27778         e.stopEvent();
27779         
27780         this.dragable = true;
27781         this.pinching = false;
27782         
27783         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27784             this.dragable = false;
27785             return;
27786         }
27787         
27788         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27789         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27790         
27791     },
27792     
27793     onMouseMove : function(e)
27794     {   
27795         e.stopEvent();
27796         
27797         if(!this.canvasLoaded){
27798             return;
27799         }
27800         
27801         if (!this.dragable){
27802             return;
27803         }
27804         
27805         var minX = Math.ceil(this.thumbEl.getLeft(true));
27806         var minY = Math.ceil(this.thumbEl.getTop(true));
27807         
27808         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27809         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27810         
27811         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27812         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27813         
27814         x = x - this.mouseX;
27815         y = y - this.mouseY;
27816         
27817         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27818         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27819         
27820         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27821         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27822         
27823         this.previewEl.setLeft(bgX);
27824         this.previewEl.setTop(bgY);
27825         
27826         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27827         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27828     },
27829     
27830     onMouseUp : function(e)
27831     {   
27832         e.stopEvent();
27833         
27834         this.dragable = false;
27835     },
27836     
27837     onMouseWheel : function(e)
27838     {   
27839         e.stopEvent();
27840         
27841         this.startScale = this.scale;
27842         
27843         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27844         
27845         if(!this.zoomable()){
27846             this.scale = this.startScale;
27847             return;
27848         }
27849         
27850         this.draw();
27851         
27852         return;
27853     },
27854     
27855     zoomable : function()
27856     {
27857         var minScale = this.thumbEl.getWidth() / this.minWidth;
27858         
27859         if(this.minWidth < this.minHeight){
27860             minScale = this.thumbEl.getHeight() / this.minHeight;
27861         }
27862         
27863         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27864         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27865         
27866         if(
27867                 this.isDocument &&
27868                 (this.rotate == 0 || this.rotate == 180) && 
27869                 (
27870                     width > this.imageEl.OriginWidth || 
27871                     height > this.imageEl.OriginHeight ||
27872                     (width < this.minWidth && height < this.minHeight)
27873                 )
27874         ){
27875             return false;
27876         }
27877         
27878         if(
27879                 this.isDocument &&
27880                 (this.rotate == 90 || this.rotate == 270) && 
27881                 (
27882                     width > this.imageEl.OriginWidth || 
27883                     height > this.imageEl.OriginHeight ||
27884                     (width < this.minHeight && height < this.minWidth)
27885                 )
27886         ){
27887             return false;
27888         }
27889         
27890         if(
27891                 !this.isDocument &&
27892                 (this.rotate == 0 || this.rotate == 180) && 
27893                 (
27894                     width < this.minWidth || 
27895                     width > this.imageEl.OriginWidth || 
27896                     height < this.minHeight || 
27897                     height > this.imageEl.OriginHeight
27898                 )
27899         ){
27900             return false;
27901         }
27902         
27903         if(
27904                 !this.isDocument &&
27905                 (this.rotate == 90 || this.rotate == 270) && 
27906                 (
27907                     width < this.minHeight || 
27908                     width > this.imageEl.OriginWidth || 
27909                     height < this.minWidth || 
27910                     height > this.imageEl.OriginHeight
27911                 )
27912         ){
27913             return false;
27914         }
27915         
27916         return true;
27917         
27918     },
27919     
27920     onRotateLeft : function(e)
27921     {   
27922         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27923             
27924             var minScale = this.thumbEl.getWidth() / this.minWidth;
27925             
27926             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27927             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27928             
27929             this.startScale = this.scale;
27930             
27931             while (this.getScaleLevel() < minScale){
27932             
27933                 this.scale = this.scale + 1;
27934                 
27935                 if(!this.zoomable()){
27936                     break;
27937                 }
27938                 
27939                 if(
27940                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27941                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27942                 ){
27943                     continue;
27944                 }
27945                 
27946                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27947
27948                 this.draw();
27949                 
27950                 return;
27951             }
27952             
27953             this.scale = this.startScale;
27954             
27955             this.onRotateFail();
27956             
27957             return false;
27958         }
27959         
27960         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27961
27962         if(this.isDocument){
27963             this.setThumbBoxSize();
27964             this.setThumbBoxPosition();
27965             this.setCanvasPosition();
27966         }
27967         
27968         this.draw();
27969         
27970         this.fireEvent('rotate', this, 'left');
27971         
27972     },
27973     
27974     onRotateRight : function(e)
27975     {
27976         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27977             
27978             var minScale = this.thumbEl.getWidth() / this.minWidth;
27979         
27980             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27981             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27982             
27983             this.startScale = this.scale;
27984             
27985             while (this.getScaleLevel() < minScale){
27986             
27987                 this.scale = this.scale + 1;
27988                 
27989                 if(!this.zoomable()){
27990                     break;
27991                 }
27992                 
27993                 if(
27994                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27995                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27996                 ){
27997                     continue;
27998                 }
27999                 
28000                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28001
28002                 this.draw();
28003                 
28004                 return;
28005             }
28006             
28007             this.scale = this.startScale;
28008             
28009             this.onRotateFail();
28010             
28011             return false;
28012         }
28013         
28014         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28015
28016         if(this.isDocument){
28017             this.setThumbBoxSize();
28018             this.setThumbBoxPosition();
28019             this.setCanvasPosition();
28020         }
28021         
28022         this.draw();
28023         
28024         this.fireEvent('rotate', this, 'right');
28025     },
28026     
28027     onRotateFail : function()
28028     {
28029         this.errorEl.show(true);
28030         
28031         var _this = this;
28032         
28033         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28034     },
28035     
28036     draw : function()
28037     {
28038         this.previewEl.dom.innerHTML = '';
28039         
28040         var canvasEl = document.createElement("canvas");
28041         
28042         var contextEl = canvasEl.getContext("2d");
28043         
28044         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28045         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28046         var center = this.imageEl.OriginWidth / 2;
28047         
28048         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28049             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28050             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28051             center = this.imageEl.OriginHeight / 2;
28052         }
28053         
28054         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28055         
28056         contextEl.translate(center, center);
28057         contextEl.rotate(this.rotate * Math.PI / 180);
28058
28059         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28060         
28061         this.canvasEl = document.createElement("canvas");
28062         
28063         this.contextEl = this.canvasEl.getContext("2d");
28064         
28065         switch (this.rotate) {
28066             case 0 :
28067                 
28068                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28069                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28070                 
28071                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28072                 
28073                 break;
28074             case 90 : 
28075                 
28076                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28077                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28078                 
28079                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28080                     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);
28081                     break;
28082                 }
28083                 
28084                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28085                 
28086                 break;
28087             case 180 :
28088                 
28089                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28090                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28091                 
28092                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28093                     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);
28094                     break;
28095                 }
28096                 
28097                 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);
28098                 
28099                 break;
28100             case 270 :
28101                 
28102                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28103                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28104         
28105                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28106                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28107                     break;
28108                 }
28109                 
28110                 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);
28111                 
28112                 break;
28113             default : 
28114                 break;
28115         }
28116         
28117         this.previewEl.appendChild(this.canvasEl);
28118         
28119         this.setCanvasPosition();
28120     },
28121     
28122     crop : function()
28123     {
28124         if(!this.canvasLoaded){
28125             return;
28126         }
28127         
28128         var imageCanvas = document.createElement("canvas");
28129         
28130         var imageContext = imageCanvas.getContext("2d");
28131         
28132         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28133         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28134         
28135         var center = imageCanvas.width / 2;
28136         
28137         imageContext.translate(center, center);
28138         
28139         imageContext.rotate(this.rotate * Math.PI / 180);
28140         
28141         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28142         
28143         var canvas = document.createElement("canvas");
28144         
28145         var context = canvas.getContext("2d");
28146                 
28147         canvas.width = this.minWidth;
28148         canvas.height = this.minHeight;
28149
28150         switch (this.rotate) {
28151             case 0 :
28152                 
28153                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28154                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28155                 
28156                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28157                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28158                 
28159                 var targetWidth = this.minWidth - 2 * x;
28160                 var targetHeight = this.minHeight - 2 * y;
28161                 
28162                 var scale = 1;
28163                 
28164                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28165                     scale = targetWidth / width;
28166                 }
28167                 
28168                 if(x > 0 && y == 0){
28169                     scale = targetHeight / height;
28170                 }
28171                 
28172                 if(x > 0 && y > 0){
28173                     scale = targetWidth / width;
28174                     
28175                     if(width < height){
28176                         scale = targetHeight / height;
28177                     }
28178                 }
28179                 
28180                 context.scale(scale, scale);
28181                 
28182                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28183                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28184
28185                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28186                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28187
28188                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28189                 
28190                 break;
28191             case 90 : 
28192                 
28193                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28194                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28195                 
28196                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28197                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28198                 
28199                 var targetWidth = this.minWidth - 2 * x;
28200                 var targetHeight = this.minHeight - 2 * y;
28201                 
28202                 var scale = 1;
28203                 
28204                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28205                     scale = targetWidth / width;
28206                 }
28207                 
28208                 if(x > 0 && y == 0){
28209                     scale = targetHeight / height;
28210                 }
28211                 
28212                 if(x > 0 && y > 0){
28213                     scale = targetWidth / width;
28214                     
28215                     if(width < height){
28216                         scale = targetHeight / height;
28217                     }
28218                 }
28219                 
28220                 context.scale(scale, scale);
28221                 
28222                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28223                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28224
28225                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28226                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28227                 
28228                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28229                 
28230                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28231                 
28232                 break;
28233             case 180 :
28234                 
28235                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28236                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28237                 
28238                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28239                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28240                 
28241                 var targetWidth = this.minWidth - 2 * x;
28242                 var targetHeight = this.minHeight - 2 * y;
28243                 
28244                 var scale = 1;
28245                 
28246                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28247                     scale = targetWidth / width;
28248                 }
28249                 
28250                 if(x > 0 && y == 0){
28251                     scale = targetHeight / height;
28252                 }
28253                 
28254                 if(x > 0 && y > 0){
28255                     scale = targetWidth / width;
28256                     
28257                     if(width < height){
28258                         scale = targetHeight / height;
28259                     }
28260                 }
28261                 
28262                 context.scale(scale, scale);
28263                 
28264                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28265                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28266
28267                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28268                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28269
28270                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28271                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28272                 
28273                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28274                 
28275                 break;
28276             case 270 :
28277                 
28278                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28279                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28280                 
28281                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28282                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28283                 
28284                 var targetWidth = this.minWidth - 2 * x;
28285                 var targetHeight = this.minHeight - 2 * y;
28286                 
28287                 var scale = 1;
28288                 
28289                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28290                     scale = targetWidth / width;
28291                 }
28292                 
28293                 if(x > 0 && y == 0){
28294                     scale = targetHeight / height;
28295                 }
28296                 
28297                 if(x > 0 && y > 0){
28298                     scale = targetWidth / width;
28299                     
28300                     if(width < height){
28301                         scale = targetHeight / height;
28302                     }
28303                 }
28304                 
28305                 context.scale(scale, scale);
28306                 
28307                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28308                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28309
28310                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28311                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28312                 
28313                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28314                 
28315                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28316                 
28317                 break;
28318             default : 
28319                 break;
28320         }
28321         
28322         this.cropData = canvas.toDataURL(this.cropType);
28323         
28324         if(this.fireEvent('crop', this, this.cropData) !== false){
28325             this.process(this.file, this.cropData);
28326         }
28327         
28328         return;
28329         
28330     },
28331     
28332     setThumbBoxSize : function()
28333     {
28334         var width, height;
28335         
28336         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28337             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28338             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28339             
28340             this.minWidth = width;
28341             this.minHeight = height;
28342             
28343             if(this.rotate == 90 || this.rotate == 270){
28344                 this.minWidth = height;
28345                 this.minHeight = width;
28346             }
28347         }
28348         
28349         height = 300;
28350         width = Math.ceil(this.minWidth * height / this.minHeight);
28351         
28352         if(this.minWidth > this.minHeight){
28353             width = 300;
28354             height = Math.ceil(this.minHeight * width / this.minWidth);
28355         }
28356         
28357         this.thumbEl.setStyle({
28358             width : width + 'px',
28359             height : height + 'px'
28360         });
28361
28362         return;
28363             
28364     },
28365     
28366     setThumbBoxPosition : function()
28367     {
28368         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28369         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28370         
28371         this.thumbEl.setLeft(x);
28372         this.thumbEl.setTop(y);
28373         
28374     },
28375     
28376     baseRotateLevel : function()
28377     {
28378         this.baseRotate = 1;
28379         
28380         if(
28381                 typeof(this.exif) != 'undefined' &&
28382                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28383                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28384         ){
28385             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28386         }
28387         
28388         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28389         
28390     },
28391     
28392     baseScaleLevel : function()
28393     {
28394         var width, height;
28395         
28396         if(this.isDocument){
28397             
28398             if(this.baseRotate == 6 || this.baseRotate == 8){
28399             
28400                 height = this.thumbEl.getHeight();
28401                 this.baseScale = height / this.imageEl.OriginWidth;
28402
28403                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28404                     width = this.thumbEl.getWidth();
28405                     this.baseScale = width / this.imageEl.OriginHeight;
28406                 }
28407
28408                 return;
28409             }
28410
28411             height = this.thumbEl.getHeight();
28412             this.baseScale = height / this.imageEl.OriginHeight;
28413
28414             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28415                 width = this.thumbEl.getWidth();
28416                 this.baseScale = width / this.imageEl.OriginWidth;
28417             }
28418
28419             return;
28420         }
28421         
28422         if(this.baseRotate == 6 || this.baseRotate == 8){
28423             
28424             width = this.thumbEl.getHeight();
28425             this.baseScale = width / this.imageEl.OriginHeight;
28426             
28427             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28428                 height = this.thumbEl.getWidth();
28429                 this.baseScale = height / this.imageEl.OriginHeight;
28430             }
28431             
28432             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28433                 height = this.thumbEl.getWidth();
28434                 this.baseScale = height / this.imageEl.OriginHeight;
28435                 
28436                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28437                     width = this.thumbEl.getHeight();
28438                     this.baseScale = width / this.imageEl.OriginWidth;
28439                 }
28440             }
28441             
28442             return;
28443         }
28444         
28445         width = this.thumbEl.getWidth();
28446         this.baseScale = width / this.imageEl.OriginWidth;
28447         
28448         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28449             height = this.thumbEl.getHeight();
28450             this.baseScale = height / this.imageEl.OriginHeight;
28451         }
28452         
28453         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28454             
28455             height = this.thumbEl.getHeight();
28456             this.baseScale = height / this.imageEl.OriginHeight;
28457             
28458             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28459                 width = this.thumbEl.getWidth();
28460                 this.baseScale = width / this.imageEl.OriginWidth;
28461             }
28462             
28463         }
28464         
28465         return;
28466     },
28467     
28468     getScaleLevel : function()
28469     {
28470         return this.baseScale * Math.pow(1.1, this.scale);
28471     },
28472     
28473     onTouchStart : function(e)
28474     {
28475         if(!this.canvasLoaded){
28476             this.beforeSelectFile(e);
28477             return;
28478         }
28479         
28480         var touches = e.browserEvent.touches;
28481         
28482         if(!touches){
28483             return;
28484         }
28485         
28486         if(touches.length == 1){
28487             this.onMouseDown(e);
28488             return;
28489         }
28490         
28491         if(touches.length != 2){
28492             return;
28493         }
28494         
28495         var coords = [];
28496         
28497         for(var i = 0, finger; finger = touches[i]; i++){
28498             coords.push(finger.pageX, finger.pageY);
28499         }
28500         
28501         var x = Math.pow(coords[0] - coords[2], 2);
28502         var y = Math.pow(coords[1] - coords[3], 2);
28503         
28504         this.startDistance = Math.sqrt(x + y);
28505         
28506         this.startScale = this.scale;
28507         
28508         this.pinching = true;
28509         this.dragable = false;
28510         
28511     },
28512     
28513     onTouchMove : function(e)
28514     {
28515         if(!this.pinching && !this.dragable){
28516             return;
28517         }
28518         
28519         var touches = e.browserEvent.touches;
28520         
28521         if(!touches){
28522             return;
28523         }
28524         
28525         if(this.dragable){
28526             this.onMouseMove(e);
28527             return;
28528         }
28529         
28530         var coords = [];
28531         
28532         for(var i = 0, finger; finger = touches[i]; i++){
28533             coords.push(finger.pageX, finger.pageY);
28534         }
28535         
28536         var x = Math.pow(coords[0] - coords[2], 2);
28537         var y = Math.pow(coords[1] - coords[3], 2);
28538         
28539         this.endDistance = Math.sqrt(x + y);
28540         
28541         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28542         
28543         if(!this.zoomable()){
28544             this.scale = this.startScale;
28545             return;
28546         }
28547         
28548         this.draw();
28549         
28550     },
28551     
28552     onTouchEnd : function(e)
28553     {
28554         this.pinching = false;
28555         this.dragable = false;
28556         
28557     },
28558     
28559     process : function(file, crop)
28560     {
28561         if(this.loadMask){
28562             this.maskEl.mask(this.loadingText);
28563         }
28564         
28565         this.xhr = new XMLHttpRequest();
28566         
28567         file.xhr = this.xhr;
28568
28569         this.xhr.open(this.method, this.url, true);
28570         
28571         var headers = {
28572             "Accept": "application/json",
28573             "Cache-Control": "no-cache",
28574             "X-Requested-With": "XMLHttpRequest"
28575         };
28576         
28577         for (var headerName in headers) {
28578             var headerValue = headers[headerName];
28579             if (headerValue) {
28580                 this.xhr.setRequestHeader(headerName, headerValue);
28581             }
28582         }
28583         
28584         var _this = this;
28585         
28586         this.xhr.onload = function()
28587         {
28588             _this.xhrOnLoad(_this.xhr);
28589         }
28590         
28591         this.xhr.onerror = function()
28592         {
28593             _this.xhrOnError(_this.xhr);
28594         }
28595         
28596         var formData = new FormData();
28597
28598         formData.append('returnHTML', 'NO');
28599         
28600         if(crop){
28601             formData.append('crop', crop);
28602         }
28603         
28604         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28605             formData.append(this.paramName, file, file.name);
28606         }
28607         
28608         if(typeof(file.filename) != 'undefined'){
28609             formData.append('filename', file.filename);
28610         }
28611         
28612         if(typeof(file.mimetype) != 'undefined'){
28613             formData.append('mimetype', file.mimetype);
28614         }
28615         
28616         if(this.fireEvent('arrange', this, formData) != false){
28617             this.xhr.send(formData);
28618         };
28619     },
28620     
28621     xhrOnLoad : function(xhr)
28622     {
28623         if(this.loadMask){
28624             this.maskEl.unmask();
28625         }
28626         
28627         if (xhr.readyState !== 4) {
28628             this.fireEvent('exception', this, xhr);
28629             return;
28630         }
28631
28632         var response = Roo.decode(xhr.responseText);
28633         
28634         if(!response.success){
28635             this.fireEvent('exception', this, xhr);
28636             return;
28637         }
28638         
28639         var response = Roo.decode(xhr.responseText);
28640         
28641         this.fireEvent('upload', this, response);
28642         
28643     },
28644     
28645     xhrOnError : function()
28646     {
28647         if(this.loadMask){
28648             this.maskEl.unmask();
28649         }
28650         
28651         Roo.log('xhr on error');
28652         
28653         var response = Roo.decode(xhr.responseText);
28654           
28655         Roo.log(response);
28656         
28657     },
28658     
28659     prepare : function(file)
28660     {   
28661         if(this.loadMask){
28662             this.maskEl.mask(this.loadingText);
28663         }
28664         
28665         this.file = false;
28666         this.exif = {};
28667         
28668         if(typeof(file) === 'string'){
28669             this.loadCanvas(file);
28670             return;
28671         }
28672         
28673         if(!file || !this.urlAPI){
28674             return;
28675         }
28676         
28677         this.file = file;
28678         this.cropType = file.type;
28679         
28680         var _this = this;
28681         
28682         if(this.fireEvent('prepare', this, this.file) != false){
28683             
28684             var reader = new FileReader();
28685             
28686             reader.onload = function (e) {
28687                 if (e.target.error) {
28688                     Roo.log(e.target.error);
28689                     return;
28690                 }
28691                 
28692                 var buffer = e.target.result,
28693                     dataView = new DataView(buffer),
28694                     offset = 2,
28695                     maxOffset = dataView.byteLength - 4,
28696                     markerBytes,
28697                     markerLength;
28698                 
28699                 if (dataView.getUint16(0) === 0xffd8) {
28700                     while (offset < maxOffset) {
28701                         markerBytes = dataView.getUint16(offset);
28702                         
28703                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28704                             markerLength = dataView.getUint16(offset + 2) + 2;
28705                             if (offset + markerLength > dataView.byteLength) {
28706                                 Roo.log('Invalid meta data: Invalid segment size.');
28707                                 break;
28708                             }
28709                             
28710                             if(markerBytes == 0xffe1){
28711                                 _this.parseExifData(
28712                                     dataView,
28713                                     offset,
28714                                     markerLength
28715                                 );
28716                             }
28717                             
28718                             offset += markerLength;
28719                             
28720                             continue;
28721                         }
28722                         
28723                         break;
28724                     }
28725                     
28726                 }
28727                 
28728                 var url = _this.urlAPI.createObjectURL(_this.file);
28729                 
28730                 _this.loadCanvas(url);
28731                 
28732                 return;
28733             }
28734             
28735             reader.readAsArrayBuffer(this.file);
28736             
28737         }
28738         
28739     },
28740     
28741     parseExifData : function(dataView, offset, length)
28742     {
28743         var tiffOffset = offset + 10,
28744             littleEndian,
28745             dirOffset;
28746     
28747         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28748             // No Exif data, might be XMP data instead
28749             return;
28750         }
28751         
28752         // Check for the ASCII code for "Exif" (0x45786966):
28753         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28754             // No Exif data, might be XMP data instead
28755             return;
28756         }
28757         if (tiffOffset + 8 > dataView.byteLength) {
28758             Roo.log('Invalid Exif data: Invalid segment size.');
28759             return;
28760         }
28761         // Check for the two null bytes:
28762         if (dataView.getUint16(offset + 8) !== 0x0000) {
28763             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28764             return;
28765         }
28766         // Check the byte alignment:
28767         switch (dataView.getUint16(tiffOffset)) {
28768         case 0x4949:
28769             littleEndian = true;
28770             break;
28771         case 0x4D4D:
28772             littleEndian = false;
28773             break;
28774         default:
28775             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28776             return;
28777         }
28778         // Check for the TIFF tag marker (0x002A):
28779         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28780             Roo.log('Invalid Exif data: Missing TIFF marker.');
28781             return;
28782         }
28783         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28784         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28785         
28786         this.parseExifTags(
28787             dataView,
28788             tiffOffset,
28789             tiffOffset + dirOffset,
28790             littleEndian
28791         );
28792     },
28793     
28794     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28795     {
28796         var tagsNumber,
28797             dirEndOffset,
28798             i;
28799         if (dirOffset + 6 > dataView.byteLength) {
28800             Roo.log('Invalid Exif data: Invalid directory offset.');
28801             return;
28802         }
28803         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28804         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28805         if (dirEndOffset + 4 > dataView.byteLength) {
28806             Roo.log('Invalid Exif data: Invalid directory size.');
28807             return;
28808         }
28809         for (i = 0; i < tagsNumber; i += 1) {
28810             this.parseExifTag(
28811                 dataView,
28812                 tiffOffset,
28813                 dirOffset + 2 + 12 * i, // tag offset
28814                 littleEndian
28815             );
28816         }
28817         // Return the offset to the next directory:
28818         return dataView.getUint32(dirEndOffset, littleEndian);
28819     },
28820     
28821     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28822     {
28823         var tag = dataView.getUint16(offset, littleEndian);
28824         
28825         this.exif[tag] = this.getExifValue(
28826             dataView,
28827             tiffOffset,
28828             offset,
28829             dataView.getUint16(offset + 2, littleEndian), // tag type
28830             dataView.getUint32(offset + 4, littleEndian), // tag length
28831             littleEndian
28832         );
28833     },
28834     
28835     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28836     {
28837         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28838             tagSize,
28839             dataOffset,
28840             values,
28841             i,
28842             str,
28843             c;
28844     
28845         if (!tagType) {
28846             Roo.log('Invalid Exif data: Invalid tag type.');
28847             return;
28848         }
28849         
28850         tagSize = tagType.size * length;
28851         // Determine if the value is contained in the dataOffset bytes,
28852         // or if the value at the dataOffset is a pointer to the actual data:
28853         dataOffset = tagSize > 4 ?
28854                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28855         if (dataOffset + tagSize > dataView.byteLength) {
28856             Roo.log('Invalid Exif data: Invalid data offset.');
28857             return;
28858         }
28859         if (length === 1) {
28860             return tagType.getValue(dataView, dataOffset, littleEndian);
28861         }
28862         values = [];
28863         for (i = 0; i < length; i += 1) {
28864             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28865         }
28866         
28867         if (tagType.ascii) {
28868             str = '';
28869             // Concatenate the chars:
28870             for (i = 0; i < values.length; i += 1) {
28871                 c = values[i];
28872                 // Ignore the terminating NULL byte(s):
28873                 if (c === '\u0000') {
28874                     break;
28875                 }
28876                 str += c;
28877             }
28878             return str;
28879         }
28880         return values;
28881     }
28882     
28883 });
28884
28885 Roo.apply(Roo.bootstrap.UploadCropbox, {
28886     tags : {
28887         'Orientation': 0x0112
28888     },
28889     
28890     Orientation: {
28891             1: 0, //'top-left',
28892 //            2: 'top-right',
28893             3: 180, //'bottom-right',
28894 //            4: 'bottom-left',
28895 //            5: 'left-top',
28896             6: 90, //'right-top',
28897 //            7: 'right-bottom',
28898             8: 270 //'left-bottom'
28899     },
28900     
28901     exifTagTypes : {
28902         // byte, 8-bit unsigned int:
28903         1: {
28904             getValue: function (dataView, dataOffset) {
28905                 return dataView.getUint8(dataOffset);
28906             },
28907             size: 1
28908         },
28909         // ascii, 8-bit byte:
28910         2: {
28911             getValue: function (dataView, dataOffset) {
28912                 return String.fromCharCode(dataView.getUint8(dataOffset));
28913             },
28914             size: 1,
28915             ascii: true
28916         },
28917         // short, 16 bit int:
28918         3: {
28919             getValue: function (dataView, dataOffset, littleEndian) {
28920                 return dataView.getUint16(dataOffset, littleEndian);
28921             },
28922             size: 2
28923         },
28924         // long, 32 bit int:
28925         4: {
28926             getValue: function (dataView, dataOffset, littleEndian) {
28927                 return dataView.getUint32(dataOffset, littleEndian);
28928             },
28929             size: 4
28930         },
28931         // rational = two long values, first is numerator, second is denominator:
28932         5: {
28933             getValue: function (dataView, dataOffset, littleEndian) {
28934                 return dataView.getUint32(dataOffset, littleEndian) /
28935                     dataView.getUint32(dataOffset + 4, littleEndian);
28936             },
28937             size: 8
28938         },
28939         // slong, 32 bit signed int:
28940         9: {
28941             getValue: function (dataView, dataOffset, littleEndian) {
28942                 return dataView.getInt32(dataOffset, littleEndian);
28943             },
28944             size: 4
28945         },
28946         // srational, two slongs, first is numerator, second is denominator:
28947         10: {
28948             getValue: function (dataView, dataOffset, littleEndian) {
28949                 return dataView.getInt32(dataOffset, littleEndian) /
28950                     dataView.getInt32(dataOffset + 4, littleEndian);
28951             },
28952             size: 8
28953         }
28954     },
28955     
28956     footer : {
28957         STANDARD : [
28958             {
28959                 tag : 'div',
28960                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28961                 action : 'rotate-left',
28962                 cn : [
28963                     {
28964                         tag : 'button',
28965                         cls : 'btn btn-default',
28966                         html : '<i class="fa fa-undo"></i>'
28967                     }
28968                 ]
28969             },
28970             {
28971                 tag : 'div',
28972                 cls : 'btn-group roo-upload-cropbox-picture',
28973                 action : 'picture',
28974                 cn : [
28975                     {
28976                         tag : 'button',
28977                         cls : 'btn btn-default',
28978                         html : '<i class="fa fa-picture-o"></i>'
28979                     }
28980                 ]
28981             },
28982             {
28983                 tag : 'div',
28984                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28985                 action : 'rotate-right',
28986                 cn : [
28987                     {
28988                         tag : 'button',
28989                         cls : 'btn btn-default',
28990                         html : '<i class="fa fa-repeat"></i>'
28991                     }
28992                 ]
28993             }
28994         ],
28995         DOCUMENT : [
28996             {
28997                 tag : 'div',
28998                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28999                 action : 'rotate-left',
29000                 cn : [
29001                     {
29002                         tag : 'button',
29003                         cls : 'btn btn-default',
29004                         html : '<i class="fa fa-undo"></i>'
29005                     }
29006                 ]
29007             },
29008             {
29009                 tag : 'div',
29010                 cls : 'btn-group roo-upload-cropbox-download',
29011                 action : 'download',
29012                 cn : [
29013                     {
29014                         tag : 'button',
29015                         cls : 'btn btn-default',
29016                         html : '<i class="fa fa-download"></i>'
29017                     }
29018                 ]
29019             },
29020             {
29021                 tag : 'div',
29022                 cls : 'btn-group roo-upload-cropbox-crop',
29023                 action : 'crop',
29024                 cn : [
29025                     {
29026                         tag : 'button',
29027                         cls : 'btn btn-default',
29028                         html : '<i class="fa fa-crop"></i>'
29029                     }
29030                 ]
29031             },
29032             {
29033                 tag : 'div',
29034                 cls : 'btn-group roo-upload-cropbox-trash',
29035                 action : 'trash',
29036                 cn : [
29037                     {
29038                         tag : 'button',
29039                         cls : 'btn btn-default',
29040                         html : '<i class="fa fa-trash"></i>'
29041                     }
29042                 ]
29043             },
29044             {
29045                 tag : 'div',
29046                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29047                 action : 'rotate-right',
29048                 cn : [
29049                     {
29050                         tag : 'button',
29051                         cls : 'btn btn-default',
29052                         html : '<i class="fa fa-repeat"></i>'
29053                     }
29054                 ]
29055             }
29056         ],
29057         ROTATOR : [
29058             {
29059                 tag : 'div',
29060                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29061                 action : 'rotate-left',
29062                 cn : [
29063                     {
29064                         tag : 'button',
29065                         cls : 'btn btn-default',
29066                         html : '<i class="fa fa-undo"></i>'
29067                     }
29068                 ]
29069             },
29070             {
29071                 tag : 'div',
29072                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29073                 action : 'rotate-right',
29074                 cn : [
29075                     {
29076                         tag : 'button',
29077                         cls : 'btn btn-default',
29078                         html : '<i class="fa fa-repeat"></i>'
29079                     }
29080                 ]
29081             }
29082         ]
29083     }
29084 });
29085
29086 /*
29087 * Licence: LGPL
29088 */
29089
29090 /**
29091  * @class Roo.bootstrap.DocumentManager
29092  * @extends Roo.bootstrap.Component
29093  * Bootstrap DocumentManager class
29094  * @cfg {String} paramName default 'imageUpload'
29095  * @cfg {String} toolTipName default 'filename'
29096  * @cfg {String} method default POST
29097  * @cfg {String} url action url
29098  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29099  * @cfg {Boolean} multiple multiple upload default true
29100  * @cfg {Number} thumbSize default 300
29101  * @cfg {String} fieldLabel
29102  * @cfg {Number} labelWidth default 4
29103  * @cfg {String} labelAlign (left|top) default left
29104  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29105 * @cfg {Number} labellg set the width of label (1-12)
29106  * @cfg {Number} labelmd set the width of label (1-12)
29107  * @cfg {Number} labelsm set the width of label (1-12)
29108  * @cfg {Number} labelxs set the width of label (1-12)
29109  * 
29110  * @constructor
29111  * Create a new DocumentManager
29112  * @param {Object} config The config object
29113  */
29114
29115 Roo.bootstrap.DocumentManager = function(config){
29116     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29117     
29118     this.files = [];
29119     this.delegates = [];
29120     
29121     this.addEvents({
29122         /**
29123          * @event initial
29124          * Fire when initial the DocumentManager
29125          * @param {Roo.bootstrap.DocumentManager} this
29126          */
29127         "initial" : true,
29128         /**
29129          * @event inspect
29130          * inspect selected file
29131          * @param {Roo.bootstrap.DocumentManager} this
29132          * @param {File} file
29133          */
29134         "inspect" : true,
29135         /**
29136          * @event exception
29137          * Fire when xhr load exception
29138          * @param {Roo.bootstrap.DocumentManager} this
29139          * @param {XMLHttpRequest} xhr
29140          */
29141         "exception" : true,
29142         /**
29143          * @event afterupload
29144          * Fire when xhr load exception
29145          * @param {Roo.bootstrap.DocumentManager} this
29146          * @param {XMLHttpRequest} xhr
29147          */
29148         "afterupload" : true,
29149         /**
29150          * @event prepare
29151          * prepare the form data
29152          * @param {Roo.bootstrap.DocumentManager} this
29153          * @param {Object} formData
29154          */
29155         "prepare" : true,
29156         /**
29157          * @event remove
29158          * Fire when remove the file
29159          * @param {Roo.bootstrap.DocumentManager} this
29160          * @param {Object} file
29161          */
29162         "remove" : true,
29163         /**
29164          * @event refresh
29165          * Fire after refresh the file
29166          * @param {Roo.bootstrap.DocumentManager} this
29167          */
29168         "refresh" : true,
29169         /**
29170          * @event click
29171          * Fire after click the image
29172          * @param {Roo.bootstrap.DocumentManager} this
29173          * @param {Object} file
29174          */
29175         "click" : true,
29176         /**
29177          * @event edit
29178          * Fire when upload a image and editable set to true
29179          * @param {Roo.bootstrap.DocumentManager} this
29180          * @param {Object} file
29181          */
29182         "edit" : true,
29183         /**
29184          * @event beforeselectfile
29185          * Fire before select file
29186          * @param {Roo.bootstrap.DocumentManager} this
29187          */
29188         "beforeselectfile" : true,
29189         /**
29190          * @event process
29191          * Fire before process file
29192          * @param {Roo.bootstrap.DocumentManager} this
29193          * @param {Object} file
29194          */
29195         "process" : true,
29196         /**
29197          * @event previewrendered
29198          * Fire when preview rendered
29199          * @param {Roo.bootstrap.DocumentManager} this
29200          * @param {Object} file
29201          */
29202         "previewrendered" : true,
29203         /**
29204          */
29205         "previewResize" : true
29206         
29207     });
29208 };
29209
29210 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29211     
29212     boxes : 0,
29213     inputName : '',
29214     thumbSize : 300,
29215     multiple : true,
29216     files : false,
29217     method : 'POST',
29218     url : '',
29219     paramName : 'imageUpload',
29220     toolTipName : 'filename',
29221     fieldLabel : '',
29222     labelWidth : 4,
29223     labelAlign : 'left',
29224     editable : true,
29225     delegates : false,
29226     xhr : false, 
29227     
29228     labellg : 0,
29229     labelmd : 0,
29230     labelsm : 0,
29231     labelxs : 0,
29232     
29233     getAutoCreate : function()
29234     {   
29235         var managerWidget = {
29236             tag : 'div',
29237             cls : 'roo-document-manager',
29238             cn : [
29239                 {
29240                     tag : 'input',
29241                     cls : 'roo-document-manager-selector',
29242                     type : 'file'
29243                 },
29244                 {
29245                     tag : 'div',
29246                     cls : 'roo-document-manager-uploader',
29247                     cn : [
29248                         {
29249                             tag : 'div',
29250                             cls : 'roo-document-manager-upload-btn',
29251                             html : '<i class="fa fa-plus"></i>'
29252                         }
29253                     ]
29254                     
29255                 }
29256             ]
29257         };
29258         
29259         var content = [
29260             {
29261                 tag : 'div',
29262                 cls : 'column col-md-12',
29263                 cn : managerWidget
29264             }
29265         ];
29266         
29267         if(this.fieldLabel.length){
29268             
29269             content = [
29270                 {
29271                     tag : 'div',
29272                     cls : 'column col-md-12',
29273                     html : this.fieldLabel
29274                 },
29275                 {
29276                     tag : 'div',
29277                     cls : 'column col-md-12',
29278                     cn : managerWidget
29279                 }
29280             ];
29281
29282             if(this.labelAlign == 'left'){
29283                 content = [
29284                     {
29285                         tag : 'div',
29286                         cls : 'column',
29287                         html : this.fieldLabel
29288                     },
29289                     {
29290                         tag : 'div',
29291                         cls : 'column',
29292                         cn : managerWidget
29293                     }
29294                 ];
29295                 
29296                 if(this.labelWidth > 12){
29297                     content[0].style = "width: " + this.labelWidth + 'px';
29298                 }
29299
29300                 if(this.labelWidth < 13 && this.labelmd == 0){
29301                     this.labelmd = this.labelWidth;
29302                 }
29303
29304                 if(this.labellg > 0){
29305                     content[0].cls += ' col-lg-' + this.labellg;
29306                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29307                 }
29308
29309                 if(this.labelmd > 0){
29310                     content[0].cls += ' col-md-' + this.labelmd;
29311                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29312                 }
29313
29314                 if(this.labelsm > 0){
29315                     content[0].cls += ' col-sm-' + this.labelsm;
29316                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29317                 }
29318
29319                 if(this.labelxs > 0){
29320                     content[0].cls += ' col-xs-' + this.labelxs;
29321                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29322                 }
29323                 
29324             }
29325         }
29326         
29327         var cfg = {
29328             tag : 'div',
29329             cls : 'row clearfix',
29330             cn : content
29331         };
29332         
29333         return cfg;
29334         
29335     },
29336     
29337     initEvents : function()
29338     {
29339         this.managerEl = this.el.select('.roo-document-manager', true).first();
29340         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29341         
29342         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29343         this.selectorEl.hide();
29344         
29345         if(this.multiple){
29346             this.selectorEl.attr('multiple', 'multiple');
29347         }
29348         
29349         this.selectorEl.on('change', this.onFileSelected, this);
29350         
29351         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29352         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29353         
29354         this.uploader.on('click', this.onUploaderClick, this);
29355         
29356         this.renderProgressDialog();
29357         
29358         var _this = this;
29359         
29360         window.addEventListener("resize", function() { _this.refresh(); } );
29361         
29362         this.fireEvent('initial', this);
29363     },
29364     
29365     renderProgressDialog : function()
29366     {
29367         var _this = this;
29368         
29369         this.progressDialog = new Roo.bootstrap.Modal({
29370             cls : 'roo-document-manager-progress-dialog',
29371             allow_close : false,
29372             animate : false,
29373             title : '',
29374             buttons : [
29375                 {
29376                     name  :'cancel',
29377                     weight : 'danger',
29378                     html : 'Cancel'
29379                 }
29380             ], 
29381             listeners : { 
29382                 btnclick : function() {
29383                     _this.uploadCancel();
29384                     this.hide();
29385                 }
29386             }
29387         });
29388          
29389         this.progressDialog.render(Roo.get(document.body));
29390          
29391         this.progress = new Roo.bootstrap.Progress({
29392             cls : 'roo-document-manager-progress',
29393             active : true,
29394             striped : true
29395         });
29396         
29397         this.progress.render(this.progressDialog.getChildContainer());
29398         
29399         this.progressBar = new Roo.bootstrap.ProgressBar({
29400             cls : 'roo-document-manager-progress-bar',
29401             aria_valuenow : 0,
29402             aria_valuemin : 0,
29403             aria_valuemax : 12,
29404             panel : 'success'
29405         });
29406         
29407         this.progressBar.render(this.progress.getChildContainer());
29408     },
29409     
29410     onUploaderClick : function(e)
29411     {
29412         e.preventDefault();
29413      
29414         if(this.fireEvent('beforeselectfile', this) != false){
29415             this.selectorEl.dom.click();
29416         }
29417         
29418     },
29419     
29420     onFileSelected : function(e)
29421     {
29422         e.preventDefault();
29423         
29424         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29425             return;
29426         }
29427         
29428         Roo.each(this.selectorEl.dom.files, function(file){
29429             if(this.fireEvent('inspect', this, file) != false){
29430                 this.files.push(file);
29431             }
29432         }, this);
29433         
29434         this.queue();
29435         
29436     },
29437     
29438     queue : function()
29439     {
29440         this.selectorEl.dom.value = '';
29441         
29442         if(!this.files || !this.files.length){
29443             return;
29444         }
29445         
29446         if(this.boxes > 0 && this.files.length > this.boxes){
29447             this.files = this.files.slice(0, this.boxes);
29448         }
29449         
29450         this.uploader.show();
29451         
29452         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29453             this.uploader.hide();
29454         }
29455         
29456         var _this = this;
29457         
29458         var files = [];
29459         
29460         var docs = [];
29461         
29462         Roo.each(this.files, function(file){
29463             
29464             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29465                 var f = this.renderPreview(file);
29466                 files.push(f);
29467                 return;
29468             }
29469             
29470             if(file.type.indexOf('image') != -1){
29471                 this.delegates.push(
29472                     (function(){
29473                         _this.process(file);
29474                     }).createDelegate(this)
29475                 );
29476         
29477                 return;
29478             }
29479             
29480             docs.push(
29481                 (function(){
29482                     _this.process(file);
29483                 }).createDelegate(this)
29484             );
29485             
29486         }, this);
29487         
29488         this.files = files;
29489         
29490         this.delegates = this.delegates.concat(docs);
29491         
29492         if(!this.delegates.length){
29493             this.refresh();
29494             return;
29495         }
29496         
29497         this.progressBar.aria_valuemax = this.delegates.length;
29498         
29499         this.arrange();
29500         
29501         return;
29502     },
29503     
29504     arrange : function()
29505     {
29506         if(!this.delegates.length){
29507             this.progressDialog.hide();
29508             this.refresh();
29509             return;
29510         }
29511         
29512         var delegate = this.delegates.shift();
29513         
29514         this.progressDialog.show();
29515         
29516         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29517         
29518         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29519         
29520         delegate();
29521     },
29522     
29523     refresh : function()
29524     {
29525         this.uploader.show();
29526         
29527         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29528             this.uploader.hide();
29529         }
29530         
29531         Roo.isTouch ? this.closable(false) : this.closable(true);
29532         
29533         this.fireEvent('refresh', this);
29534     },
29535     
29536     onRemove : function(e, el, o)
29537     {
29538         e.preventDefault();
29539         
29540         this.fireEvent('remove', this, o);
29541         
29542     },
29543     
29544     remove : function(o)
29545     {
29546         var files = [];
29547         
29548         Roo.each(this.files, function(file){
29549             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29550                 files.push(file);
29551                 return;
29552             }
29553
29554             o.target.remove();
29555
29556         }, this);
29557         
29558         this.files = files;
29559         
29560         this.refresh();
29561     },
29562     
29563     clear : function()
29564     {
29565         Roo.each(this.files, function(file){
29566             if(!file.target){
29567                 return;
29568             }
29569             
29570             file.target.remove();
29571
29572         }, this);
29573         
29574         this.files = [];
29575         
29576         this.refresh();
29577     },
29578     
29579     onClick : function(e, el, o)
29580     {
29581         e.preventDefault();
29582         
29583         this.fireEvent('click', this, o);
29584         
29585     },
29586     
29587     closable : function(closable)
29588     {
29589         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29590             
29591             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29592             
29593             if(closable){
29594                 el.show();
29595                 return;
29596             }
29597             
29598             el.hide();
29599             
29600         }, this);
29601     },
29602     
29603     xhrOnLoad : function(xhr)
29604     {
29605         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29606             el.remove();
29607         }, this);
29608         
29609         if (xhr.readyState !== 4) {
29610             this.arrange();
29611             this.fireEvent('exception', this, xhr);
29612             return;
29613         }
29614
29615         var response = Roo.decode(xhr.responseText);
29616         
29617         if(!response.success){
29618             this.arrange();
29619             this.fireEvent('exception', this, xhr);
29620             return;
29621         }
29622         
29623         var file = this.renderPreview(response.data);
29624         
29625         this.files.push(file);
29626         
29627         this.arrange();
29628         
29629         this.fireEvent('afterupload', this, xhr);
29630         
29631     },
29632     
29633     xhrOnError : function(xhr)
29634     {
29635         Roo.log('xhr on error');
29636         
29637         var response = Roo.decode(xhr.responseText);
29638           
29639         Roo.log(response);
29640         
29641         this.arrange();
29642     },
29643     
29644     process : function(file)
29645     {
29646         if(this.fireEvent('process', this, file) !== false){
29647             if(this.editable && file.type.indexOf('image') != -1){
29648                 this.fireEvent('edit', this, file);
29649                 return;
29650             }
29651
29652             this.uploadStart(file, false);
29653
29654             return;
29655         }
29656         
29657     },
29658     
29659     uploadStart : function(file, crop)
29660     {
29661         this.xhr = new XMLHttpRequest();
29662         
29663         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29664             this.arrange();
29665             return;
29666         }
29667         
29668         file.xhr = this.xhr;
29669             
29670         this.managerEl.createChild({
29671             tag : 'div',
29672             cls : 'roo-document-manager-loading',
29673             cn : [
29674                 {
29675                     tag : 'div',
29676                     tooltip : file.name,
29677                     cls : 'roo-document-manager-thumb',
29678                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29679                 }
29680             ]
29681
29682         });
29683
29684         this.xhr.open(this.method, this.url, true);
29685         
29686         var headers = {
29687             "Accept": "application/json",
29688             "Cache-Control": "no-cache",
29689             "X-Requested-With": "XMLHttpRequest"
29690         };
29691         
29692         for (var headerName in headers) {
29693             var headerValue = headers[headerName];
29694             if (headerValue) {
29695                 this.xhr.setRequestHeader(headerName, headerValue);
29696             }
29697         }
29698         
29699         var _this = this;
29700         
29701         this.xhr.onload = function()
29702         {
29703             _this.xhrOnLoad(_this.xhr);
29704         }
29705         
29706         this.xhr.onerror = function()
29707         {
29708             _this.xhrOnError(_this.xhr);
29709         }
29710         
29711         var formData = new FormData();
29712
29713         formData.append('returnHTML', 'NO');
29714         
29715         if(crop){
29716             formData.append('crop', crop);
29717         }
29718         
29719         formData.append(this.paramName, file, file.name);
29720         
29721         var options = {
29722             file : file, 
29723             manually : false
29724         };
29725         
29726         if(this.fireEvent('prepare', this, formData, options) != false){
29727             
29728             if(options.manually){
29729                 return;
29730             }
29731             
29732             this.xhr.send(formData);
29733             return;
29734         };
29735         
29736         this.uploadCancel();
29737     },
29738     
29739     uploadCancel : function()
29740     {
29741         if (this.xhr) {
29742             this.xhr.abort();
29743         }
29744         
29745         this.delegates = [];
29746         
29747         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29748             el.remove();
29749         }, this);
29750         
29751         this.arrange();
29752     },
29753     
29754     renderPreview : function(file)
29755     {
29756         if(typeof(file.target) != 'undefined' && file.target){
29757             return file;
29758         }
29759         
29760         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29761         
29762         var previewEl = this.managerEl.createChild({
29763             tag : 'div',
29764             cls : 'roo-document-manager-preview',
29765             cn : [
29766                 {
29767                     tag : 'div',
29768                     tooltip : file[this.toolTipName],
29769                     cls : 'roo-document-manager-thumb',
29770                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29771                 },
29772                 {
29773                     tag : 'button',
29774                     cls : 'close',
29775                     html : '<i class="fa fa-times-circle"></i>'
29776                 }
29777             ]
29778         });
29779
29780         var close = previewEl.select('button.close', true).first();
29781
29782         close.on('click', this.onRemove, this, file);
29783
29784         file.target = previewEl;
29785
29786         var image = previewEl.select('img', true).first();
29787         
29788         var _this = this;
29789         
29790         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29791         
29792         image.on('click', this.onClick, this, file);
29793         
29794         this.fireEvent('previewrendered', this, file);
29795         
29796         return file;
29797         
29798     },
29799     
29800     onPreviewLoad : function(file, image)
29801     {
29802         if(typeof(file.target) == 'undefined' || !file.target){
29803             return;
29804         }
29805         
29806         var width = image.dom.naturalWidth || image.dom.width;
29807         var height = image.dom.naturalHeight || image.dom.height;
29808         
29809         if(!this.previewResize) {
29810             return;
29811         }
29812         
29813         if(width > height){
29814             file.target.addClass('wide');
29815             return;
29816         }
29817         
29818         file.target.addClass('tall');
29819         return;
29820         
29821     },
29822     
29823     uploadFromSource : function(file, crop)
29824     {
29825         this.xhr = new XMLHttpRequest();
29826         
29827         this.managerEl.createChild({
29828             tag : 'div',
29829             cls : 'roo-document-manager-loading',
29830             cn : [
29831                 {
29832                     tag : 'div',
29833                     tooltip : file.name,
29834                     cls : 'roo-document-manager-thumb',
29835                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29836                 }
29837             ]
29838
29839         });
29840
29841         this.xhr.open(this.method, this.url, true);
29842         
29843         var headers = {
29844             "Accept": "application/json",
29845             "Cache-Control": "no-cache",
29846             "X-Requested-With": "XMLHttpRequest"
29847         };
29848         
29849         for (var headerName in headers) {
29850             var headerValue = headers[headerName];
29851             if (headerValue) {
29852                 this.xhr.setRequestHeader(headerName, headerValue);
29853             }
29854         }
29855         
29856         var _this = this;
29857         
29858         this.xhr.onload = function()
29859         {
29860             _this.xhrOnLoad(_this.xhr);
29861         }
29862         
29863         this.xhr.onerror = function()
29864         {
29865             _this.xhrOnError(_this.xhr);
29866         }
29867         
29868         var formData = new FormData();
29869
29870         formData.append('returnHTML', 'NO');
29871         
29872         formData.append('crop', crop);
29873         
29874         if(typeof(file.filename) != 'undefined'){
29875             formData.append('filename', file.filename);
29876         }
29877         
29878         if(typeof(file.mimetype) != 'undefined'){
29879             formData.append('mimetype', file.mimetype);
29880         }
29881         
29882         Roo.log(formData);
29883         
29884         if(this.fireEvent('prepare', this, formData) != false){
29885             this.xhr.send(formData);
29886         };
29887     }
29888 });
29889
29890 /*
29891 * Licence: LGPL
29892 */
29893
29894 /**
29895  * @class Roo.bootstrap.DocumentViewer
29896  * @extends Roo.bootstrap.Component
29897  * Bootstrap DocumentViewer class
29898  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29899  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29900  * 
29901  * @constructor
29902  * Create a new DocumentViewer
29903  * @param {Object} config The config object
29904  */
29905
29906 Roo.bootstrap.DocumentViewer = function(config){
29907     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29908     
29909     this.addEvents({
29910         /**
29911          * @event initial
29912          * Fire after initEvent
29913          * @param {Roo.bootstrap.DocumentViewer} this
29914          */
29915         "initial" : true,
29916         /**
29917          * @event click
29918          * Fire after click
29919          * @param {Roo.bootstrap.DocumentViewer} this
29920          */
29921         "click" : true,
29922         /**
29923          * @event download
29924          * Fire after download button
29925          * @param {Roo.bootstrap.DocumentViewer} this
29926          */
29927         "download" : true,
29928         /**
29929          * @event trash
29930          * Fire after trash button
29931          * @param {Roo.bootstrap.DocumentViewer} this
29932          */
29933         "trash" : true
29934         
29935     });
29936 };
29937
29938 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29939     
29940     showDownload : true,
29941     
29942     showTrash : true,
29943     
29944     getAutoCreate : function()
29945     {
29946         var cfg = {
29947             tag : 'div',
29948             cls : 'roo-document-viewer',
29949             cn : [
29950                 {
29951                     tag : 'div',
29952                     cls : 'roo-document-viewer-body',
29953                     cn : [
29954                         {
29955                             tag : 'div',
29956                             cls : 'roo-document-viewer-thumb',
29957                             cn : [
29958                                 {
29959                                     tag : 'img',
29960                                     cls : 'roo-document-viewer-image'
29961                                 }
29962                             ]
29963                         }
29964                     ]
29965                 },
29966                 {
29967                     tag : 'div',
29968                     cls : 'roo-document-viewer-footer',
29969                     cn : {
29970                         tag : 'div',
29971                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29972                         cn : [
29973                             {
29974                                 tag : 'div',
29975                                 cls : 'btn-group roo-document-viewer-download',
29976                                 cn : [
29977                                     {
29978                                         tag : 'button',
29979                                         cls : 'btn btn-default',
29980                                         html : '<i class="fa fa-download"></i>'
29981                                     }
29982                                 ]
29983                             },
29984                             {
29985                                 tag : 'div',
29986                                 cls : 'btn-group roo-document-viewer-trash',
29987                                 cn : [
29988                                     {
29989                                         tag : 'button',
29990                                         cls : 'btn btn-default',
29991                                         html : '<i class="fa fa-trash"></i>'
29992                                     }
29993                                 ]
29994                             }
29995                         ]
29996                     }
29997                 }
29998             ]
29999         };
30000         
30001         return cfg;
30002     },
30003     
30004     initEvents : function()
30005     {
30006         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30007         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30008         
30009         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30010         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30011         
30012         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30013         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30014         
30015         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30016         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30017         
30018         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30019         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30020         
30021         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30022         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30023         
30024         this.bodyEl.on('click', this.onClick, this);
30025         this.downloadBtn.on('click', this.onDownload, this);
30026         this.trashBtn.on('click', this.onTrash, this);
30027         
30028         this.downloadBtn.hide();
30029         this.trashBtn.hide();
30030         
30031         if(this.showDownload){
30032             this.downloadBtn.show();
30033         }
30034         
30035         if(this.showTrash){
30036             this.trashBtn.show();
30037         }
30038         
30039         if(!this.showDownload && !this.showTrash) {
30040             this.footerEl.hide();
30041         }
30042         
30043     },
30044     
30045     initial : function()
30046     {
30047         this.fireEvent('initial', this);
30048         
30049     },
30050     
30051     onClick : function(e)
30052     {
30053         e.preventDefault();
30054         
30055         this.fireEvent('click', this);
30056     },
30057     
30058     onDownload : function(e)
30059     {
30060         e.preventDefault();
30061         
30062         this.fireEvent('download', this);
30063     },
30064     
30065     onTrash : function(e)
30066     {
30067         e.preventDefault();
30068         
30069         this.fireEvent('trash', this);
30070     }
30071     
30072 });
30073 /*
30074  * - LGPL
30075  *
30076  * nav progress bar
30077  * 
30078  */
30079
30080 /**
30081  * @class Roo.bootstrap.NavProgressBar
30082  * @extends Roo.bootstrap.Component
30083  * Bootstrap NavProgressBar class
30084  * 
30085  * @constructor
30086  * Create a new nav progress bar
30087  * @param {Object} config The config object
30088  */
30089
30090 Roo.bootstrap.NavProgressBar = function(config){
30091     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30092
30093     this.bullets = this.bullets || [];
30094    
30095 //    Roo.bootstrap.NavProgressBar.register(this);
30096      this.addEvents({
30097         /**
30098              * @event changed
30099              * Fires when the active item changes
30100              * @param {Roo.bootstrap.NavProgressBar} this
30101              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30102              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30103          */
30104         'changed': true
30105      });
30106     
30107 };
30108
30109 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30110     
30111     bullets : [],
30112     barItems : [],
30113     
30114     getAutoCreate : function()
30115     {
30116         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30117         
30118         cfg = {
30119             tag : 'div',
30120             cls : 'roo-navigation-bar-group',
30121             cn : [
30122                 {
30123                     tag : 'div',
30124                     cls : 'roo-navigation-top-bar'
30125                 },
30126                 {
30127                     tag : 'div',
30128                     cls : 'roo-navigation-bullets-bar',
30129                     cn : [
30130                         {
30131                             tag : 'ul',
30132                             cls : 'roo-navigation-bar'
30133                         }
30134                     ]
30135                 },
30136                 
30137                 {
30138                     tag : 'div',
30139                     cls : 'roo-navigation-bottom-bar'
30140                 }
30141             ]
30142             
30143         };
30144         
30145         return cfg;
30146         
30147     },
30148     
30149     initEvents: function() 
30150     {
30151         
30152     },
30153     
30154     onRender : function(ct, position) 
30155     {
30156         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30157         
30158         if(this.bullets.length){
30159             Roo.each(this.bullets, function(b){
30160                this.addItem(b);
30161             }, this);
30162         }
30163         
30164         this.format();
30165         
30166     },
30167     
30168     addItem : function(cfg)
30169     {
30170         var item = new Roo.bootstrap.NavProgressItem(cfg);
30171         
30172         item.parentId = this.id;
30173         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30174         
30175         if(cfg.html){
30176             var top = new Roo.bootstrap.Element({
30177                 tag : 'div',
30178                 cls : 'roo-navigation-bar-text'
30179             });
30180             
30181             var bottom = new Roo.bootstrap.Element({
30182                 tag : 'div',
30183                 cls : 'roo-navigation-bar-text'
30184             });
30185             
30186             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30187             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30188             
30189             var topText = new Roo.bootstrap.Element({
30190                 tag : 'span',
30191                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30192             });
30193             
30194             var bottomText = new Roo.bootstrap.Element({
30195                 tag : 'span',
30196                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30197             });
30198             
30199             topText.onRender(top.el, null);
30200             bottomText.onRender(bottom.el, null);
30201             
30202             item.topEl = top;
30203             item.bottomEl = bottom;
30204         }
30205         
30206         this.barItems.push(item);
30207         
30208         return item;
30209     },
30210     
30211     getActive : function()
30212     {
30213         var active = false;
30214         
30215         Roo.each(this.barItems, function(v){
30216             
30217             if (!v.isActive()) {
30218                 return;
30219             }
30220             
30221             active = v;
30222             return false;
30223             
30224         });
30225         
30226         return active;
30227     },
30228     
30229     setActiveItem : function(item)
30230     {
30231         var prev = false;
30232         
30233         Roo.each(this.barItems, function(v){
30234             if (v.rid == item.rid) {
30235                 return ;
30236             }
30237             
30238             if (v.isActive()) {
30239                 v.setActive(false);
30240                 prev = v;
30241             }
30242         });
30243
30244         item.setActive(true);
30245         
30246         this.fireEvent('changed', this, item, prev);
30247     },
30248     
30249     getBarItem: function(rid)
30250     {
30251         var ret = false;
30252         
30253         Roo.each(this.barItems, function(e) {
30254             if (e.rid != rid) {
30255                 return;
30256             }
30257             
30258             ret =  e;
30259             return false;
30260         });
30261         
30262         return ret;
30263     },
30264     
30265     indexOfItem : function(item)
30266     {
30267         var index = false;
30268         
30269         Roo.each(this.barItems, function(v, i){
30270             
30271             if (v.rid != item.rid) {
30272                 return;
30273             }
30274             
30275             index = i;
30276             return false
30277         });
30278         
30279         return index;
30280     },
30281     
30282     setActiveNext : function()
30283     {
30284         var i = this.indexOfItem(this.getActive());
30285         
30286         if (i > this.barItems.length) {
30287             return;
30288         }
30289         
30290         this.setActiveItem(this.barItems[i+1]);
30291     },
30292     
30293     setActivePrev : function()
30294     {
30295         var i = this.indexOfItem(this.getActive());
30296         
30297         if (i  < 1) {
30298             return;
30299         }
30300         
30301         this.setActiveItem(this.barItems[i-1]);
30302     },
30303     
30304     format : function()
30305     {
30306         if(!this.barItems.length){
30307             return;
30308         }
30309      
30310         var width = 100 / this.barItems.length;
30311         
30312         Roo.each(this.barItems, function(i){
30313             i.el.setStyle('width', width + '%');
30314             i.topEl.el.setStyle('width', width + '%');
30315             i.bottomEl.el.setStyle('width', width + '%');
30316         }, this);
30317         
30318     }
30319     
30320 });
30321 /*
30322  * - LGPL
30323  *
30324  * Nav Progress Item
30325  * 
30326  */
30327
30328 /**
30329  * @class Roo.bootstrap.NavProgressItem
30330  * @extends Roo.bootstrap.Component
30331  * Bootstrap NavProgressItem class
30332  * @cfg {String} rid the reference id
30333  * @cfg {Boolean} active (true|false) Is item active default false
30334  * @cfg {Boolean} disabled (true|false) Is item active default false
30335  * @cfg {String} html
30336  * @cfg {String} position (top|bottom) text position default bottom
30337  * @cfg {String} icon show icon instead of number
30338  * 
30339  * @constructor
30340  * Create a new NavProgressItem
30341  * @param {Object} config The config object
30342  */
30343 Roo.bootstrap.NavProgressItem = function(config){
30344     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30345     this.addEvents({
30346         // raw events
30347         /**
30348          * @event click
30349          * The raw click event for the entire grid.
30350          * @param {Roo.bootstrap.NavProgressItem} this
30351          * @param {Roo.EventObject} e
30352          */
30353         "click" : true
30354     });
30355    
30356 };
30357
30358 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30359     
30360     rid : '',
30361     active : false,
30362     disabled : false,
30363     html : '',
30364     position : 'bottom',
30365     icon : false,
30366     
30367     getAutoCreate : function()
30368     {
30369         var iconCls = 'roo-navigation-bar-item-icon';
30370         
30371         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30372         
30373         var cfg = {
30374             tag: 'li',
30375             cls: 'roo-navigation-bar-item',
30376             cn : [
30377                 {
30378                     tag : 'i',
30379                     cls : iconCls
30380                 }
30381             ]
30382         };
30383         
30384         if(this.active){
30385             cfg.cls += ' active';
30386         }
30387         if(this.disabled){
30388             cfg.cls += ' disabled';
30389         }
30390         
30391         return cfg;
30392     },
30393     
30394     disable : function()
30395     {
30396         this.setDisabled(true);
30397     },
30398     
30399     enable : function()
30400     {
30401         this.setDisabled(false);
30402     },
30403     
30404     initEvents: function() 
30405     {
30406         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30407         
30408         this.iconEl.on('click', this.onClick, this);
30409     },
30410     
30411     onClick : function(e)
30412     {
30413         e.preventDefault();
30414         
30415         if(this.disabled){
30416             return;
30417         }
30418         
30419         if(this.fireEvent('click', this, e) === false){
30420             return;
30421         };
30422         
30423         this.parent().setActiveItem(this);
30424     },
30425     
30426     isActive: function () 
30427     {
30428         return this.active;
30429     },
30430     
30431     setActive : function(state)
30432     {
30433         if(this.active == state){
30434             return;
30435         }
30436         
30437         this.active = state;
30438         
30439         if (state) {
30440             this.el.addClass('active');
30441             return;
30442         }
30443         
30444         this.el.removeClass('active');
30445         
30446         return;
30447     },
30448     
30449     setDisabled : function(state)
30450     {
30451         if(this.disabled == state){
30452             return;
30453         }
30454         
30455         this.disabled = state;
30456         
30457         if (state) {
30458             this.el.addClass('disabled');
30459             return;
30460         }
30461         
30462         this.el.removeClass('disabled');
30463     },
30464     
30465     tooltipEl : function()
30466     {
30467         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30468     }
30469 });
30470  
30471
30472  /*
30473  * - LGPL
30474  *
30475  * FieldLabel
30476  * 
30477  */
30478
30479 /**
30480  * @class Roo.bootstrap.FieldLabel
30481  * @extends Roo.bootstrap.Component
30482  * Bootstrap FieldLabel class
30483  * @cfg {String} html contents of the element
30484  * @cfg {String} tag tag of the element default label
30485  * @cfg {String} cls class of the element
30486  * @cfg {String} target label target 
30487  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30488  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30489  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30490  * @cfg {String} iconTooltip default "This field is required"
30491  * @cfg {String} indicatorpos (left|right) default left
30492  * 
30493  * @constructor
30494  * Create a new FieldLabel
30495  * @param {Object} config The config object
30496  */
30497
30498 Roo.bootstrap.FieldLabel = function(config){
30499     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30500     
30501     this.addEvents({
30502             /**
30503              * @event invalid
30504              * Fires after the field has been marked as invalid.
30505              * @param {Roo.form.FieldLabel} this
30506              * @param {String} msg The validation message
30507              */
30508             invalid : true,
30509             /**
30510              * @event valid
30511              * Fires after the field has been validated with no errors.
30512              * @param {Roo.form.FieldLabel} this
30513              */
30514             valid : true
30515         });
30516 };
30517
30518 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30519     
30520     tag: 'label',
30521     cls: '',
30522     html: '',
30523     target: '',
30524     allowBlank : true,
30525     invalidClass : 'has-warning',
30526     validClass : 'has-success',
30527     iconTooltip : 'This field is required',
30528     indicatorpos : 'left',
30529     
30530     getAutoCreate : function(){
30531         
30532         var cls = "";
30533         if (!this.allowBlank) {
30534             cls  = "visible";
30535         }
30536         
30537         var cfg = {
30538             tag : this.tag,
30539             cls : 'roo-bootstrap-field-label ' + this.cls,
30540             for : this.target,
30541             cn : [
30542                 {
30543                     tag : 'i',
30544                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30545                     tooltip : this.iconTooltip
30546                 },
30547                 {
30548                     tag : 'span',
30549                     html : this.html
30550                 }
30551             ] 
30552         };
30553         
30554         if(this.indicatorpos == 'right'){
30555             var cfg = {
30556                 tag : this.tag,
30557                 cls : 'roo-bootstrap-field-label ' + this.cls,
30558                 for : this.target,
30559                 cn : [
30560                     {
30561                         tag : 'span',
30562                         html : this.html
30563                     },
30564                     {
30565                         tag : 'i',
30566                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30567                         tooltip : this.iconTooltip
30568                     }
30569                 ] 
30570             };
30571         }
30572         
30573         return cfg;
30574     },
30575     
30576     initEvents: function() 
30577     {
30578         Roo.bootstrap.Element.superclass.initEvents.call(this);
30579         
30580         this.indicator = this.indicatorEl();
30581         
30582         if(this.indicator){
30583             this.indicator.removeClass('visible');
30584             this.indicator.addClass('invisible');
30585         }
30586         
30587         Roo.bootstrap.FieldLabel.register(this);
30588     },
30589     
30590     indicatorEl : function()
30591     {
30592         var indicator = this.el.select('i.roo-required-indicator',true).first();
30593         
30594         if(!indicator){
30595             return false;
30596         }
30597         
30598         return indicator;
30599         
30600     },
30601     
30602     /**
30603      * Mark this field as valid
30604      */
30605     markValid : function()
30606     {
30607         if(this.indicator){
30608             this.indicator.removeClass('visible');
30609             this.indicator.addClass('invisible');
30610         }
30611         if (Roo.bootstrap.version == 3) {
30612             this.el.removeClass(this.invalidClass);
30613             this.el.addClass(this.validClass);
30614         } else {
30615             this.el.removeClass('is-invalid');
30616             this.el.addClass('is-valid');
30617         }
30618         
30619         
30620         this.fireEvent('valid', this);
30621     },
30622     
30623     /**
30624      * Mark this field as invalid
30625      * @param {String} msg The validation message
30626      */
30627     markInvalid : function(msg)
30628     {
30629         if(this.indicator){
30630             this.indicator.removeClass('invisible');
30631             this.indicator.addClass('visible');
30632         }
30633           if (Roo.bootstrap.version == 3) {
30634             this.el.removeClass(this.validClass);
30635             this.el.addClass(this.invalidClass);
30636         } else {
30637             this.el.removeClass('is-valid');
30638             this.el.addClass('is-invalid');
30639         }
30640         
30641         
30642         this.fireEvent('invalid', this, msg);
30643     }
30644     
30645    
30646 });
30647
30648 Roo.apply(Roo.bootstrap.FieldLabel, {
30649     
30650     groups: {},
30651     
30652      /**
30653     * register a FieldLabel Group
30654     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30655     */
30656     register : function(label)
30657     {
30658         if(this.groups.hasOwnProperty(label.target)){
30659             return;
30660         }
30661      
30662         this.groups[label.target] = label;
30663         
30664     },
30665     /**
30666     * fetch a FieldLabel Group based on the target
30667     * @param {string} target
30668     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30669     */
30670     get: function(target) {
30671         if (typeof(this.groups[target]) == 'undefined') {
30672             return false;
30673         }
30674         
30675         return this.groups[target] ;
30676     }
30677 });
30678
30679  
30680
30681  /*
30682  * - LGPL
30683  *
30684  * page DateSplitField.
30685  * 
30686  */
30687
30688
30689 /**
30690  * @class Roo.bootstrap.DateSplitField
30691  * @extends Roo.bootstrap.Component
30692  * Bootstrap DateSplitField class
30693  * @cfg {string} fieldLabel - the label associated
30694  * @cfg {Number} labelWidth set the width of label (0-12)
30695  * @cfg {String} labelAlign (top|left)
30696  * @cfg {Boolean} dayAllowBlank (true|false) default false
30697  * @cfg {Boolean} monthAllowBlank (true|false) default false
30698  * @cfg {Boolean} yearAllowBlank (true|false) default false
30699  * @cfg {string} dayPlaceholder 
30700  * @cfg {string} monthPlaceholder
30701  * @cfg {string} yearPlaceholder
30702  * @cfg {string} dayFormat default 'd'
30703  * @cfg {string} monthFormat default 'm'
30704  * @cfg {string} yearFormat default 'Y'
30705  * @cfg {Number} labellg set the width of label (1-12)
30706  * @cfg {Number} labelmd set the width of label (1-12)
30707  * @cfg {Number} labelsm set the width of label (1-12)
30708  * @cfg {Number} labelxs set the width of label (1-12)
30709
30710  *     
30711  * @constructor
30712  * Create a new DateSplitField
30713  * @param {Object} config The config object
30714  */
30715
30716 Roo.bootstrap.DateSplitField = function(config){
30717     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30718     
30719     this.addEvents({
30720         // raw events
30721          /**
30722          * @event years
30723          * getting the data of years
30724          * @param {Roo.bootstrap.DateSplitField} this
30725          * @param {Object} years
30726          */
30727         "years" : true,
30728         /**
30729          * @event days
30730          * getting the data of days
30731          * @param {Roo.bootstrap.DateSplitField} this
30732          * @param {Object} days
30733          */
30734         "days" : true,
30735         /**
30736          * @event invalid
30737          * Fires after the field has been marked as invalid.
30738          * @param {Roo.form.Field} this
30739          * @param {String} msg The validation message
30740          */
30741         invalid : true,
30742        /**
30743          * @event valid
30744          * Fires after the field has been validated with no errors.
30745          * @param {Roo.form.Field} this
30746          */
30747         valid : true
30748     });
30749 };
30750
30751 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30752     
30753     fieldLabel : '',
30754     labelAlign : 'top',
30755     labelWidth : 3,
30756     dayAllowBlank : false,
30757     monthAllowBlank : false,
30758     yearAllowBlank : false,
30759     dayPlaceholder : '',
30760     monthPlaceholder : '',
30761     yearPlaceholder : '',
30762     dayFormat : 'd',
30763     monthFormat : 'm',
30764     yearFormat : 'Y',
30765     isFormField : true,
30766     labellg : 0,
30767     labelmd : 0,
30768     labelsm : 0,
30769     labelxs : 0,
30770     
30771     getAutoCreate : function()
30772     {
30773         var cfg = {
30774             tag : 'div',
30775             cls : 'row roo-date-split-field-group',
30776             cn : [
30777                 {
30778                     tag : 'input',
30779                     type : 'hidden',
30780                     cls : 'form-hidden-field roo-date-split-field-group-value',
30781                     name : this.name
30782                 }
30783             ]
30784         };
30785         
30786         var labelCls = 'col-md-12';
30787         var contentCls = 'col-md-4';
30788         
30789         if(this.fieldLabel){
30790             
30791             var label = {
30792                 tag : 'div',
30793                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30794                 cn : [
30795                     {
30796                         tag : 'label',
30797                         html : this.fieldLabel
30798                     }
30799                 ]
30800             };
30801             
30802             if(this.labelAlign == 'left'){
30803             
30804                 if(this.labelWidth > 12){
30805                     label.style = "width: " + this.labelWidth + 'px';
30806                 }
30807
30808                 if(this.labelWidth < 13 && this.labelmd == 0){
30809                     this.labelmd = this.labelWidth;
30810                 }
30811
30812                 if(this.labellg > 0){
30813                     labelCls = ' col-lg-' + this.labellg;
30814                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30815                 }
30816
30817                 if(this.labelmd > 0){
30818                     labelCls = ' col-md-' + this.labelmd;
30819                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30820                 }
30821
30822                 if(this.labelsm > 0){
30823                     labelCls = ' col-sm-' + this.labelsm;
30824                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30825                 }
30826
30827                 if(this.labelxs > 0){
30828                     labelCls = ' col-xs-' + this.labelxs;
30829                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30830                 }
30831             }
30832             
30833             label.cls += ' ' + labelCls;
30834             
30835             cfg.cn.push(label);
30836         }
30837         
30838         Roo.each(['day', 'month', 'year'], function(t){
30839             cfg.cn.push({
30840                 tag : 'div',
30841                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30842             });
30843         }, this);
30844         
30845         return cfg;
30846     },
30847     
30848     inputEl: function ()
30849     {
30850         return this.el.select('.roo-date-split-field-group-value', true).first();
30851     },
30852     
30853     onRender : function(ct, position) 
30854     {
30855         var _this = this;
30856         
30857         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30858         
30859         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30860         
30861         this.dayField = new Roo.bootstrap.ComboBox({
30862             allowBlank : this.dayAllowBlank,
30863             alwaysQuery : true,
30864             displayField : 'value',
30865             editable : false,
30866             fieldLabel : '',
30867             forceSelection : true,
30868             mode : 'local',
30869             placeholder : this.dayPlaceholder,
30870             selectOnFocus : true,
30871             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30872             triggerAction : 'all',
30873             typeAhead : true,
30874             valueField : 'value',
30875             store : new Roo.data.SimpleStore({
30876                 data : (function() {    
30877                     var days = [];
30878                     _this.fireEvent('days', _this, days);
30879                     return days;
30880                 })(),
30881                 fields : [ 'value' ]
30882             }),
30883             listeners : {
30884                 select : function (_self, record, index)
30885                 {
30886                     _this.setValue(_this.getValue());
30887                 }
30888             }
30889         });
30890
30891         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30892         
30893         this.monthField = new Roo.bootstrap.MonthField({
30894             after : '<i class=\"fa fa-calendar\"></i>',
30895             allowBlank : this.monthAllowBlank,
30896             placeholder : this.monthPlaceholder,
30897             readOnly : true,
30898             listeners : {
30899                 render : function (_self)
30900                 {
30901                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30902                         e.preventDefault();
30903                         _self.focus();
30904                     });
30905                 },
30906                 select : function (_self, oldvalue, newvalue)
30907                 {
30908                     _this.setValue(_this.getValue());
30909                 }
30910             }
30911         });
30912         
30913         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30914         
30915         this.yearField = new Roo.bootstrap.ComboBox({
30916             allowBlank : this.yearAllowBlank,
30917             alwaysQuery : true,
30918             displayField : 'value',
30919             editable : false,
30920             fieldLabel : '',
30921             forceSelection : true,
30922             mode : 'local',
30923             placeholder : this.yearPlaceholder,
30924             selectOnFocus : true,
30925             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30926             triggerAction : 'all',
30927             typeAhead : true,
30928             valueField : 'value',
30929             store : new Roo.data.SimpleStore({
30930                 data : (function() {
30931                     var years = [];
30932                     _this.fireEvent('years', _this, years);
30933                     return years;
30934                 })(),
30935                 fields : [ 'value' ]
30936             }),
30937             listeners : {
30938                 select : function (_self, record, index)
30939                 {
30940                     _this.setValue(_this.getValue());
30941                 }
30942             }
30943         });
30944
30945         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30946     },
30947     
30948     setValue : function(v, format)
30949     {
30950         this.inputEl.dom.value = v;
30951         
30952         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30953         
30954         var d = Date.parseDate(v, f);
30955         
30956         if(!d){
30957             this.validate();
30958             return;
30959         }
30960         
30961         this.setDay(d.format(this.dayFormat));
30962         this.setMonth(d.format(this.monthFormat));
30963         this.setYear(d.format(this.yearFormat));
30964         
30965         this.validate();
30966         
30967         return;
30968     },
30969     
30970     setDay : function(v)
30971     {
30972         this.dayField.setValue(v);
30973         this.inputEl.dom.value = this.getValue();
30974         this.validate();
30975         return;
30976     },
30977     
30978     setMonth : function(v)
30979     {
30980         this.monthField.setValue(v, true);
30981         this.inputEl.dom.value = this.getValue();
30982         this.validate();
30983         return;
30984     },
30985     
30986     setYear : function(v)
30987     {
30988         this.yearField.setValue(v);
30989         this.inputEl.dom.value = this.getValue();
30990         this.validate();
30991         return;
30992     },
30993     
30994     getDay : function()
30995     {
30996         return this.dayField.getValue();
30997     },
30998     
30999     getMonth : function()
31000     {
31001         return this.monthField.getValue();
31002     },
31003     
31004     getYear : function()
31005     {
31006         return this.yearField.getValue();
31007     },
31008     
31009     getValue : function()
31010     {
31011         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31012         
31013         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31014         
31015         return date;
31016     },
31017     
31018     reset : function()
31019     {
31020         this.setDay('');
31021         this.setMonth('');
31022         this.setYear('');
31023         this.inputEl.dom.value = '';
31024         this.validate();
31025         return;
31026     },
31027     
31028     validate : function()
31029     {
31030         var d = this.dayField.validate();
31031         var m = this.monthField.validate();
31032         var y = this.yearField.validate();
31033         
31034         var valid = true;
31035         
31036         if(
31037                 (!this.dayAllowBlank && !d) ||
31038                 (!this.monthAllowBlank && !m) ||
31039                 (!this.yearAllowBlank && !y)
31040         ){
31041             valid = false;
31042         }
31043         
31044         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31045             return valid;
31046         }
31047         
31048         if(valid){
31049             this.markValid();
31050             return valid;
31051         }
31052         
31053         this.markInvalid();
31054         
31055         return valid;
31056     },
31057     
31058     markValid : function()
31059     {
31060         
31061         var label = this.el.select('label', true).first();
31062         var icon = this.el.select('i.fa-star', true).first();
31063
31064         if(label && icon){
31065             icon.remove();
31066         }
31067         
31068         this.fireEvent('valid', this);
31069     },
31070     
31071      /**
31072      * Mark this field as invalid
31073      * @param {String} msg The validation message
31074      */
31075     markInvalid : function(msg)
31076     {
31077         
31078         var label = this.el.select('label', true).first();
31079         var icon = this.el.select('i.fa-star', true).first();
31080
31081         if(label && !icon){
31082             this.el.select('.roo-date-split-field-label', true).createChild({
31083                 tag : 'i',
31084                 cls : 'text-danger fa fa-lg fa-star',
31085                 tooltip : 'This field is required',
31086                 style : 'margin-right:5px;'
31087             }, label, true);
31088         }
31089         
31090         this.fireEvent('invalid', this, msg);
31091     },
31092     
31093     clearInvalid : function()
31094     {
31095         var label = this.el.select('label', true).first();
31096         var icon = this.el.select('i.fa-star', true).first();
31097
31098         if(label && icon){
31099             icon.remove();
31100         }
31101         
31102         this.fireEvent('valid', this);
31103     },
31104     
31105     getName: function()
31106     {
31107         return this.name;
31108     }
31109     
31110 });
31111
31112  /**
31113  *
31114  * This is based on 
31115  * http://masonry.desandro.com
31116  *
31117  * The idea is to render all the bricks based on vertical width...
31118  *
31119  * The original code extends 'outlayer' - we might need to use that....
31120  * 
31121  */
31122
31123
31124 /**
31125  * @class Roo.bootstrap.LayoutMasonry
31126  * @extends Roo.bootstrap.Component
31127  * Bootstrap Layout Masonry class
31128  * 
31129  * @constructor
31130  * Create a new Element
31131  * @param {Object} config The config object
31132  */
31133
31134 Roo.bootstrap.LayoutMasonry = function(config){
31135     
31136     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31137     
31138     this.bricks = [];
31139     
31140     Roo.bootstrap.LayoutMasonry.register(this);
31141     
31142     this.addEvents({
31143         // raw events
31144         /**
31145          * @event layout
31146          * Fire after layout the items
31147          * @param {Roo.bootstrap.LayoutMasonry} this
31148          * @param {Roo.EventObject} e
31149          */
31150         "layout" : true
31151     });
31152     
31153 };
31154
31155 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31156     
31157     /**
31158      * @cfg {Boolean} isLayoutInstant = no animation?
31159      */   
31160     isLayoutInstant : false, // needed?
31161    
31162     /**
31163      * @cfg {Number} boxWidth  width of the columns
31164      */   
31165     boxWidth : 450,
31166     
31167       /**
31168      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31169      */   
31170     boxHeight : 0,
31171     
31172     /**
31173      * @cfg {Number} padWidth padding below box..
31174      */   
31175     padWidth : 10, 
31176     
31177     /**
31178      * @cfg {Number} gutter gutter width..
31179      */   
31180     gutter : 10,
31181     
31182      /**
31183      * @cfg {Number} maxCols maximum number of columns
31184      */   
31185     
31186     maxCols: 0,
31187     
31188     /**
31189      * @cfg {Boolean} isAutoInitial defalut true
31190      */   
31191     isAutoInitial : true, 
31192     
31193     containerWidth: 0,
31194     
31195     /**
31196      * @cfg {Boolean} isHorizontal defalut false
31197      */   
31198     isHorizontal : false, 
31199
31200     currentSize : null,
31201     
31202     tag: 'div',
31203     
31204     cls: '',
31205     
31206     bricks: null, //CompositeElement
31207     
31208     cols : 1,
31209     
31210     _isLayoutInited : false,
31211     
31212 //    isAlternative : false, // only use for vertical layout...
31213     
31214     /**
31215      * @cfg {Number} alternativePadWidth padding below box..
31216      */   
31217     alternativePadWidth : 50,
31218     
31219     selectedBrick : [],
31220     
31221     getAutoCreate : function(){
31222         
31223         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31224         
31225         var cfg = {
31226             tag: this.tag,
31227             cls: 'blog-masonary-wrapper ' + this.cls,
31228             cn : {
31229                 cls : 'mas-boxes masonary'
31230             }
31231         };
31232         
31233         return cfg;
31234     },
31235     
31236     getChildContainer: function( )
31237     {
31238         if (this.boxesEl) {
31239             return this.boxesEl;
31240         }
31241         
31242         this.boxesEl = this.el.select('.mas-boxes').first();
31243         
31244         return this.boxesEl;
31245     },
31246     
31247     
31248     initEvents : function()
31249     {
31250         var _this = this;
31251         
31252         if(this.isAutoInitial){
31253             Roo.log('hook children rendered');
31254             this.on('childrenrendered', function() {
31255                 Roo.log('children rendered');
31256                 _this.initial();
31257             } ,this);
31258         }
31259     },
31260     
31261     initial : function()
31262     {
31263         this.selectedBrick = [];
31264         
31265         this.currentSize = this.el.getBox(true);
31266         
31267         Roo.EventManager.onWindowResize(this.resize, this); 
31268
31269         if(!this.isAutoInitial){
31270             this.layout();
31271             return;
31272         }
31273         
31274         this.layout();
31275         
31276         return;
31277         //this.layout.defer(500,this);
31278         
31279     },
31280     
31281     resize : function()
31282     {
31283         var cs = this.el.getBox(true);
31284         
31285         if (
31286                 this.currentSize.width == cs.width && 
31287                 this.currentSize.x == cs.x && 
31288                 this.currentSize.height == cs.height && 
31289                 this.currentSize.y == cs.y 
31290         ) {
31291             Roo.log("no change in with or X or Y");
31292             return;
31293         }
31294         
31295         this.currentSize = cs;
31296         
31297         this.layout();
31298         
31299     },
31300     
31301     layout : function()
31302     {   
31303         this._resetLayout();
31304         
31305         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31306         
31307         this.layoutItems( isInstant );
31308       
31309         this._isLayoutInited = true;
31310         
31311         this.fireEvent('layout', this);
31312         
31313     },
31314     
31315     _resetLayout : function()
31316     {
31317         if(this.isHorizontal){
31318             this.horizontalMeasureColumns();
31319             return;
31320         }
31321         
31322         this.verticalMeasureColumns();
31323         
31324     },
31325     
31326     verticalMeasureColumns : function()
31327     {
31328         this.getContainerWidth();
31329         
31330 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31331 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31332 //            return;
31333 //        }
31334         
31335         var boxWidth = this.boxWidth + this.padWidth;
31336         
31337         if(this.containerWidth < this.boxWidth){
31338             boxWidth = this.containerWidth
31339         }
31340         
31341         var containerWidth = this.containerWidth;
31342         
31343         var cols = Math.floor(containerWidth / boxWidth);
31344         
31345         this.cols = Math.max( cols, 1 );
31346         
31347         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31348         
31349         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31350         
31351         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31352         
31353         this.colWidth = boxWidth + avail - this.padWidth;
31354         
31355         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31356         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31357     },
31358     
31359     horizontalMeasureColumns : function()
31360     {
31361         this.getContainerWidth();
31362         
31363         var boxWidth = this.boxWidth;
31364         
31365         if(this.containerWidth < boxWidth){
31366             boxWidth = this.containerWidth;
31367         }
31368         
31369         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31370         
31371         this.el.setHeight(boxWidth);
31372         
31373     },
31374     
31375     getContainerWidth : function()
31376     {
31377         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31378     },
31379     
31380     layoutItems : function( isInstant )
31381     {
31382         Roo.log(this.bricks);
31383         
31384         var items = Roo.apply([], this.bricks);
31385         
31386         if(this.isHorizontal){
31387             this._horizontalLayoutItems( items , isInstant );
31388             return;
31389         }
31390         
31391 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31392 //            this._verticalAlternativeLayoutItems( items , isInstant );
31393 //            return;
31394 //        }
31395         
31396         this._verticalLayoutItems( items , isInstant );
31397         
31398     },
31399     
31400     _verticalLayoutItems : function ( items , isInstant)
31401     {
31402         if ( !items || !items.length ) {
31403             return;
31404         }
31405         
31406         var standard = [
31407             ['xs', 'xs', 'xs', 'tall'],
31408             ['xs', 'xs', 'tall'],
31409             ['xs', 'xs', 'sm'],
31410             ['xs', 'xs', 'xs'],
31411             ['xs', 'tall'],
31412             ['xs', 'sm'],
31413             ['xs', 'xs'],
31414             ['xs'],
31415             
31416             ['sm', 'xs', 'xs'],
31417             ['sm', 'xs'],
31418             ['sm'],
31419             
31420             ['tall', 'xs', 'xs', 'xs'],
31421             ['tall', 'xs', 'xs'],
31422             ['tall', 'xs'],
31423             ['tall']
31424             
31425         ];
31426         
31427         var queue = [];
31428         
31429         var boxes = [];
31430         
31431         var box = [];
31432         
31433         Roo.each(items, function(item, k){
31434             
31435             switch (item.size) {
31436                 // these layouts take up a full box,
31437                 case 'md' :
31438                 case 'md-left' :
31439                 case 'md-right' :
31440                 case 'wide' :
31441                     
31442                     if(box.length){
31443                         boxes.push(box);
31444                         box = [];
31445                     }
31446                     
31447                     boxes.push([item]);
31448                     
31449                     break;
31450                     
31451                 case 'xs' :
31452                 case 'sm' :
31453                 case 'tall' :
31454                     
31455                     box.push(item);
31456                     
31457                     break;
31458                 default :
31459                     break;
31460                     
31461             }
31462             
31463         }, this);
31464         
31465         if(box.length){
31466             boxes.push(box);
31467             box = [];
31468         }
31469         
31470         var filterPattern = function(box, length)
31471         {
31472             if(!box.length){
31473                 return;
31474             }
31475             
31476             var match = false;
31477             
31478             var pattern = box.slice(0, length);
31479             
31480             var format = [];
31481             
31482             Roo.each(pattern, function(i){
31483                 format.push(i.size);
31484             }, this);
31485             
31486             Roo.each(standard, function(s){
31487                 
31488                 if(String(s) != String(format)){
31489                     return;
31490                 }
31491                 
31492                 match = true;
31493                 return false;
31494                 
31495             }, this);
31496             
31497             if(!match && length == 1){
31498                 return;
31499             }
31500             
31501             if(!match){
31502                 filterPattern(box, length - 1);
31503                 return;
31504             }
31505                 
31506             queue.push(pattern);
31507
31508             box = box.slice(length, box.length);
31509
31510             filterPattern(box, 4);
31511
31512             return;
31513             
31514         }
31515         
31516         Roo.each(boxes, function(box, k){
31517             
31518             if(!box.length){
31519                 return;
31520             }
31521             
31522             if(box.length == 1){
31523                 queue.push(box);
31524                 return;
31525             }
31526             
31527             filterPattern(box, 4);
31528             
31529         }, this);
31530         
31531         this._processVerticalLayoutQueue( queue, isInstant );
31532         
31533     },
31534     
31535 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31536 //    {
31537 //        if ( !items || !items.length ) {
31538 //            return;
31539 //        }
31540 //
31541 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31542 //        
31543 //    },
31544     
31545     _horizontalLayoutItems : function ( items , isInstant)
31546     {
31547         if ( !items || !items.length || items.length < 3) {
31548             return;
31549         }
31550         
31551         items.reverse();
31552         
31553         var eItems = items.slice(0, 3);
31554         
31555         items = items.slice(3, items.length);
31556         
31557         var standard = [
31558             ['xs', 'xs', 'xs', 'wide'],
31559             ['xs', 'xs', 'wide'],
31560             ['xs', 'xs', 'sm'],
31561             ['xs', 'xs', 'xs'],
31562             ['xs', 'wide'],
31563             ['xs', 'sm'],
31564             ['xs', 'xs'],
31565             ['xs'],
31566             
31567             ['sm', 'xs', 'xs'],
31568             ['sm', 'xs'],
31569             ['sm'],
31570             
31571             ['wide', 'xs', 'xs', 'xs'],
31572             ['wide', 'xs', 'xs'],
31573             ['wide', 'xs'],
31574             ['wide'],
31575             
31576             ['wide-thin']
31577         ];
31578         
31579         var queue = [];
31580         
31581         var boxes = [];
31582         
31583         var box = [];
31584         
31585         Roo.each(items, function(item, k){
31586             
31587             switch (item.size) {
31588                 case 'md' :
31589                 case 'md-left' :
31590                 case 'md-right' :
31591                 case 'tall' :
31592                     
31593                     if(box.length){
31594                         boxes.push(box);
31595                         box = [];
31596                     }
31597                     
31598                     boxes.push([item]);
31599                     
31600                     break;
31601                     
31602                 case 'xs' :
31603                 case 'sm' :
31604                 case 'wide' :
31605                 case 'wide-thin' :
31606                     
31607                     box.push(item);
31608                     
31609                     break;
31610                 default :
31611                     break;
31612                     
31613             }
31614             
31615         }, this);
31616         
31617         if(box.length){
31618             boxes.push(box);
31619             box = [];
31620         }
31621         
31622         var filterPattern = function(box, length)
31623         {
31624             if(!box.length){
31625                 return;
31626             }
31627             
31628             var match = false;
31629             
31630             var pattern = box.slice(0, length);
31631             
31632             var format = [];
31633             
31634             Roo.each(pattern, function(i){
31635                 format.push(i.size);
31636             }, this);
31637             
31638             Roo.each(standard, function(s){
31639                 
31640                 if(String(s) != String(format)){
31641                     return;
31642                 }
31643                 
31644                 match = true;
31645                 return false;
31646                 
31647             }, this);
31648             
31649             if(!match && length == 1){
31650                 return;
31651             }
31652             
31653             if(!match){
31654                 filterPattern(box, length - 1);
31655                 return;
31656             }
31657                 
31658             queue.push(pattern);
31659
31660             box = box.slice(length, box.length);
31661
31662             filterPattern(box, 4);
31663
31664             return;
31665             
31666         }
31667         
31668         Roo.each(boxes, function(box, k){
31669             
31670             if(!box.length){
31671                 return;
31672             }
31673             
31674             if(box.length == 1){
31675                 queue.push(box);
31676                 return;
31677             }
31678             
31679             filterPattern(box, 4);
31680             
31681         }, this);
31682         
31683         
31684         var prune = [];
31685         
31686         var pos = this.el.getBox(true);
31687         
31688         var minX = pos.x;
31689         
31690         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31691         
31692         var hit_end = false;
31693         
31694         Roo.each(queue, function(box){
31695             
31696             if(hit_end){
31697                 
31698                 Roo.each(box, function(b){
31699                 
31700                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31701                     b.el.hide();
31702
31703                 }, this);
31704
31705                 return;
31706             }
31707             
31708             var mx = 0;
31709             
31710             Roo.each(box, function(b){
31711                 
31712                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31713                 b.el.show();
31714
31715                 mx = Math.max(mx, b.x);
31716                 
31717             }, this);
31718             
31719             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31720             
31721             if(maxX < minX){
31722                 
31723                 Roo.each(box, function(b){
31724                 
31725                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31726                     b.el.hide();
31727                     
31728                 }, this);
31729                 
31730                 hit_end = true;
31731                 
31732                 return;
31733             }
31734             
31735             prune.push(box);
31736             
31737         }, this);
31738         
31739         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31740     },
31741     
31742     /** Sets position of item in DOM
31743     * @param {Element} item
31744     * @param {Number} x - horizontal position
31745     * @param {Number} y - vertical position
31746     * @param {Boolean} isInstant - disables transitions
31747     */
31748     _processVerticalLayoutQueue : function( queue, isInstant )
31749     {
31750         var pos = this.el.getBox(true);
31751         var x = pos.x;
31752         var y = pos.y;
31753         var maxY = [];
31754         
31755         for (var i = 0; i < this.cols; i++){
31756             maxY[i] = pos.y;
31757         }
31758         
31759         Roo.each(queue, function(box, k){
31760             
31761             var col = k % this.cols;
31762             
31763             Roo.each(box, function(b,kk){
31764                 
31765                 b.el.position('absolute');
31766                 
31767                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31768                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31769                 
31770                 if(b.size == 'md-left' || b.size == 'md-right'){
31771                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31772                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31773                 }
31774                 
31775                 b.el.setWidth(width);
31776                 b.el.setHeight(height);
31777                 // iframe?
31778                 b.el.select('iframe',true).setSize(width,height);
31779                 
31780             }, this);
31781             
31782             for (var i = 0; i < this.cols; i++){
31783                 
31784                 if(maxY[i] < maxY[col]){
31785                     col = i;
31786                     continue;
31787                 }
31788                 
31789                 col = Math.min(col, i);
31790                 
31791             }
31792             
31793             x = pos.x + col * (this.colWidth + this.padWidth);
31794             
31795             y = maxY[col];
31796             
31797             var positions = [];
31798             
31799             switch (box.length){
31800                 case 1 :
31801                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31802                     break;
31803                 case 2 :
31804                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31805                     break;
31806                 case 3 :
31807                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31808                     break;
31809                 case 4 :
31810                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31811                     break;
31812                 default :
31813                     break;
31814             }
31815             
31816             Roo.each(box, function(b,kk){
31817                 
31818                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31819                 
31820                 var sz = b.el.getSize();
31821                 
31822                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31823                 
31824             }, this);
31825             
31826         }, this);
31827         
31828         var mY = 0;
31829         
31830         for (var i = 0; i < this.cols; i++){
31831             mY = Math.max(mY, maxY[i]);
31832         }
31833         
31834         this.el.setHeight(mY - pos.y);
31835         
31836     },
31837     
31838 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31839 //    {
31840 //        var pos = this.el.getBox(true);
31841 //        var x = pos.x;
31842 //        var y = pos.y;
31843 //        var maxX = pos.right;
31844 //        
31845 //        var maxHeight = 0;
31846 //        
31847 //        Roo.each(items, function(item, k){
31848 //            
31849 //            var c = k % 2;
31850 //            
31851 //            item.el.position('absolute');
31852 //                
31853 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31854 //
31855 //            item.el.setWidth(width);
31856 //
31857 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31858 //
31859 //            item.el.setHeight(height);
31860 //            
31861 //            if(c == 0){
31862 //                item.el.setXY([x, y], isInstant ? false : true);
31863 //            } else {
31864 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31865 //            }
31866 //            
31867 //            y = y + height + this.alternativePadWidth;
31868 //            
31869 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31870 //            
31871 //        }, this);
31872 //        
31873 //        this.el.setHeight(maxHeight);
31874 //        
31875 //    },
31876     
31877     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31878     {
31879         var pos = this.el.getBox(true);
31880         
31881         var minX = pos.x;
31882         var minY = pos.y;
31883         
31884         var maxX = pos.right;
31885         
31886         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31887         
31888         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31889         
31890         Roo.each(queue, function(box, k){
31891             
31892             Roo.each(box, function(b, kk){
31893                 
31894                 b.el.position('absolute');
31895                 
31896                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31897                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31898                 
31899                 if(b.size == 'md-left' || b.size == 'md-right'){
31900                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31901                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31902                 }
31903                 
31904                 b.el.setWidth(width);
31905                 b.el.setHeight(height);
31906                 
31907             }, this);
31908             
31909             if(!box.length){
31910                 return;
31911             }
31912             
31913             var positions = [];
31914             
31915             switch (box.length){
31916                 case 1 :
31917                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31918                     break;
31919                 case 2 :
31920                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31921                     break;
31922                 case 3 :
31923                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31924                     break;
31925                 case 4 :
31926                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31927                     break;
31928                 default :
31929                     break;
31930             }
31931             
31932             Roo.each(box, function(b,kk){
31933                 
31934                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31935                 
31936                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31937                 
31938             }, this);
31939             
31940         }, this);
31941         
31942     },
31943     
31944     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31945     {
31946         Roo.each(eItems, function(b,k){
31947             
31948             b.size = (k == 0) ? 'sm' : 'xs';
31949             b.x = (k == 0) ? 2 : 1;
31950             b.y = (k == 0) ? 2 : 1;
31951             
31952             b.el.position('absolute');
31953             
31954             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31955                 
31956             b.el.setWidth(width);
31957             
31958             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31959             
31960             b.el.setHeight(height);
31961             
31962         }, this);
31963
31964         var positions = [];
31965         
31966         positions.push({
31967             x : maxX - this.unitWidth * 2 - this.gutter,
31968             y : minY
31969         });
31970         
31971         positions.push({
31972             x : maxX - this.unitWidth,
31973             y : minY + (this.unitWidth + this.gutter) * 2
31974         });
31975         
31976         positions.push({
31977             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31978             y : minY
31979         });
31980         
31981         Roo.each(eItems, function(b,k){
31982             
31983             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31984
31985         }, this);
31986         
31987     },
31988     
31989     getVerticalOneBoxColPositions : function(x, y, box)
31990     {
31991         var pos = [];
31992         
31993         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31994         
31995         if(box[0].size == 'md-left'){
31996             rand = 0;
31997         }
31998         
31999         if(box[0].size == 'md-right'){
32000             rand = 1;
32001         }
32002         
32003         pos.push({
32004             x : x + (this.unitWidth + this.gutter) * rand,
32005             y : y
32006         });
32007         
32008         return pos;
32009     },
32010     
32011     getVerticalTwoBoxColPositions : function(x, y, box)
32012     {
32013         var pos = [];
32014         
32015         if(box[0].size == 'xs'){
32016             
32017             pos.push({
32018                 x : x,
32019                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32020             });
32021
32022             pos.push({
32023                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32024                 y : y
32025             });
32026             
32027             return pos;
32028             
32029         }
32030         
32031         pos.push({
32032             x : x,
32033             y : y
32034         });
32035
32036         pos.push({
32037             x : x + (this.unitWidth + this.gutter) * 2,
32038             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32039         });
32040         
32041         return pos;
32042         
32043     },
32044     
32045     getVerticalThreeBoxColPositions : function(x, y, box)
32046     {
32047         var pos = [];
32048         
32049         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32050             
32051             pos.push({
32052                 x : x,
32053                 y : y
32054             });
32055
32056             pos.push({
32057                 x : x + (this.unitWidth + this.gutter) * 1,
32058                 y : y
32059             });
32060             
32061             pos.push({
32062                 x : x + (this.unitWidth + this.gutter) * 2,
32063                 y : y
32064             });
32065             
32066             return pos;
32067             
32068         }
32069         
32070         if(box[0].size == 'xs' && box[1].size == 'xs'){
32071             
32072             pos.push({
32073                 x : x,
32074                 y : y
32075             });
32076
32077             pos.push({
32078                 x : x,
32079                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32080             });
32081             
32082             pos.push({
32083                 x : x + (this.unitWidth + this.gutter) * 1,
32084                 y : y
32085             });
32086             
32087             return pos;
32088             
32089         }
32090         
32091         pos.push({
32092             x : x,
32093             y : y
32094         });
32095
32096         pos.push({
32097             x : x + (this.unitWidth + this.gutter) * 2,
32098             y : y
32099         });
32100
32101         pos.push({
32102             x : x + (this.unitWidth + this.gutter) * 2,
32103             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32104         });
32105             
32106         return pos;
32107         
32108     },
32109     
32110     getVerticalFourBoxColPositions : function(x, y, box)
32111     {
32112         var pos = [];
32113         
32114         if(box[0].size == 'xs'){
32115             
32116             pos.push({
32117                 x : x,
32118                 y : y
32119             });
32120
32121             pos.push({
32122                 x : x,
32123                 y : y + (this.unitHeight + this.gutter) * 1
32124             });
32125             
32126             pos.push({
32127                 x : x,
32128                 y : y + (this.unitHeight + this.gutter) * 2
32129             });
32130             
32131             pos.push({
32132                 x : x + (this.unitWidth + this.gutter) * 1,
32133                 y : y
32134             });
32135             
32136             return pos;
32137             
32138         }
32139         
32140         pos.push({
32141             x : x,
32142             y : y
32143         });
32144
32145         pos.push({
32146             x : x + (this.unitWidth + this.gutter) * 2,
32147             y : y
32148         });
32149
32150         pos.push({
32151             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32152             y : y + (this.unitHeight + this.gutter) * 1
32153         });
32154
32155         pos.push({
32156             x : x + (this.unitWidth + this.gutter) * 2,
32157             y : y + (this.unitWidth + this.gutter) * 2
32158         });
32159
32160         return pos;
32161         
32162     },
32163     
32164     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32165     {
32166         var pos = [];
32167         
32168         if(box[0].size == 'md-left'){
32169             pos.push({
32170                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32171                 y : minY
32172             });
32173             
32174             return pos;
32175         }
32176         
32177         if(box[0].size == 'md-right'){
32178             pos.push({
32179                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32180                 y : minY + (this.unitWidth + this.gutter) * 1
32181             });
32182             
32183             return pos;
32184         }
32185         
32186         var rand = Math.floor(Math.random() * (4 - box[0].y));
32187         
32188         pos.push({
32189             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32190             y : minY + (this.unitWidth + this.gutter) * rand
32191         });
32192         
32193         return pos;
32194         
32195     },
32196     
32197     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32198     {
32199         var pos = [];
32200         
32201         if(box[0].size == 'xs'){
32202             
32203             pos.push({
32204                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32205                 y : minY
32206             });
32207
32208             pos.push({
32209                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32210                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32211             });
32212             
32213             return pos;
32214             
32215         }
32216         
32217         pos.push({
32218             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32219             y : minY
32220         });
32221
32222         pos.push({
32223             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32224             y : minY + (this.unitWidth + this.gutter) * 2
32225         });
32226         
32227         return pos;
32228         
32229     },
32230     
32231     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32232     {
32233         var pos = [];
32234         
32235         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32236             
32237             pos.push({
32238                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32239                 y : minY
32240             });
32241
32242             pos.push({
32243                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32244                 y : minY + (this.unitWidth + this.gutter) * 1
32245             });
32246             
32247             pos.push({
32248                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32249                 y : minY + (this.unitWidth + this.gutter) * 2
32250             });
32251             
32252             return pos;
32253             
32254         }
32255         
32256         if(box[0].size == 'xs' && box[1].size == 'xs'){
32257             
32258             pos.push({
32259                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32260                 y : minY
32261             });
32262
32263             pos.push({
32264                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32265                 y : minY
32266             });
32267             
32268             pos.push({
32269                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32270                 y : minY + (this.unitWidth + this.gutter) * 1
32271             });
32272             
32273             return pos;
32274             
32275         }
32276         
32277         pos.push({
32278             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32279             y : minY
32280         });
32281
32282         pos.push({
32283             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32284             y : minY + (this.unitWidth + this.gutter) * 2
32285         });
32286
32287         pos.push({
32288             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32289             y : minY + (this.unitWidth + this.gutter) * 2
32290         });
32291             
32292         return pos;
32293         
32294     },
32295     
32296     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32297     {
32298         var pos = [];
32299         
32300         if(box[0].size == 'xs'){
32301             
32302             pos.push({
32303                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32304                 y : minY
32305             });
32306
32307             pos.push({
32308                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32309                 y : minY
32310             });
32311             
32312             pos.push({
32313                 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),
32314                 y : minY
32315             });
32316             
32317             pos.push({
32318                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32319                 y : minY + (this.unitWidth + this.gutter) * 1
32320             });
32321             
32322             return pos;
32323             
32324         }
32325         
32326         pos.push({
32327             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32328             y : minY
32329         });
32330         
32331         pos.push({
32332             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32333             y : minY + (this.unitWidth + this.gutter) * 2
32334         });
32335         
32336         pos.push({
32337             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32338             y : minY + (this.unitWidth + this.gutter) * 2
32339         });
32340         
32341         pos.push({
32342             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),
32343             y : minY + (this.unitWidth + this.gutter) * 2
32344         });
32345
32346         return pos;
32347         
32348     },
32349     
32350     /**
32351     * remove a Masonry Brick
32352     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32353     */
32354     removeBrick : function(brick_id)
32355     {
32356         if (!brick_id) {
32357             return;
32358         }
32359         
32360         for (var i = 0; i<this.bricks.length; i++) {
32361             if (this.bricks[i].id == brick_id) {
32362                 this.bricks.splice(i,1);
32363                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32364                 this.initial();
32365             }
32366         }
32367     },
32368     
32369     /**
32370     * adds a Masonry Brick
32371     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32372     */
32373     addBrick : function(cfg)
32374     {
32375         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32376         //this.register(cn);
32377         cn.parentId = this.id;
32378         cn.render(this.el);
32379         return cn;
32380     },
32381     
32382     /**
32383     * register a Masonry Brick
32384     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32385     */
32386     
32387     register : function(brick)
32388     {
32389         this.bricks.push(brick);
32390         brick.masonryId = this.id;
32391     },
32392     
32393     /**
32394     * clear all the Masonry Brick
32395     */
32396     clearAll : function()
32397     {
32398         this.bricks = [];
32399         //this.getChildContainer().dom.innerHTML = "";
32400         this.el.dom.innerHTML = '';
32401     },
32402     
32403     getSelected : function()
32404     {
32405         if (!this.selectedBrick) {
32406             return false;
32407         }
32408         
32409         return this.selectedBrick;
32410     }
32411 });
32412
32413 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32414     
32415     groups: {},
32416      /**
32417     * register a Masonry Layout
32418     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32419     */
32420     
32421     register : function(layout)
32422     {
32423         this.groups[layout.id] = layout;
32424     },
32425     /**
32426     * fetch a  Masonry Layout based on the masonry layout ID
32427     * @param {string} the masonry layout to add
32428     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32429     */
32430     
32431     get: function(layout_id) {
32432         if (typeof(this.groups[layout_id]) == 'undefined') {
32433             return false;
32434         }
32435         return this.groups[layout_id] ;
32436     }
32437     
32438     
32439     
32440 });
32441
32442  
32443
32444  /**
32445  *
32446  * This is based on 
32447  * http://masonry.desandro.com
32448  *
32449  * The idea is to render all the bricks based on vertical width...
32450  *
32451  * The original code extends 'outlayer' - we might need to use that....
32452  * 
32453  */
32454
32455
32456 /**
32457  * @class Roo.bootstrap.LayoutMasonryAuto
32458  * @extends Roo.bootstrap.Component
32459  * Bootstrap Layout Masonry class
32460  * 
32461  * @constructor
32462  * Create a new Element
32463  * @param {Object} config The config object
32464  */
32465
32466 Roo.bootstrap.LayoutMasonryAuto = function(config){
32467     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32468 };
32469
32470 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32471     
32472       /**
32473      * @cfg {Boolean} isFitWidth  - resize the width..
32474      */   
32475     isFitWidth : false,  // options..
32476     /**
32477      * @cfg {Boolean} isOriginLeft = left align?
32478      */   
32479     isOriginLeft : true,
32480     /**
32481      * @cfg {Boolean} isOriginTop = top align?
32482      */   
32483     isOriginTop : false,
32484     /**
32485      * @cfg {Boolean} isLayoutInstant = no animation?
32486      */   
32487     isLayoutInstant : false, // needed?
32488     /**
32489      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32490      */   
32491     isResizingContainer : true,
32492     /**
32493      * @cfg {Number} columnWidth  width of the columns 
32494      */   
32495     
32496     columnWidth : 0,
32497     
32498     /**
32499      * @cfg {Number} maxCols maximum number of columns
32500      */   
32501     
32502     maxCols: 0,
32503     /**
32504      * @cfg {Number} padHeight padding below box..
32505      */   
32506     
32507     padHeight : 10, 
32508     
32509     /**
32510      * @cfg {Boolean} isAutoInitial defalut true
32511      */   
32512     
32513     isAutoInitial : true, 
32514     
32515     // private?
32516     gutter : 0,
32517     
32518     containerWidth: 0,
32519     initialColumnWidth : 0,
32520     currentSize : null,
32521     
32522     colYs : null, // array.
32523     maxY : 0,
32524     padWidth: 10,
32525     
32526     
32527     tag: 'div',
32528     cls: '',
32529     bricks: null, //CompositeElement
32530     cols : 0, // array?
32531     // element : null, // wrapped now this.el
32532     _isLayoutInited : null, 
32533     
32534     
32535     getAutoCreate : function(){
32536         
32537         var cfg = {
32538             tag: this.tag,
32539             cls: 'blog-masonary-wrapper ' + this.cls,
32540             cn : {
32541                 cls : 'mas-boxes masonary'
32542             }
32543         };
32544         
32545         return cfg;
32546     },
32547     
32548     getChildContainer: function( )
32549     {
32550         if (this.boxesEl) {
32551             return this.boxesEl;
32552         }
32553         
32554         this.boxesEl = this.el.select('.mas-boxes').first();
32555         
32556         return this.boxesEl;
32557     },
32558     
32559     
32560     initEvents : function()
32561     {
32562         var _this = this;
32563         
32564         if(this.isAutoInitial){
32565             Roo.log('hook children rendered');
32566             this.on('childrenrendered', function() {
32567                 Roo.log('children rendered');
32568                 _this.initial();
32569             } ,this);
32570         }
32571         
32572     },
32573     
32574     initial : function()
32575     {
32576         this.reloadItems();
32577
32578         this.currentSize = this.el.getBox(true);
32579
32580         /// was window resize... - let's see if this works..
32581         Roo.EventManager.onWindowResize(this.resize, this); 
32582
32583         if(!this.isAutoInitial){
32584             this.layout();
32585             return;
32586         }
32587         
32588         this.layout.defer(500,this);
32589     },
32590     
32591     reloadItems: function()
32592     {
32593         this.bricks = this.el.select('.masonry-brick', true);
32594         
32595         this.bricks.each(function(b) {
32596             //Roo.log(b.getSize());
32597             if (!b.attr('originalwidth')) {
32598                 b.attr('originalwidth',  b.getSize().width);
32599             }
32600             
32601         });
32602         
32603         Roo.log(this.bricks.elements.length);
32604     },
32605     
32606     resize : function()
32607     {
32608         Roo.log('resize');
32609         var cs = this.el.getBox(true);
32610         
32611         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32612             Roo.log("no change in with or X");
32613             return;
32614         }
32615         this.currentSize = cs;
32616         this.layout();
32617     },
32618     
32619     layout : function()
32620     {
32621          Roo.log('layout');
32622         this._resetLayout();
32623         //this._manageStamps();
32624       
32625         // don't animate first layout
32626         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32627         this.layoutItems( isInstant );
32628       
32629         // flag for initalized
32630         this._isLayoutInited = true;
32631     },
32632     
32633     layoutItems : function( isInstant )
32634     {
32635         //var items = this._getItemsForLayout( this.items );
32636         // original code supports filtering layout items.. we just ignore it..
32637         
32638         this._layoutItems( this.bricks , isInstant );
32639       
32640         this._postLayout();
32641     },
32642     _layoutItems : function ( items , isInstant)
32643     {
32644        //this.fireEvent( 'layout', this, items );
32645     
32646
32647         if ( !items || !items.elements.length ) {
32648           // no items, emit event with empty array
32649             return;
32650         }
32651
32652         var queue = [];
32653         items.each(function(item) {
32654             Roo.log("layout item");
32655             Roo.log(item);
32656             // get x/y object from method
32657             var position = this._getItemLayoutPosition( item );
32658             // enqueue
32659             position.item = item;
32660             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32661             queue.push( position );
32662         }, this);
32663       
32664         this._processLayoutQueue( queue );
32665     },
32666     /** Sets position of item in DOM
32667     * @param {Element} item
32668     * @param {Number} x - horizontal position
32669     * @param {Number} y - vertical position
32670     * @param {Boolean} isInstant - disables transitions
32671     */
32672     _processLayoutQueue : function( queue )
32673     {
32674         for ( var i=0, len = queue.length; i < len; i++ ) {
32675             var obj = queue[i];
32676             obj.item.position('absolute');
32677             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32678         }
32679     },
32680       
32681     
32682     /**
32683     * Any logic you want to do after each layout,
32684     * i.e. size the container
32685     */
32686     _postLayout : function()
32687     {
32688         this.resizeContainer();
32689     },
32690     
32691     resizeContainer : function()
32692     {
32693         if ( !this.isResizingContainer ) {
32694             return;
32695         }
32696         var size = this._getContainerSize();
32697         if ( size ) {
32698             this.el.setSize(size.width,size.height);
32699             this.boxesEl.setSize(size.width,size.height);
32700         }
32701     },
32702     
32703     
32704     
32705     _resetLayout : function()
32706     {
32707         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32708         this.colWidth = this.el.getWidth();
32709         //this.gutter = this.el.getWidth(); 
32710         
32711         this.measureColumns();
32712
32713         // reset column Y
32714         var i = this.cols;
32715         this.colYs = [];
32716         while (i--) {
32717             this.colYs.push( 0 );
32718         }
32719     
32720         this.maxY = 0;
32721     },
32722
32723     measureColumns : function()
32724     {
32725         this.getContainerWidth();
32726       // if columnWidth is 0, default to outerWidth of first item
32727         if ( !this.columnWidth ) {
32728             var firstItem = this.bricks.first();
32729             Roo.log(firstItem);
32730             this.columnWidth  = this.containerWidth;
32731             if (firstItem && firstItem.attr('originalwidth') ) {
32732                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32733             }
32734             // columnWidth fall back to item of first element
32735             Roo.log("set column width?");
32736                         this.initialColumnWidth = this.columnWidth  ;
32737
32738             // if first elem has no width, default to size of container
32739             
32740         }
32741         
32742         
32743         if (this.initialColumnWidth) {
32744             this.columnWidth = this.initialColumnWidth;
32745         }
32746         
32747         
32748             
32749         // column width is fixed at the top - however if container width get's smaller we should
32750         // reduce it...
32751         
32752         // this bit calcs how man columns..
32753             
32754         var columnWidth = this.columnWidth += this.gutter;
32755       
32756         // calculate columns
32757         var containerWidth = this.containerWidth + this.gutter;
32758         
32759         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32760         // fix rounding errors, typically with gutters
32761         var excess = columnWidth - containerWidth % columnWidth;
32762         
32763         
32764         // if overshoot is less than a pixel, round up, otherwise floor it
32765         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32766         cols = Math[ mathMethod ]( cols );
32767         this.cols = Math.max( cols, 1 );
32768         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32769         
32770          // padding positioning..
32771         var totalColWidth = this.cols * this.columnWidth;
32772         var padavail = this.containerWidth - totalColWidth;
32773         // so for 2 columns - we need 3 'pads'
32774         
32775         var padNeeded = (1+this.cols) * this.padWidth;
32776         
32777         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32778         
32779         this.columnWidth += padExtra
32780         //this.padWidth = Math.floor(padavail /  ( this.cols));
32781         
32782         // adjust colum width so that padding is fixed??
32783         
32784         // we have 3 columns ... total = width * 3
32785         // we have X left over... that should be used by 
32786         
32787         //if (this.expandC) {
32788             
32789         //}
32790         
32791         
32792         
32793     },
32794     
32795     getContainerWidth : function()
32796     {
32797        /* // container is parent if fit width
32798         var container = this.isFitWidth ? this.element.parentNode : this.element;
32799         // check that this.size and size are there
32800         // IE8 triggers resize on body size change, so they might not be
32801         
32802         var size = getSize( container );  //FIXME
32803         this.containerWidth = size && size.innerWidth; //FIXME
32804         */
32805          
32806         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32807         
32808     },
32809     
32810     _getItemLayoutPosition : function( item )  // what is item?
32811     {
32812         // we resize the item to our columnWidth..
32813       
32814         item.setWidth(this.columnWidth);
32815         item.autoBoxAdjust  = false;
32816         
32817         var sz = item.getSize();
32818  
32819         // how many columns does this brick span
32820         var remainder = this.containerWidth % this.columnWidth;
32821         
32822         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32823         // round if off by 1 pixel, otherwise use ceil
32824         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32825         colSpan = Math.min( colSpan, this.cols );
32826         
32827         // normally this should be '1' as we dont' currently allow multi width columns..
32828         
32829         var colGroup = this._getColGroup( colSpan );
32830         // get the minimum Y value from the columns
32831         var minimumY = Math.min.apply( Math, colGroup );
32832         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32833         
32834         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32835          
32836         // position the brick
32837         var position = {
32838             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32839             y: this.currentSize.y + minimumY + this.padHeight
32840         };
32841         
32842         Roo.log(position);
32843         // apply setHeight to necessary columns
32844         var setHeight = minimumY + sz.height + this.padHeight;
32845         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32846         
32847         var setSpan = this.cols + 1 - colGroup.length;
32848         for ( var i = 0; i < setSpan; i++ ) {
32849           this.colYs[ shortColIndex + i ] = setHeight ;
32850         }
32851       
32852         return position;
32853     },
32854     
32855     /**
32856      * @param {Number} colSpan - number of columns the element spans
32857      * @returns {Array} colGroup
32858      */
32859     _getColGroup : function( colSpan )
32860     {
32861         if ( colSpan < 2 ) {
32862           // if brick spans only one column, use all the column Ys
32863           return this.colYs;
32864         }
32865       
32866         var colGroup = [];
32867         // how many different places could this brick fit horizontally
32868         var groupCount = this.cols + 1 - colSpan;
32869         // for each group potential horizontal position
32870         for ( var i = 0; i < groupCount; i++ ) {
32871           // make an array of colY values for that one group
32872           var groupColYs = this.colYs.slice( i, i + colSpan );
32873           // and get the max value of the array
32874           colGroup[i] = Math.max.apply( Math, groupColYs );
32875         }
32876         return colGroup;
32877     },
32878     /*
32879     _manageStamp : function( stamp )
32880     {
32881         var stampSize =  stamp.getSize();
32882         var offset = stamp.getBox();
32883         // get the columns that this stamp affects
32884         var firstX = this.isOriginLeft ? offset.x : offset.right;
32885         var lastX = firstX + stampSize.width;
32886         var firstCol = Math.floor( firstX / this.columnWidth );
32887         firstCol = Math.max( 0, firstCol );
32888         
32889         var lastCol = Math.floor( lastX / this.columnWidth );
32890         // lastCol should not go over if multiple of columnWidth #425
32891         lastCol -= lastX % this.columnWidth ? 0 : 1;
32892         lastCol = Math.min( this.cols - 1, lastCol );
32893         
32894         // set colYs to bottom of the stamp
32895         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32896             stampSize.height;
32897             
32898         for ( var i = firstCol; i <= lastCol; i++ ) {
32899           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32900         }
32901     },
32902     */
32903     
32904     _getContainerSize : function()
32905     {
32906         this.maxY = Math.max.apply( Math, this.colYs );
32907         var size = {
32908             height: this.maxY
32909         };
32910       
32911         if ( this.isFitWidth ) {
32912             size.width = this._getContainerFitWidth();
32913         }
32914       
32915         return size;
32916     },
32917     
32918     _getContainerFitWidth : function()
32919     {
32920         var unusedCols = 0;
32921         // count unused columns
32922         var i = this.cols;
32923         while ( --i ) {
32924           if ( this.colYs[i] !== 0 ) {
32925             break;
32926           }
32927           unusedCols++;
32928         }
32929         // fit container to columns that have been used
32930         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32931     },
32932     
32933     needsResizeLayout : function()
32934     {
32935         var previousWidth = this.containerWidth;
32936         this.getContainerWidth();
32937         return previousWidth !== this.containerWidth;
32938     }
32939  
32940 });
32941
32942  
32943
32944  /*
32945  * - LGPL
32946  *
32947  * element
32948  * 
32949  */
32950
32951 /**
32952  * @class Roo.bootstrap.MasonryBrick
32953  * @extends Roo.bootstrap.Component
32954  * Bootstrap MasonryBrick class
32955  * 
32956  * @constructor
32957  * Create a new MasonryBrick
32958  * @param {Object} config The config object
32959  */
32960
32961 Roo.bootstrap.MasonryBrick = function(config){
32962     
32963     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32964     
32965     Roo.bootstrap.MasonryBrick.register(this);
32966     
32967     this.addEvents({
32968         // raw events
32969         /**
32970          * @event click
32971          * When a MasonryBrick is clcik
32972          * @param {Roo.bootstrap.MasonryBrick} this
32973          * @param {Roo.EventObject} e
32974          */
32975         "click" : true
32976     });
32977 };
32978
32979 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32980     
32981     /**
32982      * @cfg {String} title
32983      */   
32984     title : '',
32985     /**
32986      * @cfg {String} html
32987      */   
32988     html : '',
32989     /**
32990      * @cfg {String} bgimage
32991      */   
32992     bgimage : '',
32993     /**
32994      * @cfg {String} videourl
32995      */   
32996     videourl : '',
32997     /**
32998      * @cfg {String} cls
32999      */   
33000     cls : '',
33001     /**
33002      * @cfg {String} href
33003      */   
33004     href : '',
33005     /**
33006      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33007      */   
33008     size : 'xs',
33009     
33010     /**
33011      * @cfg {String} placetitle (center|bottom)
33012      */   
33013     placetitle : '',
33014     
33015     /**
33016      * @cfg {Boolean} isFitContainer defalut true
33017      */   
33018     isFitContainer : true, 
33019     
33020     /**
33021      * @cfg {Boolean} preventDefault defalut false
33022      */   
33023     preventDefault : false, 
33024     
33025     /**
33026      * @cfg {Boolean} inverse defalut false
33027      */   
33028     maskInverse : false, 
33029     
33030     getAutoCreate : function()
33031     {
33032         if(!this.isFitContainer){
33033             return this.getSplitAutoCreate();
33034         }
33035         
33036         var cls = 'masonry-brick masonry-brick-full';
33037         
33038         if(this.href.length){
33039             cls += ' masonry-brick-link';
33040         }
33041         
33042         if(this.bgimage.length){
33043             cls += ' masonry-brick-image';
33044         }
33045         
33046         if(this.maskInverse){
33047             cls += ' mask-inverse';
33048         }
33049         
33050         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33051             cls += ' enable-mask';
33052         }
33053         
33054         if(this.size){
33055             cls += ' masonry-' + this.size + '-brick';
33056         }
33057         
33058         if(this.placetitle.length){
33059             
33060             switch (this.placetitle) {
33061                 case 'center' :
33062                     cls += ' masonry-center-title';
33063                     break;
33064                 case 'bottom' :
33065                     cls += ' masonry-bottom-title';
33066                     break;
33067                 default:
33068                     break;
33069             }
33070             
33071         } else {
33072             if(!this.html.length && !this.bgimage.length){
33073                 cls += ' masonry-center-title';
33074             }
33075
33076             if(!this.html.length && this.bgimage.length){
33077                 cls += ' masonry-bottom-title';
33078             }
33079         }
33080         
33081         if(this.cls){
33082             cls += ' ' + this.cls;
33083         }
33084         
33085         var cfg = {
33086             tag: (this.href.length) ? 'a' : 'div',
33087             cls: cls,
33088             cn: [
33089                 {
33090                     tag: 'div',
33091                     cls: 'masonry-brick-mask'
33092                 },
33093                 {
33094                     tag: 'div',
33095                     cls: 'masonry-brick-paragraph',
33096                     cn: []
33097                 }
33098             ]
33099         };
33100         
33101         if(this.href.length){
33102             cfg.href = this.href;
33103         }
33104         
33105         var cn = cfg.cn[1].cn;
33106         
33107         if(this.title.length){
33108             cn.push({
33109                 tag: 'h4',
33110                 cls: 'masonry-brick-title',
33111                 html: this.title
33112             });
33113         }
33114         
33115         if(this.html.length){
33116             cn.push({
33117                 tag: 'p',
33118                 cls: 'masonry-brick-text',
33119                 html: this.html
33120             });
33121         }
33122         
33123         if (!this.title.length && !this.html.length) {
33124             cfg.cn[1].cls += ' hide';
33125         }
33126         
33127         if(this.bgimage.length){
33128             cfg.cn.push({
33129                 tag: 'img',
33130                 cls: 'masonry-brick-image-view',
33131                 src: this.bgimage
33132             });
33133         }
33134         
33135         if(this.videourl.length){
33136             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33137             // youtube support only?
33138             cfg.cn.push({
33139                 tag: 'iframe',
33140                 cls: 'masonry-brick-image-view',
33141                 src: vurl,
33142                 frameborder : 0,
33143                 allowfullscreen : true
33144             });
33145         }
33146         
33147         return cfg;
33148         
33149     },
33150     
33151     getSplitAutoCreate : function()
33152     {
33153         var cls = 'masonry-brick masonry-brick-split';
33154         
33155         if(this.href.length){
33156             cls += ' masonry-brick-link';
33157         }
33158         
33159         if(this.bgimage.length){
33160             cls += ' masonry-brick-image';
33161         }
33162         
33163         if(this.size){
33164             cls += ' masonry-' + this.size + '-brick';
33165         }
33166         
33167         switch (this.placetitle) {
33168             case 'center' :
33169                 cls += ' masonry-center-title';
33170                 break;
33171             case 'bottom' :
33172                 cls += ' masonry-bottom-title';
33173                 break;
33174             default:
33175                 if(!this.bgimage.length){
33176                     cls += ' masonry-center-title';
33177                 }
33178
33179                 if(this.bgimage.length){
33180                     cls += ' masonry-bottom-title';
33181                 }
33182                 break;
33183         }
33184         
33185         if(this.cls){
33186             cls += ' ' + this.cls;
33187         }
33188         
33189         var cfg = {
33190             tag: (this.href.length) ? 'a' : 'div',
33191             cls: cls,
33192             cn: [
33193                 {
33194                     tag: 'div',
33195                     cls: 'masonry-brick-split-head',
33196                     cn: [
33197                         {
33198                             tag: 'div',
33199                             cls: 'masonry-brick-paragraph',
33200                             cn: []
33201                         }
33202                     ]
33203                 },
33204                 {
33205                     tag: 'div',
33206                     cls: 'masonry-brick-split-body',
33207                     cn: []
33208                 }
33209             ]
33210         };
33211         
33212         if(this.href.length){
33213             cfg.href = this.href;
33214         }
33215         
33216         if(this.title.length){
33217             cfg.cn[0].cn[0].cn.push({
33218                 tag: 'h4',
33219                 cls: 'masonry-brick-title',
33220                 html: this.title
33221             });
33222         }
33223         
33224         if(this.html.length){
33225             cfg.cn[1].cn.push({
33226                 tag: 'p',
33227                 cls: 'masonry-brick-text',
33228                 html: this.html
33229             });
33230         }
33231
33232         if(this.bgimage.length){
33233             cfg.cn[0].cn.push({
33234                 tag: 'img',
33235                 cls: 'masonry-brick-image-view',
33236                 src: this.bgimage
33237             });
33238         }
33239         
33240         if(this.videourl.length){
33241             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33242             // youtube support only?
33243             cfg.cn[0].cn.cn.push({
33244                 tag: 'iframe',
33245                 cls: 'masonry-brick-image-view',
33246                 src: vurl,
33247                 frameborder : 0,
33248                 allowfullscreen : true
33249             });
33250         }
33251         
33252         return cfg;
33253     },
33254     
33255     initEvents: function() 
33256     {
33257         switch (this.size) {
33258             case 'xs' :
33259                 this.x = 1;
33260                 this.y = 1;
33261                 break;
33262             case 'sm' :
33263                 this.x = 2;
33264                 this.y = 2;
33265                 break;
33266             case 'md' :
33267             case 'md-left' :
33268             case 'md-right' :
33269                 this.x = 3;
33270                 this.y = 3;
33271                 break;
33272             case 'tall' :
33273                 this.x = 2;
33274                 this.y = 3;
33275                 break;
33276             case 'wide' :
33277                 this.x = 3;
33278                 this.y = 2;
33279                 break;
33280             case 'wide-thin' :
33281                 this.x = 3;
33282                 this.y = 1;
33283                 break;
33284                         
33285             default :
33286                 break;
33287         }
33288         
33289         if(Roo.isTouch){
33290             this.el.on('touchstart', this.onTouchStart, this);
33291             this.el.on('touchmove', this.onTouchMove, this);
33292             this.el.on('touchend', this.onTouchEnd, this);
33293             this.el.on('contextmenu', this.onContextMenu, this);
33294         } else {
33295             this.el.on('mouseenter'  ,this.enter, this);
33296             this.el.on('mouseleave', this.leave, this);
33297             this.el.on('click', this.onClick, this);
33298         }
33299         
33300         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33301             this.parent().bricks.push(this);   
33302         }
33303         
33304     },
33305     
33306     onClick: function(e, el)
33307     {
33308         var time = this.endTimer - this.startTimer;
33309         // Roo.log(e.preventDefault());
33310         if(Roo.isTouch){
33311             if(time > 1000){
33312                 e.preventDefault();
33313                 return;
33314             }
33315         }
33316         
33317         if(!this.preventDefault){
33318             return;
33319         }
33320         
33321         e.preventDefault();
33322         
33323         if (this.activeClass != '') {
33324             this.selectBrick();
33325         }
33326         
33327         this.fireEvent('click', this, e);
33328     },
33329     
33330     enter: function(e, el)
33331     {
33332         e.preventDefault();
33333         
33334         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33335             return;
33336         }
33337         
33338         if(this.bgimage.length && this.html.length){
33339             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33340         }
33341     },
33342     
33343     leave: function(e, el)
33344     {
33345         e.preventDefault();
33346         
33347         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33348             return;
33349         }
33350         
33351         if(this.bgimage.length && this.html.length){
33352             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33353         }
33354     },
33355     
33356     onTouchStart: function(e, el)
33357     {
33358 //        e.preventDefault();
33359         
33360         this.touchmoved = false;
33361         
33362         if(!this.isFitContainer){
33363             return;
33364         }
33365         
33366         if(!this.bgimage.length || !this.html.length){
33367             return;
33368         }
33369         
33370         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33371         
33372         this.timer = new Date().getTime();
33373         
33374     },
33375     
33376     onTouchMove: function(e, el)
33377     {
33378         this.touchmoved = true;
33379     },
33380     
33381     onContextMenu : function(e,el)
33382     {
33383         e.preventDefault();
33384         e.stopPropagation();
33385         return false;
33386     },
33387     
33388     onTouchEnd: function(e, el)
33389     {
33390 //        e.preventDefault();
33391         
33392         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33393         
33394             this.leave(e,el);
33395             
33396             return;
33397         }
33398         
33399         if(!this.bgimage.length || !this.html.length){
33400             
33401             if(this.href.length){
33402                 window.location.href = this.href;
33403             }
33404             
33405             return;
33406         }
33407         
33408         if(!this.isFitContainer){
33409             return;
33410         }
33411         
33412         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33413         
33414         window.location.href = this.href;
33415     },
33416     
33417     //selection on single brick only
33418     selectBrick : function() {
33419         
33420         if (!this.parentId) {
33421             return;
33422         }
33423         
33424         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33425         var index = m.selectedBrick.indexOf(this.id);
33426         
33427         if ( index > -1) {
33428             m.selectedBrick.splice(index,1);
33429             this.el.removeClass(this.activeClass);
33430             return;
33431         }
33432         
33433         for(var i = 0; i < m.selectedBrick.length; i++) {
33434             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33435             b.el.removeClass(b.activeClass);
33436         }
33437         
33438         m.selectedBrick = [];
33439         
33440         m.selectedBrick.push(this.id);
33441         this.el.addClass(this.activeClass);
33442         return;
33443     },
33444     
33445     isSelected : function(){
33446         return this.el.hasClass(this.activeClass);
33447         
33448     }
33449 });
33450
33451 Roo.apply(Roo.bootstrap.MasonryBrick, {
33452     
33453     //groups: {},
33454     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33455      /**
33456     * register a Masonry Brick
33457     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33458     */
33459     
33460     register : function(brick)
33461     {
33462         //this.groups[brick.id] = brick;
33463         this.groups.add(brick.id, brick);
33464     },
33465     /**
33466     * fetch a  masonry brick based on the masonry brick ID
33467     * @param {string} the masonry brick to add
33468     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33469     */
33470     
33471     get: function(brick_id) 
33472     {
33473         // if (typeof(this.groups[brick_id]) == 'undefined') {
33474         //     return false;
33475         // }
33476         // return this.groups[brick_id] ;
33477         
33478         if(this.groups.key(brick_id)) {
33479             return this.groups.key(brick_id);
33480         }
33481         
33482         return false;
33483     }
33484     
33485     
33486     
33487 });
33488
33489  /*
33490  * - LGPL
33491  *
33492  * element
33493  * 
33494  */
33495
33496 /**
33497  * @class Roo.bootstrap.Brick
33498  * @extends Roo.bootstrap.Component
33499  * Bootstrap Brick class
33500  * 
33501  * @constructor
33502  * Create a new Brick
33503  * @param {Object} config The config object
33504  */
33505
33506 Roo.bootstrap.Brick = function(config){
33507     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33508     
33509     this.addEvents({
33510         // raw events
33511         /**
33512          * @event click
33513          * When a Brick is click
33514          * @param {Roo.bootstrap.Brick} this
33515          * @param {Roo.EventObject} e
33516          */
33517         "click" : true
33518     });
33519 };
33520
33521 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33522     
33523     /**
33524      * @cfg {String} title
33525      */   
33526     title : '',
33527     /**
33528      * @cfg {String} html
33529      */   
33530     html : '',
33531     /**
33532      * @cfg {String} bgimage
33533      */   
33534     bgimage : '',
33535     /**
33536      * @cfg {String} cls
33537      */   
33538     cls : '',
33539     /**
33540      * @cfg {String} href
33541      */   
33542     href : '',
33543     /**
33544      * @cfg {String} video
33545      */   
33546     video : '',
33547     /**
33548      * @cfg {Boolean} square
33549      */   
33550     square : true,
33551     
33552     getAutoCreate : function()
33553     {
33554         var cls = 'roo-brick';
33555         
33556         if(this.href.length){
33557             cls += ' roo-brick-link';
33558         }
33559         
33560         if(this.bgimage.length){
33561             cls += ' roo-brick-image';
33562         }
33563         
33564         if(!this.html.length && !this.bgimage.length){
33565             cls += ' roo-brick-center-title';
33566         }
33567         
33568         if(!this.html.length && this.bgimage.length){
33569             cls += ' roo-brick-bottom-title';
33570         }
33571         
33572         if(this.cls){
33573             cls += ' ' + this.cls;
33574         }
33575         
33576         var cfg = {
33577             tag: (this.href.length) ? 'a' : 'div',
33578             cls: cls,
33579             cn: [
33580                 {
33581                     tag: 'div',
33582                     cls: 'roo-brick-paragraph',
33583                     cn: []
33584                 }
33585             ]
33586         };
33587         
33588         if(this.href.length){
33589             cfg.href = this.href;
33590         }
33591         
33592         var cn = cfg.cn[0].cn;
33593         
33594         if(this.title.length){
33595             cn.push({
33596                 tag: 'h4',
33597                 cls: 'roo-brick-title',
33598                 html: this.title
33599             });
33600         }
33601         
33602         if(this.html.length){
33603             cn.push({
33604                 tag: 'p',
33605                 cls: 'roo-brick-text',
33606                 html: this.html
33607             });
33608         } else {
33609             cn.cls += ' hide';
33610         }
33611         
33612         if(this.bgimage.length){
33613             cfg.cn.push({
33614                 tag: 'img',
33615                 cls: 'roo-brick-image-view',
33616                 src: this.bgimage
33617             });
33618         }
33619         
33620         return cfg;
33621     },
33622     
33623     initEvents: function() 
33624     {
33625         if(this.title.length || this.html.length){
33626             this.el.on('mouseenter'  ,this.enter, this);
33627             this.el.on('mouseleave', this.leave, this);
33628         }
33629         
33630         Roo.EventManager.onWindowResize(this.resize, this); 
33631         
33632         if(this.bgimage.length){
33633             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33634             this.imageEl.on('load', this.onImageLoad, this);
33635             return;
33636         }
33637         
33638         this.resize();
33639     },
33640     
33641     onImageLoad : function()
33642     {
33643         this.resize();
33644     },
33645     
33646     resize : function()
33647     {
33648         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33649         
33650         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33651         
33652         if(this.bgimage.length){
33653             var image = this.el.select('.roo-brick-image-view', true).first();
33654             
33655             image.setWidth(paragraph.getWidth());
33656             
33657             if(this.square){
33658                 image.setHeight(paragraph.getWidth());
33659             }
33660             
33661             this.el.setHeight(image.getHeight());
33662             paragraph.setHeight(image.getHeight());
33663             
33664         }
33665         
33666     },
33667     
33668     enter: function(e, el)
33669     {
33670         e.preventDefault();
33671         
33672         if(this.bgimage.length){
33673             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33674             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33675         }
33676     },
33677     
33678     leave: function(e, el)
33679     {
33680         e.preventDefault();
33681         
33682         if(this.bgimage.length){
33683             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33684             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33685         }
33686     }
33687     
33688 });
33689
33690  
33691
33692  /*
33693  * - LGPL
33694  *
33695  * Number field 
33696  */
33697
33698 /**
33699  * @class Roo.bootstrap.NumberField
33700  * @extends Roo.bootstrap.Input
33701  * Bootstrap NumberField class
33702  * 
33703  * 
33704  * 
33705  * 
33706  * @constructor
33707  * Create a new NumberField
33708  * @param {Object} config The config object
33709  */
33710
33711 Roo.bootstrap.NumberField = function(config){
33712     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33713 };
33714
33715 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33716     
33717     /**
33718      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33719      */
33720     allowDecimals : true,
33721     /**
33722      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33723      */
33724     decimalSeparator : ".",
33725     /**
33726      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33727      */
33728     decimalPrecision : 2,
33729     /**
33730      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33731      */
33732     allowNegative : true,
33733     
33734     /**
33735      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33736      */
33737     allowZero: true,
33738     /**
33739      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33740      */
33741     minValue : Number.NEGATIVE_INFINITY,
33742     /**
33743      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33744      */
33745     maxValue : Number.MAX_VALUE,
33746     /**
33747      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33748      */
33749     minText : "The minimum value for this field is {0}",
33750     /**
33751      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33752      */
33753     maxText : "The maximum value for this field is {0}",
33754     /**
33755      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33756      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33757      */
33758     nanText : "{0} is not a valid number",
33759     /**
33760      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33761      */
33762     thousandsDelimiter : false,
33763     /**
33764      * @cfg {String} valueAlign alignment of value
33765      */
33766     valueAlign : "left",
33767
33768     getAutoCreate : function()
33769     {
33770         var hiddenInput = {
33771             tag: 'input',
33772             type: 'hidden',
33773             id: Roo.id(),
33774             cls: 'hidden-number-input'
33775         };
33776         
33777         if (this.name) {
33778             hiddenInput.name = this.name;
33779         }
33780         
33781         this.name = '';
33782         
33783         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33784         
33785         this.name = hiddenInput.name;
33786         
33787         if(cfg.cn.length > 0) {
33788             cfg.cn.push(hiddenInput);
33789         }
33790         
33791         return cfg;
33792     },
33793
33794     // private
33795     initEvents : function()
33796     {   
33797         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33798         
33799         var allowed = "0123456789";
33800         
33801         if(this.allowDecimals){
33802             allowed += this.decimalSeparator;
33803         }
33804         
33805         if(this.allowNegative){
33806             allowed += "-";
33807         }
33808         
33809         if(this.thousandsDelimiter) {
33810             allowed += ",";
33811         }
33812         
33813         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33814         
33815         var keyPress = function(e){
33816             
33817             var k = e.getKey();
33818             
33819             var c = e.getCharCode();
33820             
33821             if(
33822                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33823                     allowed.indexOf(String.fromCharCode(c)) === -1
33824             ){
33825                 e.stopEvent();
33826                 return;
33827             }
33828             
33829             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33830                 return;
33831             }
33832             
33833             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33834                 e.stopEvent();
33835             }
33836         };
33837         
33838         this.el.on("keypress", keyPress, this);
33839     },
33840     
33841     validateValue : function(value)
33842     {
33843         
33844         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33845             return false;
33846         }
33847         
33848         var num = this.parseValue(value);
33849         
33850         if(isNaN(num)){
33851             this.markInvalid(String.format(this.nanText, value));
33852             return false;
33853         }
33854         
33855         if(num < this.minValue){
33856             this.markInvalid(String.format(this.minText, this.minValue));
33857             return false;
33858         }
33859         
33860         if(num > this.maxValue){
33861             this.markInvalid(String.format(this.maxText, this.maxValue));
33862             return false;
33863         }
33864         
33865         return true;
33866     },
33867
33868     getValue : function()
33869     {
33870         var v = this.hiddenEl().getValue();
33871         
33872         return this.fixPrecision(this.parseValue(v));
33873     },
33874
33875     parseValue : function(value)
33876     {
33877         if(this.thousandsDelimiter) {
33878             value += "";
33879             r = new RegExp(",", "g");
33880             value = value.replace(r, "");
33881         }
33882         
33883         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33884         return isNaN(value) ? '' : value;
33885     },
33886
33887     fixPrecision : function(value)
33888     {
33889         if(this.thousandsDelimiter) {
33890             value += "";
33891             r = new RegExp(",", "g");
33892             value = value.replace(r, "");
33893         }
33894         
33895         var nan = isNaN(value);
33896         
33897         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33898             return nan ? '' : value;
33899         }
33900         return parseFloat(value).toFixed(this.decimalPrecision);
33901     },
33902
33903     setValue : function(v)
33904     {
33905         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33906         
33907         this.value = v;
33908         
33909         if(this.rendered){
33910             
33911             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33912             
33913             this.inputEl().dom.value = (v == '') ? '' :
33914                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33915             
33916             if(!this.allowZero && v === '0') {
33917                 this.hiddenEl().dom.value = '';
33918                 this.inputEl().dom.value = '';
33919             }
33920             
33921             this.validate();
33922         }
33923     },
33924
33925     decimalPrecisionFcn : function(v)
33926     {
33927         return Math.floor(v);
33928     },
33929
33930     beforeBlur : function()
33931     {
33932         var v = this.parseValue(this.getRawValue());
33933         
33934         if(v || v === 0 || v === ''){
33935             this.setValue(v);
33936         }
33937     },
33938     
33939     hiddenEl : function()
33940     {
33941         return this.el.select('input.hidden-number-input',true).first();
33942     }
33943     
33944 });
33945
33946  
33947
33948 /*
33949 * Licence: LGPL
33950 */
33951
33952 /**
33953  * @class Roo.bootstrap.DocumentSlider
33954  * @extends Roo.bootstrap.Component
33955  * Bootstrap DocumentSlider class
33956  * 
33957  * @constructor
33958  * Create a new DocumentViewer
33959  * @param {Object} config The config object
33960  */
33961
33962 Roo.bootstrap.DocumentSlider = function(config){
33963     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33964     
33965     this.files = [];
33966     
33967     this.addEvents({
33968         /**
33969          * @event initial
33970          * Fire after initEvent
33971          * @param {Roo.bootstrap.DocumentSlider} this
33972          */
33973         "initial" : true,
33974         /**
33975          * @event update
33976          * Fire after update
33977          * @param {Roo.bootstrap.DocumentSlider} this
33978          */
33979         "update" : true,
33980         /**
33981          * @event click
33982          * Fire after click
33983          * @param {Roo.bootstrap.DocumentSlider} this
33984          */
33985         "click" : true
33986     });
33987 };
33988
33989 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33990     
33991     files : false,
33992     
33993     indicator : 0,
33994     
33995     getAutoCreate : function()
33996     {
33997         var cfg = {
33998             tag : 'div',
33999             cls : 'roo-document-slider',
34000             cn : [
34001                 {
34002                     tag : 'div',
34003                     cls : 'roo-document-slider-header',
34004                     cn : [
34005                         {
34006                             tag : 'div',
34007                             cls : 'roo-document-slider-header-title'
34008                         }
34009                     ]
34010                 },
34011                 {
34012                     tag : 'div',
34013                     cls : 'roo-document-slider-body',
34014                     cn : [
34015                         {
34016                             tag : 'div',
34017                             cls : 'roo-document-slider-prev',
34018                             cn : [
34019                                 {
34020                                     tag : 'i',
34021                                     cls : 'fa fa-chevron-left'
34022                                 }
34023                             ]
34024                         },
34025                         {
34026                             tag : 'div',
34027                             cls : 'roo-document-slider-thumb',
34028                             cn : [
34029                                 {
34030                                     tag : 'img',
34031                                     cls : 'roo-document-slider-image'
34032                                 }
34033                             ]
34034                         },
34035                         {
34036                             tag : 'div',
34037                             cls : 'roo-document-slider-next',
34038                             cn : [
34039                                 {
34040                                     tag : 'i',
34041                                     cls : 'fa fa-chevron-right'
34042                                 }
34043                             ]
34044                         }
34045                     ]
34046                 }
34047             ]
34048         };
34049         
34050         return cfg;
34051     },
34052     
34053     initEvents : function()
34054     {
34055         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34056         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34057         
34058         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34059         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34060         
34061         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34062         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34063         
34064         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34065         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34066         
34067         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34068         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34069         
34070         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34071         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34072         
34073         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34074         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34075         
34076         this.thumbEl.on('click', this.onClick, this);
34077         
34078         this.prevIndicator.on('click', this.prev, this);
34079         
34080         this.nextIndicator.on('click', this.next, this);
34081         
34082     },
34083     
34084     initial : function()
34085     {
34086         if(this.files.length){
34087             this.indicator = 1;
34088             this.update()
34089         }
34090         
34091         this.fireEvent('initial', this);
34092     },
34093     
34094     update : function()
34095     {
34096         this.imageEl.attr('src', this.files[this.indicator - 1]);
34097         
34098         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34099         
34100         this.prevIndicator.show();
34101         
34102         if(this.indicator == 1){
34103             this.prevIndicator.hide();
34104         }
34105         
34106         this.nextIndicator.show();
34107         
34108         if(this.indicator == this.files.length){
34109             this.nextIndicator.hide();
34110         }
34111         
34112         this.thumbEl.scrollTo('top');
34113         
34114         this.fireEvent('update', this);
34115     },
34116     
34117     onClick : function(e)
34118     {
34119         e.preventDefault();
34120         
34121         this.fireEvent('click', this);
34122     },
34123     
34124     prev : function(e)
34125     {
34126         e.preventDefault();
34127         
34128         this.indicator = Math.max(1, this.indicator - 1);
34129         
34130         this.update();
34131     },
34132     
34133     next : function(e)
34134     {
34135         e.preventDefault();
34136         
34137         this.indicator = Math.min(this.files.length, this.indicator + 1);
34138         
34139         this.update();
34140     }
34141 });
34142 /*
34143  * - LGPL
34144  *
34145  * RadioSet
34146  *
34147  *
34148  */
34149
34150 /**
34151  * @class Roo.bootstrap.RadioSet
34152  * @extends Roo.bootstrap.Input
34153  * Bootstrap RadioSet class
34154  * @cfg {String} indicatorpos (left|right) default left
34155  * @cfg {Boolean} inline (true|false) inline the element (default true)
34156  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34157  * @constructor
34158  * Create a new RadioSet
34159  * @param {Object} config The config object
34160  */
34161
34162 Roo.bootstrap.RadioSet = function(config){
34163     
34164     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34165     
34166     this.radioes = [];
34167     
34168     Roo.bootstrap.RadioSet.register(this);
34169     
34170     this.addEvents({
34171         /**
34172         * @event check
34173         * Fires when the element is checked or unchecked.
34174         * @param {Roo.bootstrap.RadioSet} this This radio
34175         * @param {Roo.bootstrap.Radio} item The checked item
34176         */
34177        check : true,
34178        /**
34179         * @event click
34180         * Fires when the element is click.
34181         * @param {Roo.bootstrap.RadioSet} this This radio set
34182         * @param {Roo.bootstrap.Radio} item The checked item
34183         * @param {Roo.EventObject} e The event object
34184         */
34185        click : true
34186     });
34187     
34188 };
34189
34190 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34191
34192     radioes : false,
34193     
34194     inline : true,
34195     
34196     weight : '',
34197     
34198     indicatorpos : 'left',
34199     
34200     getAutoCreate : function()
34201     {
34202         var label = {
34203             tag : 'label',
34204             cls : 'roo-radio-set-label',
34205             cn : [
34206                 {
34207                     tag : 'span',
34208                     html : this.fieldLabel
34209                 }
34210             ]
34211         };
34212         if (Roo.bootstrap.version == 3) {
34213             
34214             
34215             if(this.indicatorpos == 'left'){
34216                 label.cn.unshift({
34217                     tag : 'i',
34218                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34219                     tooltip : 'This field is required'
34220                 });
34221             } else {
34222                 label.cn.push({
34223                     tag : 'i',
34224                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34225                     tooltip : 'This field is required'
34226                 });
34227             }
34228         }
34229         var items = {
34230             tag : 'div',
34231             cls : 'roo-radio-set-items'
34232         };
34233         
34234         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34235         
34236         if (align === 'left' && this.fieldLabel.length) {
34237             
34238             items = {
34239                 cls : "roo-radio-set-right", 
34240                 cn: [
34241                     items
34242                 ]
34243             };
34244             
34245             if(this.labelWidth > 12){
34246                 label.style = "width: " + this.labelWidth + 'px';
34247             }
34248             
34249             if(this.labelWidth < 13 && this.labelmd == 0){
34250                 this.labelmd = this.labelWidth;
34251             }
34252             
34253             if(this.labellg > 0){
34254                 label.cls += ' col-lg-' + this.labellg;
34255                 items.cls += ' col-lg-' + (12 - this.labellg);
34256             }
34257             
34258             if(this.labelmd > 0){
34259                 label.cls += ' col-md-' + this.labelmd;
34260                 items.cls += ' col-md-' + (12 - this.labelmd);
34261             }
34262             
34263             if(this.labelsm > 0){
34264                 label.cls += ' col-sm-' + this.labelsm;
34265                 items.cls += ' col-sm-' + (12 - this.labelsm);
34266             }
34267             
34268             if(this.labelxs > 0){
34269                 label.cls += ' col-xs-' + this.labelxs;
34270                 items.cls += ' col-xs-' + (12 - this.labelxs);
34271             }
34272         }
34273         
34274         var cfg = {
34275             tag : 'div',
34276             cls : 'roo-radio-set',
34277             cn : [
34278                 {
34279                     tag : 'input',
34280                     cls : 'roo-radio-set-input',
34281                     type : 'hidden',
34282                     name : this.name,
34283                     value : this.value ? this.value :  ''
34284                 },
34285                 label,
34286                 items
34287             ]
34288         };
34289         
34290         if(this.weight.length){
34291             cfg.cls += ' roo-radio-' + this.weight;
34292         }
34293         
34294         if(this.inline) {
34295             cfg.cls += ' roo-radio-set-inline';
34296         }
34297         
34298         var settings=this;
34299         ['xs','sm','md','lg'].map(function(size){
34300             if (settings[size]) {
34301                 cfg.cls += ' col-' + size + '-' + settings[size];
34302             }
34303         });
34304         
34305         return cfg;
34306         
34307     },
34308
34309     initEvents : function()
34310     {
34311         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34312         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34313         
34314         if(!this.fieldLabel.length){
34315             this.labelEl.hide();
34316         }
34317         
34318         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34319         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34320         
34321         this.indicator = this.indicatorEl();
34322         
34323         if(this.indicator){
34324             this.indicator.addClass('invisible');
34325         }
34326         
34327         this.originalValue = this.getValue();
34328         
34329     },
34330     
34331     inputEl: function ()
34332     {
34333         return this.el.select('.roo-radio-set-input', true).first();
34334     },
34335     
34336     getChildContainer : function()
34337     {
34338         return this.itemsEl;
34339     },
34340     
34341     register : function(item)
34342     {
34343         this.radioes.push(item);
34344         
34345     },
34346     
34347     validate : function()
34348     {   
34349         if(this.getVisibilityEl().hasClass('hidden')){
34350             return true;
34351         }
34352         
34353         var valid = false;
34354         
34355         Roo.each(this.radioes, function(i){
34356             if(!i.checked){
34357                 return;
34358             }
34359             
34360             valid = true;
34361             return false;
34362         });
34363         
34364         if(this.allowBlank) {
34365             return true;
34366         }
34367         
34368         if(this.disabled || valid){
34369             this.markValid();
34370             return true;
34371         }
34372         
34373         this.markInvalid();
34374         return false;
34375         
34376     },
34377     
34378     markValid : function()
34379     {
34380         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34381             this.indicatorEl().removeClass('visible');
34382             this.indicatorEl().addClass('invisible');
34383         }
34384         
34385         
34386         if (Roo.bootstrap.version == 3) {
34387             this.el.removeClass([this.invalidClass, this.validClass]);
34388             this.el.addClass(this.validClass);
34389         } else {
34390             this.el.removeClass(['is-invalid','is-valid']);
34391             this.el.addClass(['is-valid']);
34392         }
34393         this.fireEvent('valid', this);
34394     },
34395     
34396     markInvalid : function(msg)
34397     {
34398         if(this.allowBlank || this.disabled){
34399             return;
34400         }
34401         
34402         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34403             this.indicatorEl().removeClass('invisible');
34404             this.indicatorEl().addClass('visible');
34405         }
34406         if (Roo.bootstrap.version == 3) {
34407             this.el.removeClass([this.invalidClass, this.validClass]);
34408             this.el.addClass(this.invalidClass);
34409         } else {
34410             this.el.removeClass(['is-invalid','is-valid']);
34411             this.el.addClass(['is-invalid']);
34412         }
34413         
34414         this.fireEvent('invalid', this, msg);
34415         
34416     },
34417     
34418     setValue : function(v, suppressEvent)
34419     {   
34420         if(this.value === v){
34421             return;
34422         }
34423         
34424         this.value = v;
34425         
34426         if(this.rendered){
34427             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34428         }
34429         
34430         Roo.each(this.radioes, function(i){
34431             i.checked = false;
34432             i.el.removeClass('checked');
34433         });
34434         
34435         Roo.each(this.radioes, function(i){
34436             
34437             if(i.value === v || i.value.toString() === v.toString()){
34438                 i.checked = true;
34439                 i.el.addClass('checked');
34440                 
34441                 if(suppressEvent !== true){
34442                     this.fireEvent('check', this, i);
34443                 }
34444                 
34445                 return false;
34446             }
34447             
34448         }, this);
34449         
34450         this.validate();
34451     },
34452     
34453     clearInvalid : function(){
34454         
34455         if(!this.el || this.preventMark){
34456             return;
34457         }
34458         
34459         this.el.removeClass([this.invalidClass]);
34460         
34461         this.fireEvent('valid', this);
34462     }
34463     
34464 });
34465
34466 Roo.apply(Roo.bootstrap.RadioSet, {
34467     
34468     groups: {},
34469     
34470     register : function(set)
34471     {
34472         this.groups[set.name] = set;
34473     },
34474     
34475     get: function(name) 
34476     {
34477         if (typeof(this.groups[name]) == 'undefined') {
34478             return false;
34479         }
34480         
34481         return this.groups[name] ;
34482     }
34483     
34484 });
34485 /*
34486  * Based on:
34487  * Ext JS Library 1.1.1
34488  * Copyright(c) 2006-2007, Ext JS, LLC.
34489  *
34490  * Originally Released Under LGPL - original licence link has changed is not relivant.
34491  *
34492  * Fork - LGPL
34493  * <script type="text/javascript">
34494  */
34495
34496
34497 /**
34498  * @class Roo.bootstrap.SplitBar
34499  * @extends Roo.util.Observable
34500  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34501  * <br><br>
34502  * Usage:
34503  * <pre><code>
34504 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34505                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34506 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34507 split.minSize = 100;
34508 split.maxSize = 600;
34509 split.animate = true;
34510 split.on('moved', splitterMoved);
34511 </code></pre>
34512  * @constructor
34513  * Create a new SplitBar
34514  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34515  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34516  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34517  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34518                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34519                         position of the SplitBar).
34520  */
34521 Roo.bootstrap.SplitBar = function(cfg){
34522     
34523     /** @private */
34524     
34525     //{
34526     //  dragElement : elm
34527     //  resizingElement: el,
34528         // optional..
34529     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34530     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34531         // existingProxy ???
34532     //}
34533     
34534     this.el = Roo.get(cfg.dragElement, true);
34535     this.el.dom.unselectable = "on";
34536     /** @private */
34537     this.resizingEl = Roo.get(cfg.resizingElement, true);
34538
34539     /**
34540      * @private
34541      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34542      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34543      * @type Number
34544      */
34545     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34546     
34547     /**
34548      * The minimum size of the resizing element. (Defaults to 0)
34549      * @type Number
34550      */
34551     this.minSize = 0;
34552     
34553     /**
34554      * The maximum size of the resizing element. (Defaults to 2000)
34555      * @type Number
34556      */
34557     this.maxSize = 2000;
34558     
34559     /**
34560      * Whether to animate the transition to the new size
34561      * @type Boolean
34562      */
34563     this.animate = false;
34564     
34565     /**
34566      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34567      * @type Boolean
34568      */
34569     this.useShim = false;
34570     
34571     /** @private */
34572     this.shim = null;
34573     
34574     if(!cfg.existingProxy){
34575         /** @private */
34576         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34577     }else{
34578         this.proxy = Roo.get(cfg.existingProxy).dom;
34579     }
34580     /** @private */
34581     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34582     
34583     /** @private */
34584     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34585     
34586     /** @private */
34587     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34588     
34589     /** @private */
34590     this.dragSpecs = {};
34591     
34592     /**
34593      * @private The adapter to use to positon and resize elements
34594      */
34595     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34596     this.adapter.init(this);
34597     
34598     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34599         /** @private */
34600         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34601         this.el.addClass("roo-splitbar-h");
34602     }else{
34603         /** @private */
34604         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34605         this.el.addClass("roo-splitbar-v");
34606     }
34607     
34608     this.addEvents({
34609         /**
34610          * @event resize
34611          * Fires when the splitter is moved (alias for {@link #event-moved})
34612          * @param {Roo.bootstrap.SplitBar} this
34613          * @param {Number} newSize the new width or height
34614          */
34615         "resize" : true,
34616         /**
34617          * @event moved
34618          * Fires when the splitter is moved
34619          * @param {Roo.bootstrap.SplitBar} this
34620          * @param {Number} newSize the new width or height
34621          */
34622         "moved" : true,
34623         /**
34624          * @event beforeresize
34625          * Fires before the splitter is dragged
34626          * @param {Roo.bootstrap.SplitBar} this
34627          */
34628         "beforeresize" : true,
34629
34630         "beforeapply" : true
34631     });
34632
34633     Roo.util.Observable.call(this);
34634 };
34635
34636 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34637     onStartProxyDrag : function(x, y){
34638         this.fireEvent("beforeresize", this);
34639         if(!this.overlay){
34640             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34641             o.unselectable();
34642             o.enableDisplayMode("block");
34643             // all splitbars share the same overlay
34644             Roo.bootstrap.SplitBar.prototype.overlay = o;
34645         }
34646         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34647         this.overlay.show();
34648         Roo.get(this.proxy).setDisplayed("block");
34649         var size = this.adapter.getElementSize(this);
34650         this.activeMinSize = this.getMinimumSize();;
34651         this.activeMaxSize = this.getMaximumSize();;
34652         var c1 = size - this.activeMinSize;
34653         var c2 = Math.max(this.activeMaxSize - size, 0);
34654         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34655             this.dd.resetConstraints();
34656             this.dd.setXConstraint(
34657                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34658                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34659             );
34660             this.dd.setYConstraint(0, 0);
34661         }else{
34662             this.dd.resetConstraints();
34663             this.dd.setXConstraint(0, 0);
34664             this.dd.setYConstraint(
34665                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34666                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34667             );
34668          }
34669         this.dragSpecs.startSize = size;
34670         this.dragSpecs.startPoint = [x, y];
34671         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34672     },
34673     
34674     /** 
34675      * @private Called after the drag operation by the DDProxy
34676      */
34677     onEndProxyDrag : function(e){
34678         Roo.get(this.proxy).setDisplayed(false);
34679         var endPoint = Roo.lib.Event.getXY(e);
34680         if(this.overlay){
34681             this.overlay.hide();
34682         }
34683         var newSize;
34684         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34685             newSize = this.dragSpecs.startSize + 
34686                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34687                     endPoint[0] - this.dragSpecs.startPoint[0] :
34688                     this.dragSpecs.startPoint[0] - endPoint[0]
34689                 );
34690         }else{
34691             newSize = this.dragSpecs.startSize + 
34692                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34693                     endPoint[1] - this.dragSpecs.startPoint[1] :
34694                     this.dragSpecs.startPoint[1] - endPoint[1]
34695                 );
34696         }
34697         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34698         if(newSize != this.dragSpecs.startSize){
34699             if(this.fireEvent('beforeapply', this, newSize) !== false){
34700                 this.adapter.setElementSize(this, newSize);
34701                 this.fireEvent("moved", this, newSize);
34702                 this.fireEvent("resize", this, newSize);
34703             }
34704         }
34705     },
34706     
34707     /**
34708      * Get the adapter this SplitBar uses
34709      * @return The adapter object
34710      */
34711     getAdapter : function(){
34712         return this.adapter;
34713     },
34714     
34715     /**
34716      * Set the adapter this SplitBar uses
34717      * @param {Object} adapter A SplitBar adapter object
34718      */
34719     setAdapter : function(adapter){
34720         this.adapter = adapter;
34721         this.adapter.init(this);
34722     },
34723     
34724     /**
34725      * Gets the minimum size for the resizing element
34726      * @return {Number} The minimum size
34727      */
34728     getMinimumSize : function(){
34729         return this.minSize;
34730     },
34731     
34732     /**
34733      * Sets the minimum size for the resizing element
34734      * @param {Number} minSize The minimum size
34735      */
34736     setMinimumSize : function(minSize){
34737         this.minSize = minSize;
34738     },
34739     
34740     /**
34741      * Gets the maximum size for the resizing element
34742      * @return {Number} The maximum size
34743      */
34744     getMaximumSize : function(){
34745         return this.maxSize;
34746     },
34747     
34748     /**
34749      * Sets the maximum size for the resizing element
34750      * @param {Number} maxSize The maximum size
34751      */
34752     setMaximumSize : function(maxSize){
34753         this.maxSize = maxSize;
34754     },
34755     
34756     /**
34757      * Sets the initialize size for the resizing element
34758      * @param {Number} size The initial size
34759      */
34760     setCurrentSize : function(size){
34761         var oldAnimate = this.animate;
34762         this.animate = false;
34763         this.adapter.setElementSize(this, size);
34764         this.animate = oldAnimate;
34765     },
34766     
34767     /**
34768      * Destroy this splitbar. 
34769      * @param {Boolean} removeEl True to remove the element
34770      */
34771     destroy : function(removeEl){
34772         if(this.shim){
34773             this.shim.remove();
34774         }
34775         this.dd.unreg();
34776         this.proxy.parentNode.removeChild(this.proxy);
34777         if(removeEl){
34778             this.el.remove();
34779         }
34780     }
34781 });
34782
34783 /**
34784  * @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.
34785  */
34786 Roo.bootstrap.SplitBar.createProxy = function(dir){
34787     var proxy = new Roo.Element(document.createElement("div"));
34788     proxy.unselectable();
34789     var cls = 'roo-splitbar-proxy';
34790     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34791     document.body.appendChild(proxy.dom);
34792     return proxy.dom;
34793 };
34794
34795 /** 
34796  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34797  * Default Adapter. It assumes the splitter and resizing element are not positioned
34798  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34799  */
34800 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34801 };
34802
34803 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34804     // do nothing for now
34805     init : function(s){
34806     
34807     },
34808     /**
34809      * Called before drag operations to get the current size of the resizing element. 
34810      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34811      */
34812      getElementSize : function(s){
34813         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34814             return s.resizingEl.getWidth();
34815         }else{
34816             return s.resizingEl.getHeight();
34817         }
34818     },
34819     
34820     /**
34821      * Called after drag operations to set the size of the resizing element.
34822      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34823      * @param {Number} newSize The new size to set
34824      * @param {Function} onComplete A function to be invoked when resizing is complete
34825      */
34826     setElementSize : function(s, newSize, onComplete){
34827         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34828             if(!s.animate){
34829                 s.resizingEl.setWidth(newSize);
34830                 if(onComplete){
34831                     onComplete(s, newSize);
34832                 }
34833             }else{
34834                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34835             }
34836         }else{
34837             
34838             if(!s.animate){
34839                 s.resizingEl.setHeight(newSize);
34840                 if(onComplete){
34841                     onComplete(s, newSize);
34842                 }
34843             }else{
34844                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34845             }
34846         }
34847     }
34848 };
34849
34850 /** 
34851  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34852  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34853  * Adapter that  moves the splitter element to align with the resized sizing element. 
34854  * Used with an absolute positioned SplitBar.
34855  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34856  * document.body, make sure you assign an id to the body element.
34857  */
34858 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34859     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34860     this.container = Roo.get(container);
34861 };
34862
34863 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34864     init : function(s){
34865         this.basic.init(s);
34866     },
34867     
34868     getElementSize : function(s){
34869         return this.basic.getElementSize(s);
34870     },
34871     
34872     setElementSize : function(s, newSize, onComplete){
34873         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34874     },
34875     
34876     moveSplitter : function(s){
34877         var yes = Roo.bootstrap.SplitBar;
34878         switch(s.placement){
34879             case yes.LEFT:
34880                 s.el.setX(s.resizingEl.getRight());
34881                 break;
34882             case yes.RIGHT:
34883                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34884                 break;
34885             case yes.TOP:
34886                 s.el.setY(s.resizingEl.getBottom());
34887                 break;
34888             case yes.BOTTOM:
34889                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34890                 break;
34891         }
34892     }
34893 };
34894
34895 /**
34896  * Orientation constant - Create a vertical SplitBar
34897  * @static
34898  * @type Number
34899  */
34900 Roo.bootstrap.SplitBar.VERTICAL = 1;
34901
34902 /**
34903  * Orientation constant - Create a horizontal SplitBar
34904  * @static
34905  * @type Number
34906  */
34907 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34908
34909 /**
34910  * Placement constant - The resizing element is to the left of the splitter element
34911  * @static
34912  * @type Number
34913  */
34914 Roo.bootstrap.SplitBar.LEFT = 1;
34915
34916 /**
34917  * Placement constant - The resizing element is to the right of the splitter element
34918  * @static
34919  * @type Number
34920  */
34921 Roo.bootstrap.SplitBar.RIGHT = 2;
34922
34923 /**
34924  * Placement constant - The resizing element is positioned above the splitter element
34925  * @static
34926  * @type Number
34927  */
34928 Roo.bootstrap.SplitBar.TOP = 3;
34929
34930 /**
34931  * Placement constant - The resizing element is positioned under splitter element
34932  * @static
34933  * @type Number
34934  */
34935 Roo.bootstrap.SplitBar.BOTTOM = 4;
34936 Roo.namespace("Roo.bootstrap.layout");/*
34937  * Based on:
34938  * Ext JS Library 1.1.1
34939  * Copyright(c) 2006-2007, Ext JS, LLC.
34940  *
34941  * Originally Released Under LGPL - original licence link has changed is not relivant.
34942  *
34943  * Fork - LGPL
34944  * <script type="text/javascript">
34945  */
34946
34947 /**
34948  * @class Roo.bootstrap.layout.Manager
34949  * @extends Roo.bootstrap.Component
34950  * Base class for layout managers.
34951  */
34952 Roo.bootstrap.layout.Manager = function(config)
34953 {
34954     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34955
34956
34957
34958
34959
34960     /** false to disable window resize monitoring @type Boolean */
34961     this.monitorWindowResize = true;
34962     this.regions = {};
34963     this.addEvents({
34964         /**
34965          * @event layout
34966          * Fires when a layout is performed.
34967          * @param {Roo.LayoutManager} this
34968          */
34969         "layout" : true,
34970         /**
34971          * @event regionresized
34972          * Fires when the user resizes a region.
34973          * @param {Roo.LayoutRegion} region The resized region
34974          * @param {Number} newSize The new size (width for east/west, height for north/south)
34975          */
34976         "regionresized" : true,
34977         /**
34978          * @event regioncollapsed
34979          * Fires when a region is collapsed.
34980          * @param {Roo.LayoutRegion} region The collapsed region
34981          */
34982         "regioncollapsed" : true,
34983         /**
34984          * @event regionexpanded
34985          * Fires when a region is expanded.
34986          * @param {Roo.LayoutRegion} region The expanded region
34987          */
34988         "regionexpanded" : true
34989     });
34990     this.updating = false;
34991
34992     if (config.el) {
34993         this.el = Roo.get(config.el);
34994         this.initEvents();
34995     }
34996
34997 };
34998
34999 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35000
35001
35002     regions : null,
35003
35004     monitorWindowResize : true,
35005
35006
35007     updating : false,
35008
35009
35010     onRender : function(ct, position)
35011     {
35012         if(!this.el){
35013             this.el = Roo.get(ct);
35014             this.initEvents();
35015         }
35016         //this.fireEvent('render',this);
35017     },
35018
35019
35020     initEvents: function()
35021     {
35022
35023
35024         // ie scrollbar fix
35025         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35026             document.body.scroll = "no";
35027         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35028             this.el.position('relative');
35029         }
35030         this.id = this.el.id;
35031         this.el.addClass("roo-layout-container");
35032         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35033         if(this.el.dom != document.body ) {
35034             this.el.on('resize', this.layout,this);
35035             this.el.on('show', this.layout,this);
35036         }
35037
35038     },
35039
35040     /**
35041      * Returns true if this layout is currently being updated
35042      * @return {Boolean}
35043      */
35044     isUpdating : function(){
35045         return this.updating;
35046     },
35047
35048     /**
35049      * Suspend the LayoutManager from doing auto-layouts while
35050      * making multiple add or remove calls
35051      */
35052     beginUpdate : function(){
35053         this.updating = true;
35054     },
35055
35056     /**
35057      * Restore auto-layouts and optionally disable the manager from performing a layout
35058      * @param {Boolean} noLayout true to disable a layout update
35059      */
35060     endUpdate : function(noLayout){
35061         this.updating = false;
35062         if(!noLayout){
35063             this.layout();
35064         }
35065     },
35066
35067     layout: function(){
35068         // abstract...
35069     },
35070
35071     onRegionResized : function(region, newSize){
35072         this.fireEvent("regionresized", region, newSize);
35073         this.layout();
35074     },
35075
35076     onRegionCollapsed : function(region){
35077         this.fireEvent("regioncollapsed", region);
35078     },
35079
35080     onRegionExpanded : function(region){
35081         this.fireEvent("regionexpanded", region);
35082     },
35083
35084     /**
35085      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35086      * performs box-model adjustments.
35087      * @return {Object} The size as an object {width: (the width), height: (the height)}
35088      */
35089     getViewSize : function()
35090     {
35091         var size;
35092         if(this.el.dom != document.body){
35093             size = this.el.getSize();
35094         }else{
35095             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35096         }
35097         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35098         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35099         return size;
35100     },
35101
35102     /**
35103      * Returns the Element this layout is bound to.
35104      * @return {Roo.Element}
35105      */
35106     getEl : function(){
35107         return this.el;
35108     },
35109
35110     /**
35111      * Returns the specified region.
35112      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35113      * @return {Roo.LayoutRegion}
35114      */
35115     getRegion : function(target){
35116         return this.regions[target.toLowerCase()];
35117     },
35118
35119     onWindowResize : function(){
35120         if(this.monitorWindowResize){
35121             this.layout();
35122         }
35123     }
35124 });
35125 /*
35126  * Based on:
35127  * Ext JS Library 1.1.1
35128  * Copyright(c) 2006-2007, Ext JS, LLC.
35129  *
35130  * Originally Released Under LGPL - original licence link has changed is not relivant.
35131  *
35132  * Fork - LGPL
35133  * <script type="text/javascript">
35134  */
35135 /**
35136  * @class Roo.bootstrap.layout.Border
35137  * @extends Roo.bootstrap.layout.Manager
35138  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35139  * please see: examples/bootstrap/nested.html<br><br>
35140  
35141 <b>The container the layout is rendered into can be either the body element or any other element.
35142 If it is not the body element, the container needs to either be an absolute positioned element,
35143 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35144 the container size if it is not the body element.</b>
35145
35146 * @constructor
35147 * Create a new Border
35148 * @param {Object} config Configuration options
35149  */
35150 Roo.bootstrap.layout.Border = function(config){
35151     config = config || {};
35152     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35153     
35154     
35155     
35156     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35157         if(config[region]){
35158             config[region].region = region;
35159             this.addRegion(config[region]);
35160         }
35161     },this);
35162     
35163 };
35164
35165 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35166
35167 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35168     
35169     parent : false, // this might point to a 'nest' or a ???
35170     
35171     /**
35172      * Creates and adds a new region if it doesn't already exist.
35173      * @param {String} target The target region key (north, south, east, west or center).
35174      * @param {Object} config The regions config object
35175      * @return {BorderLayoutRegion} The new region
35176      */
35177     addRegion : function(config)
35178     {
35179         if(!this.regions[config.region]){
35180             var r = this.factory(config);
35181             this.bindRegion(r);
35182         }
35183         return this.regions[config.region];
35184     },
35185
35186     // private (kinda)
35187     bindRegion : function(r){
35188         this.regions[r.config.region] = r;
35189         
35190         r.on("visibilitychange",    this.layout, this);
35191         r.on("paneladded",          this.layout, this);
35192         r.on("panelremoved",        this.layout, this);
35193         r.on("invalidated",         this.layout, this);
35194         r.on("resized",             this.onRegionResized, this);
35195         r.on("collapsed",           this.onRegionCollapsed, this);
35196         r.on("expanded",            this.onRegionExpanded, this);
35197     },
35198
35199     /**
35200      * Performs a layout update.
35201      */
35202     layout : function()
35203     {
35204         if(this.updating) {
35205             return;
35206         }
35207         
35208         // render all the rebions if they have not been done alreayd?
35209         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35210             if(this.regions[region] && !this.regions[region].bodyEl){
35211                 this.regions[region].onRender(this.el)
35212             }
35213         },this);
35214         
35215         var size = this.getViewSize();
35216         var w = size.width;
35217         var h = size.height;
35218         var centerW = w;
35219         var centerH = h;
35220         var centerY = 0;
35221         var centerX = 0;
35222         //var x = 0, y = 0;
35223
35224         var rs = this.regions;
35225         var north = rs["north"];
35226         var south = rs["south"]; 
35227         var west = rs["west"];
35228         var east = rs["east"];
35229         var center = rs["center"];
35230         //if(this.hideOnLayout){ // not supported anymore
35231             //c.el.setStyle("display", "none");
35232         //}
35233         if(north && north.isVisible()){
35234             var b = north.getBox();
35235             var m = north.getMargins();
35236             b.width = w - (m.left+m.right);
35237             b.x = m.left;
35238             b.y = m.top;
35239             centerY = b.height + b.y + m.bottom;
35240             centerH -= centerY;
35241             north.updateBox(this.safeBox(b));
35242         }
35243         if(south && south.isVisible()){
35244             var b = south.getBox();
35245             var m = south.getMargins();
35246             b.width = w - (m.left+m.right);
35247             b.x = m.left;
35248             var totalHeight = (b.height + m.top + m.bottom);
35249             b.y = h - totalHeight + m.top;
35250             centerH -= totalHeight;
35251             south.updateBox(this.safeBox(b));
35252         }
35253         if(west && west.isVisible()){
35254             var b = west.getBox();
35255             var m = west.getMargins();
35256             b.height = centerH - (m.top+m.bottom);
35257             b.x = m.left;
35258             b.y = centerY + m.top;
35259             var totalWidth = (b.width + m.left + m.right);
35260             centerX += totalWidth;
35261             centerW -= totalWidth;
35262             west.updateBox(this.safeBox(b));
35263         }
35264         if(east && east.isVisible()){
35265             var b = east.getBox();
35266             var m = east.getMargins();
35267             b.height = centerH - (m.top+m.bottom);
35268             var totalWidth = (b.width + m.left + m.right);
35269             b.x = w - totalWidth + m.left;
35270             b.y = centerY + m.top;
35271             centerW -= totalWidth;
35272             east.updateBox(this.safeBox(b));
35273         }
35274         if(center){
35275             var m = center.getMargins();
35276             var centerBox = {
35277                 x: centerX + m.left,
35278                 y: centerY + m.top,
35279                 width: centerW - (m.left+m.right),
35280                 height: centerH - (m.top+m.bottom)
35281             };
35282             //if(this.hideOnLayout){
35283                 //center.el.setStyle("display", "block");
35284             //}
35285             center.updateBox(this.safeBox(centerBox));
35286         }
35287         this.el.repaint();
35288         this.fireEvent("layout", this);
35289     },
35290
35291     // private
35292     safeBox : function(box){
35293         box.width = Math.max(0, box.width);
35294         box.height = Math.max(0, box.height);
35295         return box;
35296     },
35297
35298     /**
35299      * Adds a ContentPanel (or subclass) to this layout.
35300      * @param {String} target The target region key (north, south, east, west or center).
35301      * @param {Roo.ContentPanel} panel The panel to add
35302      * @return {Roo.ContentPanel} The added panel
35303      */
35304     add : function(target, panel){
35305          
35306         target = target.toLowerCase();
35307         return this.regions[target].add(panel);
35308     },
35309
35310     /**
35311      * Remove a ContentPanel (or subclass) to this layout.
35312      * @param {String} target The target region key (north, south, east, west or center).
35313      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35314      * @return {Roo.ContentPanel} The removed panel
35315      */
35316     remove : function(target, panel){
35317         target = target.toLowerCase();
35318         return this.regions[target].remove(panel);
35319     },
35320
35321     /**
35322      * Searches all regions for a panel with the specified id
35323      * @param {String} panelId
35324      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35325      */
35326     findPanel : function(panelId){
35327         var rs = this.regions;
35328         for(var target in rs){
35329             if(typeof rs[target] != "function"){
35330                 var p = rs[target].getPanel(panelId);
35331                 if(p){
35332                     return p;
35333                 }
35334             }
35335         }
35336         return null;
35337     },
35338
35339     /**
35340      * Searches all regions for a panel with the specified id and activates (shows) it.
35341      * @param {String/ContentPanel} panelId The panels id or the panel itself
35342      * @return {Roo.ContentPanel} The shown panel or null
35343      */
35344     showPanel : function(panelId) {
35345       var rs = this.regions;
35346       for(var target in rs){
35347          var r = rs[target];
35348          if(typeof r != "function"){
35349             if(r.hasPanel(panelId)){
35350                return r.showPanel(panelId);
35351             }
35352          }
35353       }
35354       return null;
35355    },
35356
35357    /**
35358      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35359      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35360      */
35361    /*
35362     restoreState : function(provider){
35363         if(!provider){
35364             provider = Roo.state.Manager;
35365         }
35366         var sm = new Roo.LayoutStateManager();
35367         sm.init(this, provider);
35368     },
35369 */
35370  
35371  
35372     /**
35373      * Adds a xtype elements to the layout.
35374      * <pre><code>
35375
35376 layout.addxtype({
35377        xtype : 'ContentPanel',
35378        region: 'west',
35379        items: [ .... ]
35380    }
35381 );
35382
35383 layout.addxtype({
35384         xtype : 'NestedLayoutPanel',
35385         region: 'west',
35386         layout: {
35387            center: { },
35388            west: { }   
35389         },
35390         items : [ ... list of content panels or nested layout panels.. ]
35391    }
35392 );
35393 </code></pre>
35394      * @param {Object} cfg Xtype definition of item to add.
35395      */
35396     addxtype : function(cfg)
35397     {
35398         // basically accepts a pannel...
35399         // can accept a layout region..!?!?
35400         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35401         
35402         
35403         // theory?  children can only be panels??
35404         
35405         //if (!cfg.xtype.match(/Panel$/)) {
35406         //    return false;
35407         //}
35408         var ret = false;
35409         
35410         if (typeof(cfg.region) == 'undefined') {
35411             Roo.log("Failed to add Panel, region was not set");
35412             Roo.log(cfg);
35413             return false;
35414         }
35415         var region = cfg.region;
35416         delete cfg.region;
35417         
35418           
35419         var xitems = [];
35420         if (cfg.items) {
35421             xitems = cfg.items;
35422             delete cfg.items;
35423         }
35424         var nb = false;
35425         
35426         if ( region == 'center') {
35427             Roo.log("Center: " + cfg.title);
35428         }
35429         
35430         
35431         switch(cfg.xtype) 
35432         {
35433             case 'Content':  // ContentPanel (el, cfg)
35434             case 'Scroll':  // ContentPanel (el, cfg)
35435             case 'View': 
35436                 cfg.autoCreate = cfg.autoCreate || true;
35437                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35438                 //} else {
35439                 //    var el = this.el.createChild();
35440                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35441                 //}
35442                 
35443                 this.add(region, ret);
35444                 break;
35445             
35446             /*
35447             case 'TreePanel': // our new panel!
35448                 cfg.el = this.el.createChild();
35449                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35450                 this.add(region, ret);
35451                 break;
35452             */
35453             
35454             case 'Nest': 
35455                 // create a new Layout (which is  a Border Layout...
35456                 
35457                 var clayout = cfg.layout;
35458                 clayout.el  = this.el.createChild();
35459                 clayout.items   = clayout.items  || [];
35460                 
35461                 delete cfg.layout;
35462                 
35463                 // replace this exitems with the clayout ones..
35464                 xitems = clayout.items;
35465                  
35466                 // force background off if it's in center...
35467                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35468                     cfg.background = false;
35469                 }
35470                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35471                 
35472                 
35473                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35474                 //console.log('adding nested layout panel '  + cfg.toSource());
35475                 this.add(region, ret);
35476                 nb = {}; /// find first...
35477                 break;
35478             
35479             case 'Grid':
35480                 
35481                 // needs grid and region
35482                 
35483                 //var el = this.getRegion(region).el.createChild();
35484                 /*
35485                  *var el = this.el.createChild();
35486                 // create the grid first...
35487                 cfg.grid.container = el;
35488                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35489                 */
35490                 
35491                 if (region == 'center' && this.active ) {
35492                     cfg.background = false;
35493                 }
35494                 
35495                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35496                 
35497                 this.add(region, ret);
35498                 /*
35499                 if (cfg.background) {
35500                     // render grid on panel activation (if panel background)
35501                     ret.on('activate', function(gp) {
35502                         if (!gp.grid.rendered) {
35503                     //        gp.grid.render(el);
35504                         }
35505                     });
35506                 } else {
35507                   //  cfg.grid.render(el);
35508                 }
35509                 */
35510                 break;
35511            
35512            
35513             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35514                 // it was the old xcomponent building that caused this before.
35515                 // espeically if border is the top element in the tree.
35516                 ret = this;
35517                 break; 
35518                 
35519                     
35520                 
35521                 
35522                 
35523             default:
35524                 /*
35525                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35526                     
35527                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35528                     this.add(region, ret);
35529                 } else {
35530                 */
35531                     Roo.log(cfg);
35532                     throw "Can not add '" + cfg.xtype + "' to Border";
35533                     return null;
35534              
35535                                 
35536              
35537         }
35538         this.beginUpdate();
35539         // add children..
35540         var region = '';
35541         var abn = {};
35542         Roo.each(xitems, function(i)  {
35543             region = nb && i.region ? i.region : false;
35544             
35545             var add = ret.addxtype(i);
35546            
35547             if (region) {
35548                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35549                 if (!i.background) {
35550                     abn[region] = nb[region] ;
35551                 }
35552             }
35553             
35554         });
35555         this.endUpdate();
35556
35557         // make the last non-background panel active..
35558         //if (nb) { Roo.log(abn); }
35559         if (nb) {
35560             
35561             for(var r in abn) {
35562                 region = this.getRegion(r);
35563                 if (region) {
35564                     // tried using nb[r], but it does not work..
35565                      
35566                     region.showPanel(abn[r]);
35567                    
35568                 }
35569             }
35570         }
35571         return ret;
35572         
35573     },
35574     
35575     
35576 // private
35577     factory : function(cfg)
35578     {
35579         
35580         var validRegions = Roo.bootstrap.layout.Border.regions;
35581
35582         var target = cfg.region;
35583         cfg.mgr = this;
35584         
35585         var r = Roo.bootstrap.layout;
35586         Roo.log(target);
35587         switch(target){
35588             case "north":
35589                 return new r.North(cfg);
35590             case "south":
35591                 return new r.South(cfg);
35592             case "east":
35593                 return new r.East(cfg);
35594             case "west":
35595                 return new r.West(cfg);
35596             case "center":
35597                 return new r.Center(cfg);
35598         }
35599         throw 'Layout region "'+target+'" not supported.';
35600     }
35601     
35602     
35603 });
35604  /*
35605  * Based on:
35606  * Ext JS Library 1.1.1
35607  * Copyright(c) 2006-2007, Ext JS, LLC.
35608  *
35609  * Originally Released Under LGPL - original licence link has changed is not relivant.
35610  *
35611  * Fork - LGPL
35612  * <script type="text/javascript">
35613  */
35614  
35615 /**
35616  * @class Roo.bootstrap.layout.Basic
35617  * @extends Roo.util.Observable
35618  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35619  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35620  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35621  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35622  * @cfg {string}   region  the region that it inhabits..
35623  * @cfg {bool}   skipConfig skip config?
35624  * 
35625
35626  */
35627 Roo.bootstrap.layout.Basic = function(config){
35628     
35629     this.mgr = config.mgr;
35630     
35631     this.position = config.region;
35632     
35633     var skipConfig = config.skipConfig;
35634     
35635     this.events = {
35636         /**
35637          * @scope Roo.BasicLayoutRegion
35638          */
35639         
35640         /**
35641          * @event beforeremove
35642          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35643          * @param {Roo.LayoutRegion} this
35644          * @param {Roo.ContentPanel} panel The panel
35645          * @param {Object} e The cancel event object
35646          */
35647         "beforeremove" : true,
35648         /**
35649          * @event invalidated
35650          * Fires when the layout for this region is changed.
35651          * @param {Roo.LayoutRegion} this
35652          */
35653         "invalidated" : true,
35654         /**
35655          * @event visibilitychange
35656          * Fires when this region is shown or hidden 
35657          * @param {Roo.LayoutRegion} this
35658          * @param {Boolean} visibility true or false
35659          */
35660         "visibilitychange" : true,
35661         /**
35662          * @event paneladded
35663          * Fires when a panel is added. 
35664          * @param {Roo.LayoutRegion} this
35665          * @param {Roo.ContentPanel} panel The panel
35666          */
35667         "paneladded" : true,
35668         /**
35669          * @event panelremoved
35670          * Fires when a panel is removed. 
35671          * @param {Roo.LayoutRegion} this
35672          * @param {Roo.ContentPanel} panel The panel
35673          */
35674         "panelremoved" : true,
35675         /**
35676          * @event beforecollapse
35677          * Fires when this region before collapse.
35678          * @param {Roo.LayoutRegion} this
35679          */
35680         "beforecollapse" : true,
35681         /**
35682          * @event collapsed
35683          * Fires when this region is collapsed.
35684          * @param {Roo.LayoutRegion} this
35685          */
35686         "collapsed" : true,
35687         /**
35688          * @event expanded
35689          * Fires when this region is expanded.
35690          * @param {Roo.LayoutRegion} this
35691          */
35692         "expanded" : true,
35693         /**
35694          * @event slideshow
35695          * Fires when this region is slid into view.
35696          * @param {Roo.LayoutRegion} this
35697          */
35698         "slideshow" : true,
35699         /**
35700          * @event slidehide
35701          * Fires when this region slides out of view. 
35702          * @param {Roo.LayoutRegion} this
35703          */
35704         "slidehide" : true,
35705         /**
35706          * @event panelactivated
35707          * Fires when a panel is activated. 
35708          * @param {Roo.LayoutRegion} this
35709          * @param {Roo.ContentPanel} panel The activated panel
35710          */
35711         "panelactivated" : true,
35712         /**
35713          * @event resized
35714          * Fires when the user resizes this region. 
35715          * @param {Roo.LayoutRegion} this
35716          * @param {Number} newSize The new size (width for east/west, height for north/south)
35717          */
35718         "resized" : true
35719     };
35720     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35721     this.panels = new Roo.util.MixedCollection();
35722     this.panels.getKey = this.getPanelId.createDelegate(this);
35723     this.box = null;
35724     this.activePanel = null;
35725     // ensure listeners are added...
35726     
35727     if (config.listeners || config.events) {
35728         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35729             listeners : config.listeners || {},
35730             events : config.events || {}
35731         });
35732     }
35733     
35734     if(skipConfig !== true){
35735         this.applyConfig(config);
35736     }
35737 };
35738
35739 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35740 {
35741     getPanelId : function(p){
35742         return p.getId();
35743     },
35744     
35745     applyConfig : function(config){
35746         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35747         this.config = config;
35748         
35749     },
35750     
35751     /**
35752      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35753      * the width, for horizontal (north, south) the height.
35754      * @param {Number} newSize The new width or height
35755      */
35756     resizeTo : function(newSize){
35757         var el = this.el ? this.el :
35758                  (this.activePanel ? this.activePanel.getEl() : null);
35759         if(el){
35760             switch(this.position){
35761                 case "east":
35762                 case "west":
35763                     el.setWidth(newSize);
35764                     this.fireEvent("resized", this, newSize);
35765                 break;
35766                 case "north":
35767                 case "south":
35768                     el.setHeight(newSize);
35769                     this.fireEvent("resized", this, newSize);
35770                 break;                
35771             }
35772         }
35773     },
35774     
35775     getBox : function(){
35776         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35777     },
35778     
35779     getMargins : function(){
35780         return this.margins;
35781     },
35782     
35783     updateBox : function(box){
35784         this.box = box;
35785         var el = this.activePanel.getEl();
35786         el.dom.style.left = box.x + "px";
35787         el.dom.style.top = box.y + "px";
35788         this.activePanel.setSize(box.width, box.height);
35789     },
35790     
35791     /**
35792      * Returns the container element for this region.
35793      * @return {Roo.Element}
35794      */
35795     getEl : function(){
35796         return this.activePanel;
35797     },
35798     
35799     /**
35800      * Returns true if this region is currently visible.
35801      * @return {Boolean}
35802      */
35803     isVisible : function(){
35804         return this.activePanel ? true : false;
35805     },
35806     
35807     setActivePanel : function(panel){
35808         panel = this.getPanel(panel);
35809         if(this.activePanel && this.activePanel != panel){
35810             this.activePanel.setActiveState(false);
35811             this.activePanel.getEl().setLeftTop(-10000,-10000);
35812         }
35813         this.activePanel = panel;
35814         panel.setActiveState(true);
35815         if(this.box){
35816             panel.setSize(this.box.width, this.box.height);
35817         }
35818         this.fireEvent("panelactivated", this, panel);
35819         this.fireEvent("invalidated");
35820     },
35821     
35822     /**
35823      * Show the specified panel.
35824      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35825      * @return {Roo.ContentPanel} The shown panel or null
35826      */
35827     showPanel : function(panel){
35828         panel = this.getPanel(panel);
35829         if(panel){
35830             this.setActivePanel(panel);
35831         }
35832         return panel;
35833     },
35834     
35835     /**
35836      * Get the active panel for this region.
35837      * @return {Roo.ContentPanel} The active panel or null
35838      */
35839     getActivePanel : function(){
35840         return this.activePanel;
35841     },
35842     
35843     /**
35844      * Add the passed ContentPanel(s)
35845      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35846      * @return {Roo.ContentPanel} The panel added (if only one was added)
35847      */
35848     add : function(panel){
35849         if(arguments.length > 1){
35850             for(var i = 0, len = arguments.length; i < len; i++) {
35851                 this.add(arguments[i]);
35852             }
35853             return null;
35854         }
35855         if(this.hasPanel(panel)){
35856             this.showPanel(panel);
35857             return panel;
35858         }
35859         var el = panel.getEl();
35860         if(el.dom.parentNode != this.mgr.el.dom){
35861             this.mgr.el.dom.appendChild(el.dom);
35862         }
35863         if(panel.setRegion){
35864             panel.setRegion(this);
35865         }
35866         this.panels.add(panel);
35867         el.setStyle("position", "absolute");
35868         if(!panel.background){
35869             this.setActivePanel(panel);
35870             if(this.config.initialSize && this.panels.getCount()==1){
35871                 this.resizeTo(this.config.initialSize);
35872             }
35873         }
35874         this.fireEvent("paneladded", this, panel);
35875         return panel;
35876     },
35877     
35878     /**
35879      * Returns true if the panel is in this region.
35880      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35881      * @return {Boolean}
35882      */
35883     hasPanel : function(panel){
35884         if(typeof panel == "object"){ // must be panel obj
35885             panel = panel.getId();
35886         }
35887         return this.getPanel(panel) ? true : false;
35888     },
35889     
35890     /**
35891      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35892      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35893      * @param {Boolean} preservePanel Overrides the config preservePanel option
35894      * @return {Roo.ContentPanel} The panel that was removed
35895      */
35896     remove : function(panel, preservePanel){
35897         panel = this.getPanel(panel);
35898         if(!panel){
35899             return null;
35900         }
35901         var e = {};
35902         this.fireEvent("beforeremove", this, panel, e);
35903         if(e.cancel === true){
35904             return null;
35905         }
35906         var panelId = panel.getId();
35907         this.panels.removeKey(panelId);
35908         return panel;
35909     },
35910     
35911     /**
35912      * Returns the panel specified or null if it's not in this region.
35913      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35914      * @return {Roo.ContentPanel}
35915      */
35916     getPanel : function(id){
35917         if(typeof id == "object"){ // must be panel obj
35918             return id;
35919         }
35920         return this.panels.get(id);
35921     },
35922     
35923     /**
35924      * Returns this regions position (north/south/east/west/center).
35925      * @return {String} 
35926      */
35927     getPosition: function(){
35928         return this.position;    
35929     }
35930 });/*
35931  * Based on:
35932  * Ext JS Library 1.1.1
35933  * Copyright(c) 2006-2007, Ext JS, LLC.
35934  *
35935  * Originally Released Under LGPL - original licence link has changed is not relivant.
35936  *
35937  * Fork - LGPL
35938  * <script type="text/javascript">
35939  */
35940  
35941 /**
35942  * @class Roo.bootstrap.layout.Region
35943  * @extends Roo.bootstrap.layout.Basic
35944  * This class represents a region in a layout manager.
35945  
35946  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35947  * @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})
35948  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35949  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35950  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35951  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35952  * @cfg {String}    title           The title for the region (overrides panel titles)
35953  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35954  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35955  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35956  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35957  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35958  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35959  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35960  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35961  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35962  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35963
35964  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35965  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35966  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35967  * @cfg {Number}    width           For East/West panels
35968  * @cfg {Number}    height          For North/South panels
35969  * @cfg {Boolean}   split           To show the splitter
35970  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35971  * 
35972  * @cfg {string}   cls             Extra CSS classes to add to region
35973  * 
35974  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35975  * @cfg {string}   region  the region that it inhabits..
35976  *
35977
35978  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35979  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35980
35981  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35982  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35983  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35984  */
35985 Roo.bootstrap.layout.Region = function(config)
35986 {
35987     this.applyConfig(config);
35988
35989     var mgr = config.mgr;
35990     var pos = config.region;
35991     config.skipConfig = true;
35992     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35993     
35994     if (mgr.el) {
35995         this.onRender(mgr.el);   
35996     }
35997      
35998     this.visible = true;
35999     this.collapsed = false;
36000     this.unrendered_panels = [];
36001 };
36002
36003 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36004
36005     position: '', // set by wrapper (eg. north/south etc..)
36006     unrendered_panels : null,  // unrendered panels.
36007     
36008     tabPosition : false,
36009     
36010     mgr: false, // points to 'Border'
36011     
36012     
36013     createBody : function(){
36014         /** This region's body element 
36015         * @type Roo.Element */
36016         this.bodyEl = this.el.createChild({
36017                 tag: "div",
36018                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36019         });
36020     },
36021
36022     onRender: function(ctr, pos)
36023     {
36024         var dh = Roo.DomHelper;
36025         /** This region's container element 
36026         * @type Roo.Element */
36027         this.el = dh.append(ctr.dom, {
36028                 tag: "div",
36029                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36030             }, true);
36031         /** This region's title element 
36032         * @type Roo.Element */
36033     
36034         this.titleEl = dh.append(this.el.dom,  {
36035                 tag: "div",
36036                 unselectable: "on",
36037                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36038                 children:[
36039                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36040                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36041                 ]
36042             }, true);
36043         
36044         this.titleEl.enableDisplayMode();
36045         /** This region's title text element 
36046         * @type HTMLElement */
36047         this.titleTextEl = this.titleEl.dom.firstChild;
36048         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36049         /*
36050         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36051         this.closeBtn.enableDisplayMode();
36052         this.closeBtn.on("click", this.closeClicked, this);
36053         this.closeBtn.hide();
36054     */
36055         this.createBody(this.config);
36056         if(this.config.hideWhenEmpty){
36057             this.hide();
36058             this.on("paneladded", this.validateVisibility, this);
36059             this.on("panelremoved", this.validateVisibility, this);
36060         }
36061         if(this.autoScroll){
36062             this.bodyEl.setStyle("overflow", "auto");
36063         }else{
36064             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36065         }
36066         //if(c.titlebar !== false){
36067             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36068                 this.titleEl.hide();
36069             }else{
36070                 this.titleEl.show();
36071                 if(this.config.title){
36072                     this.titleTextEl.innerHTML = this.config.title;
36073                 }
36074             }
36075         //}
36076         if(this.config.collapsed){
36077             this.collapse(true);
36078         }
36079         if(this.config.hidden){
36080             this.hide();
36081         }
36082         
36083         if (this.unrendered_panels && this.unrendered_panels.length) {
36084             for (var i =0;i< this.unrendered_panels.length; i++) {
36085                 this.add(this.unrendered_panels[i]);
36086             }
36087             this.unrendered_panels = null;
36088             
36089         }
36090         
36091     },
36092     
36093     applyConfig : function(c)
36094     {
36095         /*
36096          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36097             var dh = Roo.DomHelper;
36098             if(c.titlebar !== false){
36099                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36100                 this.collapseBtn.on("click", this.collapse, this);
36101                 this.collapseBtn.enableDisplayMode();
36102                 /*
36103                 if(c.showPin === true || this.showPin){
36104                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36105                     this.stickBtn.enableDisplayMode();
36106                     this.stickBtn.on("click", this.expand, this);
36107                     this.stickBtn.hide();
36108                 }
36109                 
36110             }
36111             */
36112             /** This region's collapsed element
36113             * @type Roo.Element */
36114             /*
36115              *
36116             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36117                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36118             ]}, true);
36119             
36120             if(c.floatable !== false){
36121                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36122                this.collapsedEl.on("click", this.collapseClick, this);
36123             }
36124
36125             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36126                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36127                    id: "message", unselectable: "on", style:{"float":"left"}});
36128                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36129              }
36130             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36131             this.expandBtn.on("click", this.expand, this);
36132             
36133         }
36134         
36135         if(this.collapseBtn){
36136             this.collapseBtn.setVisible(c.collapsible == true);
36137         }
36138         
36139         this.cmargins = c.cmargins || this.cmargins ||
36140                          (this.position == "west" || this.position == "east" ?
36141                              {top: 0, left: 2, right:2, bottom: 0} :
36142                              {top: 2, left: 0, right:0, bottom: 2});
36143         */
36144         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36145         
36146         
36147         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36148         
36149         this.autoScroll = c.autoScroll || false;
36150         
36151         
36152        
36153         
36154         this.duration = c.duration || .30;
36155         this.slideDuration = c.slideDuration || .45;
36156         this.config = c;
36157        
36158     },
36159     /**
36160      * Returns true if this region is currently visible.
36161      * @return {Boolean}
36162      */
36163     isVisible : function(){
36164         return this.visible;
36165     },
36166
36167     /**
36168      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36169      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36170      */
36171     //setCollapsedTitle : function(title){
36172     //    title = title || "&#160;";
36173      //   if(this.collapsedTitleTextEl){
36174       //      this.collapsedTitleTextEl.innerHTML = title;
36175        // }
36176     //},
36177
36178     getBox : function(){
36179         var b;
36180       //  if(!this.collapsed){
36181             b = this.el.getBox(false, true);
36182        // }else{
36183           //  b = this.collapsedEl.getBox(false, true);
36184         //}
36185         return b;
36186     },
36187
36188     getMargins : function(){
36189         return this.margins;
36190         //return this.collapsed ? this.cmargins : this.margins;
36191     },
36192 /*
36193     highlight : function(){
36194         this.el.addClass("x-layout-panel-dragover");
36195     },
36196
36197     unhighlight : function(){
36198         this.el.removeClass("x-layout-panel-dragover");
36199     },
36200 */
36201     updateBox : function(box)
36202     {
36203         if (!this.bodyEl) {
36204             return; // not rendered yet..
36205         }
36206         
36207         this.box = box;
36208         if(!this.collapsed){
36209             this.el.dom.style.left = box.x + "px";
36210             this.el.dom.style.top = box.y + "px";
36211             this.updateBody(box.width, box.height);
36212         }else{
36213             this.collapsedEl.dom.style.left = box.x + "px";
36214             this.collapsedEl.dom.style.top = box.y + "px";
36215             this.collapsedEl.setSize(box.width, box.height);
36216         }
36217         if(this.tabs){
36218             this.tabs.autoSizeTabs();
36219         }
36220     },
36221
36222     updateBody : function(w, h)
36223     {
36224         if(w !== null){
36225             this.el.setWidth(w);
36226             w -= this.el.getBorderWidth("rl");
36227             if(this.config.adjustments){
36228                 w += this.config.adjustments[0];
36229             }
36230         }
36231         if(h !== null && h > 0){
36232             this.el.setHeight(h);
36233             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36234             h -= this.el.getBorderWidth("tb");
36235             if(this.config.adjustments){
36236                 h += this.config.adjustments[1];
36237             }
36238             this.bodyEl.setHeight(h);
36239             if(this.tabs){
36240                 h = this.tabs.syncHeight(h);
36241             }
36242         }
36243         if(this.panelSize){
36244             w = w !== null ? w : this.panelSize.width;
36245             h = h !== null ? h : this.panelSize.height;
36246         }
36247         if(this.activePanel){
36248             var el = this.activePanel.getEl();
36249             w = w !== null ? w : el.getWidth();
36250             h = h !== null ? h : el.getHeight();
36251             this.panelSize = {width: w, height: h};
36252             this.activePanel.setSize(w, h);
36253         }
36254         if(Roo.isIE && this.tabs){
36255             this.tabs.el.repaint();
36256         }
36257     },
36258
36259     /**
36260      * Returns the container element for this region.
36261      * @return {Roo.Element}
36262      */
36263     getEl : function(){
36264         return this.el;
36265     },
36266
36267     /**
36268      * Hides this region.
36269      */
36270     hide : function(){
36271         //if(!this.collapsed){
36272             this.el.dom.style.left = "-2000px";
36273             this.el.hide();
36274         //}else{
36275          //   this.collapsedEl.dom.style.left = "-2000px";
36276          //   this.collapsedEl.hide();
36277        // }
36278         this.visible = false;
36279         this.fireEvent("visibilitychange", this, false);
36280     },
36281
36282     /**
36283      * Shows this region if it was previously hidden.
36284      */
36285     show : function(){
36286         //if(!this.collapsed){
36287             this.el.show();
36288         //}else{
36289         //    this.collapsedEl.show();
36290        // }
36291         this.visible = true;
36292         this.fireEvent("visibilitychange", this, true);
36293     },
36294 /*
36295     closeClicked : function(){
36296         if(this.activePanel){
36297             this.remove(this.activePanel);
36298         }
36299     },
36300
36301     collapseClick : function(e){
36302         if(this.isSlid){
36303            e.stopPropagation();
36304            this.slideIn();
36305         }else{
36306            e.stopPropagation();
36307            this.slideOut();
36308         }
36309     },
36310 */
36311     /**
36312      * Collapses this region.
36313      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36314      */
36315     /*
36316     collapse : function(skipAnim, skipCheck = false){
36317         if(this.collapsed) {
36318             return;
36319         }
36320         
36321         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36322             
36323             this.collapsed = true;
36324             if(this.split){
36325                 this.split.el.hide();
36326             }
36327             if(this.config.animate && skipAnim !== true){
36328                 this.fireEvent("invalidated", this);
36329                 this.animateCollapse();
36330             }else{
36331                 this.el.setLocation(-20000,-20000);
36332                 this.el.hide();
36333                 this.collapsedEl.show();
36334                 this.fireEvent("collapsed", this);
36335                 this.fireEvent("invalidated", this);
36336             }
36337         }
36338         
36339     },
36340 */
36341     animateCollapse : function(){
36342         // overridden
36343     },
36344
36345     /**
36346      * Expands this region if it was previously collapsed.
36347      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36348      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36349      */
36350     /*
36351     expand : function(e, skipAnim){
36352         if(e) {
36353             e.stopPropagation();
36354         }
36355         if(!this.collapsed || this.el.hasActiveFx()) {
36356             return;
36357         }
36358         if(this.isSlid){
36359             this.afterSlideIn();
36360             skipAnim = true;
36361         }
36362         this.collapsed = false;
36363         if(this.config.animate && skipAnim !== true){
36364             this.animateExpand();
36365         }else{
36366             this.el.show();
36367             if(this.split){
36368                 this.split.el.show();
36369             }
36370             this.collapsedEl.setLocation(-2000,-2000);
36371             this.collapsedEl.hide();
36372             this.fireEvent("invalidated", this);
36373             this.fireEvent("expanded", this);
36374         }
36375     },
36376 */
36377     animateExpand : function(){
36378         // overridden
36379     },
36380
36381     initTabs : function()
36382     {
36383         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36384         
36385         var ts = new Roo.bootstrap.panel.Tabs({
36386             el: this.bodyEl.dom,
36387             region : this,
36388             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36389             disableTooltips: this.config.disableTabTips,
36390             toolbar : this.config.toolbar
36391         });
36392         
36393         if(this.config.hideTabs){
36394             ts.stripWrap.setDisplayed(false);
36395         }
36396         this.tabs = ts;
36397         ts.resizeTabs = this.config.resizeTabs === true;
36398         ts.minTabWidth = this.config.minTabWidth || 40;
36399         ts.maxTabWidth = this.config.maxTabWidth || 250;
36400         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36401         ts.monitorResize = false;
36402         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36403         ts.bodyEl.addClass('roo-layout-tabs-body');
36404         this.panels.each(this.initPanelAsTab, this);
36405     },
36406
36407     initPanelAsTab : function(panel){
36408         var ti = this.tabs.addTab(
36409             panel.getEl().id,
36410             panel.getTitle(),
36411             null,
36412             this.config.closeOnTab && panel.isClosable(),
36413             panel.tpl
36414         );
36415         if(panel.tabTip !== undefined){
36416             ti.setTooltip(panel.tabTip);
36417         }
36418         ti.on("activate", function(){
36419               this.setActivePanel(panel);
36420         }, this);
36421         
36422         if(this.config.closeOnTab){
36423             ti.on("beforeclose", function(t, e){
36424                 e.cancel = true;
36425                 this.remove(panel);
36426             }, this);
36427         }
36428         
36429         panel.tabItem = ti;
36430         
36431         return ti;
36432     },
36433
36434     updatePanelTitle : function(panel, title)
36435     {
36436         if(this.activePanel == panel){
36437             this.updateTitle(title);
36438         }
36439         if(this.tabs){
36440             var ti = this.tabs.getTab(panel.getEl().id);
36441             ti.setText(title);
36442             if(panel.tabTip !== undefined){
36443                 ti.setTooltip(panel.tabTip);
36444             }
36445         }
36446     },
36447
36448     updateTitle : function(title){
36449         if(this.titleTextEl && !this.config.title){
36450             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36451         }
36452     },
36453
36454     setActivePanel : function(panel)
36455     {
36456         panel = this.getPanel(panel);
36457         if(this.activePanel && this.activePanel != panel){
36458             if(this.activePanel.setActiveState(false) === false){
36459                 return;
36460             }
36461         }
36462         this.activePanel = panel;
36463         panel.setActiveState(true);
36464         if(this.panelSize){
36465             panel.setSize(this.panelSize.width, this.panelSize.height);
36466         }
36467         if(this.closeBtn){
36468             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36469         }
36470         this.updateTitle(panel.getTitle());
36471         if(this.tabs){
36472             this.fireEvent("invalidated", this);
36473         }
36474         this.fireEvent("panelactivated", this, panel);
36475     },
36476
36477     /**
36478      * Shows the specified panel.
36479      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36480      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36481      */
36482     showPanel : function(panel)
36483     {
36484         panel = this.getPanel(panel);
36485         if(panel){
36486             if(this.tabs){
36487                 var tab = this.tabs.getTab(panel.getEl().id);
36488                 if(tab.isHidden()){
36489                     this.tabs.unhideTab(tab.id);
36490                 }
36491                 tab.activate();
36492             }else{
36493                 this.setActivePanel(panel);
36494             }
36495         }
36496         return panel;
36497     },
36498
36499     /**
36500      * Get the active panel for this region.
36501      * @return {Roo.ContentPanel} The active panel or null
36502      */
36503     getActivePanel : function(){
36504         return this.activePanel;
36505     },
36506
36507     validateVisibility : function(){
36508         if(this.panels.getCount() < 1){
36509             this.updateTitle("&#160;");
36510             this.closeBtn.hide();
36511             this.hide();
36512         }else{
36513             if(!this.isVisible()){
36514                 this.show();
36515             }
36516         }
36517     },
36518
36519     /**
36520      * Adds the passed ContentPanel(s) to this region.
36521      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36522      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36523      */
36524     add : function(panel)
36525     {
36526         if(arguments.length > 1){
36527             for(var i = 0, len = arguments.length; i < len; i++) {
36528                 this.add(arguments[i]);
36529             }
36530             return null;
36531         }
36532         
36533         // if we have not been rendered yet, then we can not really do much of this..
36534         if (!this.bodyEl) {
36535             this.unrendered_panels.push(panel);
36536             return panel;
36537         }
36538         
36539         
36540         
36541         
36542         if(this.hasPanel(panel)){
36543             this.showPanel(panel);
36544             return panel;
36545         }
36546         panel.setRegion(this);
36547         this.panels.add(panel);
36548        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36549             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36550             // and hide them... ???
36551             this.bodyEl.dom.appendChild(panel.getEl().dom);
36552             if(panel.background !== true){
36553                 this.setActivePanel(panel);
36554             }
36555             this.fireEvent("paneladded", this, panel);
36556             return panel;
36557         }
36558         */
36559         if(!this.tabs){
36560             this.initTabs();
36561         }else{
36562             this.initPanelAsTab(panel);
36563         }
36564         
36565         
36566         if(panel.background !== true){
36567             this.tabs.activate(panel.getEl().id);
36568         }
36569         this.fireEvent("paneladded", this, panel);
36570         return panel;
36571     },
36572
36573     /**
36574      * Hides the tab for the specified panel.
36575      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36576      */
36577     hidePanel : function(panel){
36578         if(this.tabs && (panel = this.getPanel(panel))){
36579             this.tabs.hideTab(panel.getEl().id);
36580         }
36581     },
36582
36583     /**
36584      * Unhides the tab for a previously hidden panel.
36585      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36586      */
36587     unhidePanel : function(panel){
36588         if(this.tabs && (panel = this.getPanel(panel))){
36589             this.tabs.unhideTab(panel.getEl().id);
36590         }
36591     },
36592
36593     clearPanels : function(){
36594         while(this.panels.getCount() > 0){
36595              this.remove(this.panels.first());
36596         }
36597     },
36598
36599     /**
36600      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36601      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36602      * @param {Boolean} preservePanel Overrides the config preservePanel option
36603      * @return {Roo.ContentPanel} The panel that was removed
36604      */
36605     remove : function(panel, preservePanel)
36606     {
36607         panel = this.getPanel(panel);
36608         if(!panel){
36609             return null;
36610         }
36611         var e = {};
36612         this.fireEvent("beforeremove", this, panel, e);
36613         if(e.cancel === true){
36614             return null;
36615         }
36616         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36617         var panelId = panel.getId();
36618         this.panels.removeKey(panelId);
36619         if(preservePanel){
36620             document.body.appendChild(panel.getEl().dom);
36621         }
36622         if(this.tabs){
36623             this.tabs.removeTab(panel.getEl().id);
36624         }else if (!preservePanel){
36625             this.bodyEl.dom.removeChild(panel.getEl().dom);
36626         }
36627         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36628             var p = this.panels.first();
36629             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36630             tempEl.appendChild(p.getEl().dom);
36631             this.bodyEl.update("");
36632             this.bodyEl.dom.appendChild(p.getEl().dom);
36633             tempEl = null;
36634             this.updateTitle(p.getTitle());
36635             this.tabs = null;
36636             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36637             this.setActivePanel(p);
36638         }
36639         panel.setRegion(null);
36640         if(this.activePanel == panel){
36641             this.activePanel = null;
36642         }
36643         if(this.config.autoDestroy !== false && preservePanel !== true){
36644             try{panel.destroy();}catch(e){}
36645         }
36646         this.fireEvent("panelremoved", this, panel);
36647         return panel;
36648     },
36649
36650     /**
36651      * Returns the TabPanel component used by this region
36652      * @return {Roo.TabPanel}
36653      */
36654     getTabs : function(){
36655         return this.tabs;
36656     },
36657
36658     createTool : function(parentEl, className){
36659         var btn = Roo.DomHelper.append(parentEl, {
36660             tag: "div",
36661             cls: "x-layout-tools-button",
36662             children: [ {
36663                 tag: "div",
36664                 cls: "roo-layout-tools-button-inner " + className,
36665                 html: "&#160;"
36666             }]
36667         }, true);
36668         btn.addClassOnOver("roo-layout-tools-button-over");
36669         return btn;
36670     }
36671 });/*
36672  * Based on:
36673  * Ext JS Library 1.1.1
36674  * Copyright(c) 2006-2007, Ext JS, LLC.
36675  *
36676  * Originally Released Under LGPL - original licence link has changed is not relivant.
36677  *
36678  * Fork - LGPL
36679  * <script type="text/javascript">
36680  */
36681  
36682
36683
36684 /**
36685  * @class Roo.SplitLayoutRegion
36686  * @extends Roo.LayoutRegion
36687  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36688  */
36689 Roo.bootstrap.layout.Split = function(config){
36690     this.cursor = config.cursor;
36691     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36692 };
36693
36694 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36695 {
36696     splitTip : "Drag to resize.",
36697     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36698     useSplitTips : false,
36699
36700     applyConfig : function(config){
36701         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36702     },
36703     
36704     onRender : function(ctr,pos) {
36705         
36706         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36707         if(!this.config.split){
36708             return;
36709         }
36710         if(!this.split){
36711             
36712             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36713                             tag: "div",
36714                             id: this.el.id + "-split",
36715                             cls: "roo-layout-split roo-layout-split-"+this.position,
36716                             html: "&#160;"
36717             });
36718             /** The SplitBar for this region 
36719             * @type Roo.SplitBar */
36720             // does not exist yet...
36721             Roo.log([this.position, this.orientation]);
36722             
36723             this.split = new Roo.bootstrap.SplitBar({
36724                 dragElement : splitEl,
36725                 resizingElement: this.el,
36726                 orientation : this.orientation
36727             });
36728             
36729             this.split.on("moved", this.onSplitMove, this);
36730             this.split.useShim = this.config.useShim === true;
36731             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36732             if(this.useSplitTips){
36733                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36734             }
36735             //if(config.collapsible){
36736             //    this.split.el.on("dblclick", this.collapse,  this);
36737             //}
36738         }
36739         if(typeof this.config.minSize != "undefined"){
36740             this.split.minSize = this.config.minSize;
36741         }
36742         if(typeof this.config.maxSize != "undefined"){
36743             this.split.maxSize = this.config.maxSize;
36744         }
36745         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36746             this.hideSplitter();
36747         }
36748         
36749     },
36750
36751     getHMaxSize : function(){
36752          var cmax = this.config.maxSize || 10000;
36753          var center = this.mgr.getRegion("center");
36754          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36755     },
36756
36757     getVMaxSize : function(){
36758          var cmax = this.config.maxSize || 10000;
36759          var center = this.mgr.getRegion("center");
36760          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36761     },
36762
36763     onSplitMove : function(split, newSize){
36764         this.fireEvent("resized", this, newSize);
36765     },
36766     
36767     /** 
36768      * Returns the {@link Roo.SplitBar} for this region.
36769      * @return {Roo.SplitBar}
36770      */
36771     getSplitBar : function(){
36772         return this.split;
36773     },
36774     
36775     hide : function(){
36776         this.hideSplitter();
36777         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36778     },
36779
36780     hideSplitter : function(){
36781         if(this.split){
36782             this.split.el.setLocation(-2000,-2000);
36783             this.split.el.hide();
36784         }
36785     },
36786
36787     show : function(){
36788         if(this.split){
36789             this.split.el.show();
36790         }
36791         Roo.bootstrap.layout.Split.superclass.show.call(this);
36792     },
36793     
36794     beforeSlide: function(){
36795         if(Roo.isGecko){// firefox overflow auto bug workaround
36796             this.bodyEl.clip();
36797             if(this.tabs) {
36798                 this.tabs.bodyEl.clip();
36799             }
36800             if(this.activePanel){
36801                 this.activePanel.getEl().clip();
36802                 
36803                 if(this.activePanel.beforeSlide){
36804                     this.activePanel.beforeSlide();
36805                 }
36806             }
36807         }
36808     },
36809     
36810     afterSlide : function(){
36811         if(Roo.isGecko){// firefox overflow auto bug workaround
36812             this.bodyEl.unclip();
36813             if(this.tabs) {
36814                 this.tabs.bodyEl.unclip();
36815             }
36816             if(this.activePanel){
36817                 this.activePanel.getEl().unclip();
36818                 if(this.activePanel.afterSlide){
36819                     this.activePanel.afterSlide();
36820                 }
36821             }
36822         }
36823     },
36824
36825     initAutoHide : function(){
36826         if(this.autoHide !== false){
36827             if(!this.autoHideHd){
36828                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36829                 this.autoHideHd = {
36830                     "mouseout": function(e){
36831                         if(!e.within(this.el, true)){
36832                             st.delay(500);
36833                         }
36834                     },
36835                     "mouseover" : function(e){
36836                         st.cancel();
36837                     },
36838                     scope : this
36839                 };
36840             }
36841             this.el.on(this.autoHideHd);
36842         }
36843     },
36844
36845     clearAutoHide : function(){
36846         if(this.autoHide !== false){
36847             this.el.un("mouseout", this.autoHideHd.mouseout);
36848             this.el.un("mouseover", this.autoHideHd.mouseover);
36849         }
36850     },
36851
36852     clearMonitor : function(){
36853         Roo.get(document).un("click", this.slideInIf, this);
36854     },
36855
36856     // these names are backwards but not changed for compat
36857     slideOut : function(){
36858         if(this.isSlid || this.el.hasActiveFx()){
36859             return;
36860         }
36861         this.isSlid = true;
36862         if(this.collapseBtn){
36863             this.collapseBtn.hide();
36864         }
36865         this.closeBtnState = this.closeBtn.getStyle('display');
36866         this.closeBtn.hide();
36867         if(this.stickBtn){
36868             this.stickBtn.show();
36869         }
36870         this.el.show();
36871         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36872         this.beforeSlide();
36873         this.el.setStyle("z-index", 10001);
36874         this.el.slideIn(this.getSlideAnchor(), {
36875             callback: function(){
36876                 this.afterSlide();
36877                 this.initAutoHide();
36878                 Roo.get(document).on("click", this.slideInIf, this);
36879                 this.fireEvent("slideshow", this);
36880             },
36881             scope: this,
36882             block: true
36883         });
36884     },
36885
36886     afterSlideIn : function(){
36887         this.clearAutoHide();
36888         this.isSlid = false;
36889         this.clearMonitor();
36890         this.el.setStyle("z-index", "");
36891         if(this.collapseBtn){
36892             this.collapseBtn.show();
36893         }
36894         this.closeBtn.setStyle('display', this.closeBtnState);
36895         if(this.stickBtn){
36896             this.stickBtn.hide();
36897         }
36898         this.fireEvent("slidehide", this);
36899     },
36900
36901     slideIn : function(cb){
36902         if(!this.isSlid || this.el.hasActiveFx()){
36903             Roo.callback(cb);
36904             return;
36905         }
36906         this.isSlid = false;
36907         this.beforeSlide();
36908         this.el.slideOut(this.getSlideAnchor(), {
36909             callback: function(){
36910                 this.el.setLeftTop(-10000, -10000);
36911                 this.afterSlide();
36912                 this.afterSlideIn();
36913                 Roo.callback(cb);
36914             },
36915             scope: this,
36916             block: true
36917         });
36918     },
36919     
36920     slideInIf : function(e){
36921         if(!e.within(this.el)){
36922             this.slideIn();
36923         }
36924     },
36925
36926     animateCollapse : function(){
36927         this.beforeSlide();
36928         this.el.setStyle("z-index", 20000);
36929         var anchor = this.getSlideAnchor();
36930         this.el.slideOut(anchor, {
36931             callback : function(){
36932                 this.el.setStyle("z-index", "");
36933                 this.collapsedEl.slideIn(anchor, {duration:.3});
36934                 this.afterSlide();
36935                 this.el.setLocation(-10000,-10000);
36936                 this.el.hide();
36937                 this.fireEvent("collapsed", this);
36938             },
36939             scope: this,
36940             block: true
36941         });
36942     },
36943
36944     animateExpand : function(){
36945         this.beforeSlide();
36946         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36947         this.el.setStyle("z-index", 20000);
36948         this.collapsedEl.hide({
36949             duration:.1
36950         });
36951         this.el.slideIn(this.getSlideAnchor(), {
36952             callback : function(){
36953                 this.el.setStyle("z-index", "");
36954                 this.afterSlide();
36955                 if(this.split){
36956                     this.split.el.show();
36957                 }
36958                 this.fireEvent("invalidated", this);
36959                 this.fireEvent("expanded", this);
36960             },
36961             scope: this,
36962             block: true
36963         });
36964     },
36965
36966     anchors : {
36967         "west" : "left",
36968         "east" : "right",
36969         "north" : "top",
36970         "south" : "bottom"
36971     },
36972
36973     sanchors : {
36974         "west" : "l",
36975         "east" : "r",
36976         "north" : "t",
36977         "south" : "b"
36978     },
36979
36980     canchors : {
36981         "west" : "tl-tr",
36982         "east" : "tr-tl",
36983         "north" : "tl-bl",
36984         "south" : "bl-tl"
36985     },
36986
36987     getAnchor : function(){
36988         return this.anchors[this.position];
36989     },
36990
36991     getCollapseAnchor : function(){
36992         return this.canchors[this.position];
36993     },
36994
36995     getSlideAnchor : function(){
36996         return this.sanchors[this.position];
36997     },
36998
36999     getAlignAdj : function(){
37000         var cm = this.cmargins;
37001         switch(this.position){
37002             case "west":
37003                 return [0, 0];
37004             break;
37005             case "east":
37006                 return [0, 0];
37007             break;
37008             case "north":
37009                 return [0, 0];
37010             break;
37011             case "south":
37012                 return [0, 0];
37013             break;
37014         }
37015     },
37016
37017     getExpandAdj : function(){
37018         var c = this.collapsedEl, cm = this.cmargins;
37019         switch(this.position){
37020             case "west":
37021                 return [-(cm.right+c.getWidth()+cm.left), 0];
37022             break;
37023             case "east":
37024                 return [cm.right+c.getWidth()+cm.left, 0];
37025             break;
37026             case "north":
37027                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37028             break;
37029             case "south":
37030                 return [0, cm.top+cm.bottom+c.getHeight()];
37031             break;
37032         }
37033     }
37034 });/*
37035  * Based on:
37036  * Ext JS Library 1.1.1
37037  * Copyright(c) 2006-2007, Ext JS, LLC.
37038  *
37039  * Originally Released Under LGPL - original licence link has changed is not relivant.
37040  *
37041  * Fork - LGPL
37042  * <script type="text/javascript">
37043  */
37044 /*
37045  * These classes are private internal classes
37046  */
37047 Roo.bootstrap.layout.Center = function(config){
37048     config.region = "center";
37049     Roo.bootstrap.layout.Region.call(this, config);
37050     this.visible = true;
37051     this.minWidth = config.minWidth || 20;
37052     this.minHeight = config.minHeight || 20;
37053 };
37054
37055 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37056     hide : function(){
37057         // center panel can't be hidden
37058     },
37059     
37060     show : function(){
37061         // center panel can't be hidden
37062     },
37063     
37064     getMinWidth: function(){
37065         return this.minWidth;
37066     },
37067     
37068     getMinHeight: function(){
37069         return this.minHeight;
37070     }
37071 });
37072
37073
37074
37075
37076  
37077
37078
37079
37080
37081
37082
37083 Roo.bootstrap.layout.North = function(config)
37084 {
37085     config.region = 'north';
37086     config.cursor = 'n-resize';
37087     
37088     Roo.bootstrap.layout.Split.call(this, config);
37089     
37090     
37091     if(this.split){
37092         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37093         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37094         this.split.el.addClass("roo-layout-split-v");
37095     }
37096     var size = config.initialSize || config.height;
37097     if(typeof size != "undefined"){
37098         this.el.setHeight(size);
37099     }
37100 };
37101 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37102 {
37103     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37104     
37105     
37106     
37107     getBox : function(){
37108         if(this.collapsed){
37109             return this.collapsedEl.getBox();
37110         }
37111         var box = this.el.getBox();
37112         if(this.split){
37113             box.height += this.split.el.getHeight();
37114         }
37115         return box;
37116     },
37117     
37118     updateBox : function(box){
37119         if(this.split && !this.collapsed){
37120             box.height -= this.split.el.getHeight();
37121             this.split.el.setLeft(box.x);
37122             this.split.el.setTop(box.y+box.height);
37123             this.split.el.setWidth(box.width);
37124         }
37125         if(this.collapsed){
37126             this.updateBody(box.width, null);
37127         }
37128         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37129     }
37130 });
37131
37132
37133
37134
37135
37136 Roo.bootstrap.layout.South = function(config){
37137     config.region = 'south';
37138     config.cursor = 's-resize';
37139     Roo.bootstrap.layout.Split.call(this, config);
37140     if(this.split){
37141         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37142         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37143         this.split.el.addClass("roo-layout-split-v");
37144     }
37145     var size = config.initialSize || config.height;
37146     if(typeof size != "undefined"){
37147         this.el.setHeight(size);
37148     }
37149 };
37150
37151 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37152     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37153     getBox : function(){
37154         if(this.collapsed){
37155             return this.collapsedEl.getBox();
37156         }
37157         var box = this.el.getBox();
37158         if(this.split){
37159             var sh = this.split.el.getHeight();
37160             box.height += sh;
37161             box.y -= sh;
37162         }
37163         return box;
37164     },
37165     
37166     updateBox : function(box){
37167         if(this.split && !this.collapsed){
37168             var sh = this.split.el.getHeight();
37169             box.height -= sh;
37170             box.y += sh;
37171             this.split.el.setLeft(box.x);
37172             this.split.el.setTop(box.y-sh);
37173             this.split.el.setWidth(box.width);
37174         }
37175         if(this.collapsed){
37176             this.updateBody(box.width, null);
37177         }
37178         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37179     }
37180 });
37181
37182 Roo.bootstrap.layout.East = function(config){
37183     config.region = "east";
37184     config.cursor = "e-resize";
37185     Roo.bootstrap.layout.Split.call(this, config);
37186     if(this.split){
37187         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37188         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37189         this.split.el.addClass("roo-layout-split-h");
37190     }
37191     var size = config.initialSize || config.width;
37192     if(typeof size != "undefined"){
37193         this.el.setWidth(size);
37194     }
37195 };
37196 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37197     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37198     getBox : function(){
37199         if(this.collapsed){
37200             return this.collapsedEl.getBox();
37201         }
37202         var box = this.el.getBox();
37203         if(this.split){
37204             var sw = this.split.el.getWidth();
37205             box.width += sw;
37206             box.x -= sw;
37207         }
37208         return box;
37209     },
37210
37211     updateBox : function(box){
37212         if(this.split && !this.collapsed){
37213             var sw = this.split.el.getWidth();
37214             box.width -= sw;
37215             this.split.el.setLeft(box.x);
37216             this.split.el.setTop(box.y);
37217             this.split.el.setHeight(box.height);
37218             box.x += sw;
37219         }
37220         if(this.collapsed){
37221             this.updateBody(null, box.height);
37222         }
37223         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37224     }
37225 });
37226
37227 Roo.bootstrap.layout.West = function(config){
37228     config.region = "west";
37229     config.cursor = "w-resize";
37230     
37231     Roo.bootstrap.layout.Split.call(this, config);
37232     if(this.split){
37233         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37234         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37235         this.split.el.addClass("roo-layout-split-h");
37236     }
37237     
37238 };
37239 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37240     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37241     
37242     onRender: function(ctr, pos)
37243     {
37244         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37245         var size = this.config.initialSize || this.config.width;
37246         if(typeof size != "undefined"){
37247             this.el.setWidth(size);
37248         }
37249     },
37250     
37251     getBox : function(){
37252         if(this.collapsed){
37253             return this.collapsedEl.getBox();
37254         }
37255         var box = this.el.getBox();
37256         if(this.split){
37257             box.width += this.split.el.getWidth();
37258         }
37259         return box;
37260     },
37261     
37262     updateBox : function(box){
37263         if(this.split && !this.collapsed){
37264             var sw = this.split.el.getWidth();
37265             box.width -= sw;
37266             this.split.el.setLeft(box.x+box.width);
37267             this.split.el.setTop(box.y);
37268             this.split.el.setHeight(box.height);
37269         }
37270         if(this.collapsed){
37271             this.updateBody(null, box.height);
37272         }
37273         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37274     }
37275 });Roo.namespace("Roo.bootstrap.panel");/*
37276  * Based on:
37277  * Ext JS Library 1.1.1
37278  * Copyright(c) 2006-2007, Ext JS, LLC.
37279  *
37280  * Originally Released Under LGPL - original licence link has changed is not relivant.
37281  *
37282  * Fork - LGPL
37283  * <script type="text/javascript">
37284  */
37285 /**
37286  * @class Roo.ContentPanel
37287  * @extends Roo.util.Observable
37288  * A basic ContentPanel element.
37289  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37290  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37291  * @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
37292  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37293  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37294  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37295  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37296  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37297  * @cfg {String} title          The title for this panel
37298  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37299  * @cfg {String} url            Calls {@link #setUrl} with this value
37300  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37301  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37302  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37303  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37304  * @cfg {Boolean} badges render the badges
37305
37306  * @constructor
37307  * Create a new ContentPanel.
37308  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37309  * @param {String/Object} config A string to set only the title or a config object
37310  * @param {String} content (optional) Set the HTML content for this panel
37311  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37312  */
37313 Roo.bootstrap.panel.Content = function( config){
37314     
37315     this.tpl = config.tpl || false;
37316     
37317     var el = config.el;
37318     var content = config.content;
37319
37320     if(config.autoCreate){ // xtype is available if this is called from factory
37321         el = Roo.id();
37322     }
37323     this.el = Roo.get(el);
37324     if(!this.el && config && config.autoCreate){
37325         if(typeof config.autoCreate == "object"){
37326             if(!config.autoCreate.id){
37327                 config.autoCreate.id = config.id||el;
37328             }
37329             this.el = Roo.DomHelper.append(document.body,
37330                         config.autoCreate, true);
37331         }else{
37332             var elcfg =  {   tag: "div",
37333                             cls: "roo-layout-inactive-content",
37334                             id: config.id||el
37335                             };
37336             if (config.html) {
37337                 elcfg.html = config.html;
37338                 
37339             }
37340                         
37341             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37342         }
37343     } 
37344     this.closable = false;
37345     this.loaded = false;
37346     this.active = false;
37347    
37348       
37349     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37350         
37351         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37352         
37353         this.wrapEl = this.el; //this.el.wrap();
37354         var ti = [];
37355         if (config.toolbar.items) {
37356             ti = config.toolbar.items ;
37357             delete config.toolbar.items ;
37358         }
37359         
37360         var nitems = [];
37361         this.toolbar.render(this.wrapEl, 'before');
37362         for(var i =0;i < ti.length;i++) {
37363           //  Roo.log(['add child', items[i]]);
37364             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37365         }
37366         this.toolbar.items = nitems;
37367         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37368         delete config.toolbar;
37369         
37370     }
37371     /*
37372     // xtype created footer. - not sure if will work as we normally have to render first..
37373     if (this.footer && !this.footer.el && this.footer.xtype) {
37374         if (!this.wrapEl) {
37375             this.wrapEl = this.el.wrap();
37376         }
37377     
37378         this.footer.container = this.wrapEl.createChild();
37379          
37380         this.footer = Roo.factory(this.footer, Roo);
37381         
37382     }
37383     */
37384     
37385      if(typeof config == "string"){
37386         this.title = config;
37387     }else{
37388         Roo.apply(this, config);
37389     }
37390     
37391     if(this.resizeEl){
37392         this.resizeEl = Roo.get(this.resizeEl, true);
37393     }else{
37394         this.resizeEl = this.el;
37395     }
37396     // handle view.xtype
37397     
37398  
37399     
37400     
37401     this.addEvents({
37402         /**
37403          * @event activate
37404          * Fires when this panel is activated. 
37405          * @param {Roo.ContentPanel} this
37406          */
37407         "activate" : true,
37408         /**
37409          * @event deactivate
37410          * Fires when this panel is activated. 
37411          * @param {Roo.ContentPanel} this
37412          */
37413         "deactivate" : true,
37414
37415         /**
37416          * @event resize
37417          * Fires when this panel is resized if fitToFrame is true.
37418          * @param {Roo.ContentPanel} this
37419          * @param {Number} width The width after any component adjustments
37420          * @param {Number} height The height after any component adjustments
37421          */
37422         "resize" : true,
37423         
37424          /**
37425          * @event render
37426          * Fires when this tab is created
37427          * @param {Roo.ContentPanel} this
37428          */
37429         "render" : true
37430         
37431         
37432         
37433     });
37434     
37435
37436     
37437     
37438     if(this.autoScroll){
37439         this.resizeEl.setStyle("overflow", "auto");
37440     } else {
37441         // fix randome scrolling
37442         //this.el.on('scroll', function() {
37443         //    Roo.log('fix random scolling');
37444         //    this.scrollTo('top',0); 
37445         //});
37446     }
37447     content = content || this.content;
37448     if(content){
37449         this.setContent(content);
37450     }
37451     if(config && config.url){
37452         this.setUrl(this.url, this.params, this.loadOnce);
37453     }
37454     
37455     
37456     
37457     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37458     
37459     if (this.view && typeof(this.view.xtype) != 'undefined') {
37460         this.view.el = this.el.appendChild(document.createElement("div"));
37461         this.view = Roo.factory(this.view); 
37462         this.view.render  &&  this.view.render(false, '');  
37463     }
37464     
37465     
37466     this.fireEvent('render', this);
37467 };
37468
37469 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37470     
37471     tabTip : '',
37472     
37473     setRegion : function(region){
37474         this.region = region;
37475         this.setActiveClass(region && !this.background);
37476     },
37477     
37478     
37479     setActiveClass: function(state)
37480     {
37481         if(state){
37482            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37483            this.el.setStyle('position','relative');
37484         }else{
37485            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37486            this.el.setStyle('position', 'absolute');
37487         } 
37488     },
37489     
37490     /**
37491      * Returns the toolbar for this Panel if one was configured. 
37492      * @return {Roo.Toolbar} 
37493      */
37494     getToolbar : function(){
37495         return this.toolbar;
37496     },
37497     
37498     setActiveState : function(active)
37499     {
37500         this.active = active;
37501         this.setActiveClass(active);
37502         if(!active){
37503             if(this.fireEvent("deactivate", this) === false){
37504                 return false;
37505             }
37506             return true;
37507         }
37508         this.fireEvent("activate", this);
37509         return true;
37510     },
37511     /**
37512      * Updates this panel's element
37513      * @param {String} content The new content
37514      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37515     */
37516     setContent : function(content, loadScripts){
37517         this.el.update(content, loadScripts);
37518     },
37519
37520     ignoreResize : function(w, h){
37521         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37522             return true;
37523         }else{
37524             this.lastSize = {width: w, height: h};
37525             return false;
37526         }
37527     },
37528     /**
37529      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37530      * @return {Roo.UpdateManager} The UpdateManager
37531      */
37532     getUpdateManager : function(){
37533         return this.el.getUpdateManager();
37534     },
37535      /**
37536      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37537      * @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:
37538 <pre><code>
37539 panel.load({
37540     url: "your-url.php",
37541     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37542     callback: yourFunction,
37543     scope: yourObject, //(optional scope)
37544     discardUrl: false,
37545     nocache: false,
37546     text: "Loading...",
37547     timeout: 30,
37548     scripts: false
37549 });
37550 </code></pre>
37551      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37552      * 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.
37553      * @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}
37554      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37555      * @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.
37556      * @return {Roo.ContentPanel} this
37557      */
37558     load : function(){
37559         var um = this.el.getUpdateManager();
37560         um.update.apply(um, arguments);
37561         return this;
37562     },
37563
37564
37565     /**
37566      * 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.
37567      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37568      * @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)
37569      * @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)
37570      * @return {Roo.UpdateManager} The UpdateManager
37571      */
37572     setUrl : function(url, params, loadOnce){
37573         if(this.refreshDelegate){
37574             this.removeListener("activate", this.refreshDelegate);
37575         }
37576         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37577         this.on("activate", this.refreshDelegate);
37578         return this.el.getUpdateManager();
37579     },
37580     
37581     _handleRefresh : function(url, params, loadOnce){
37582         if(!loadOnce || !this.loaded){
37583             var updater = this.el.getUpdateManager();
37584             updater.update(url, params, this._setLoaded.createDelegate(this));
37585         }
37586     },
37587     
37588     _setLoaded : function(){
37589         this.loaded = true;
37590     }, 
37591     
37592     /**
37593      * Returns this panel's id
37594      * @return {String} 
37595      */
37596     getId : function(){
37597         return this.el.id;
37598     },
37599     
37600     /** 
37601      * Returns this panel's element - used by regiosn to add.
37602      * @return {Roo.Element} 
37603      */
37604     getEl : function(){
37605         return this.wrapEl || this.el;
37606     },
37607     
37608    
37609     
37610     adjustForComponents : function(width, height)
37611     {
37612         //Roo.log('adjustForComponents ');
37613         if(this.resizeEl != this.el){
37614             width -= this.el.getFrameWidth('lr');
37615             height -= this.el.getFrameWidth('tb');
37616         }
37617         if(this.toolbar){
37618             var te = this.toolbar.getEl();
37619             te.setWidth(width);
37620             height -= te.getHeight();
37621         }
37622         if(this.footer){
37623             var te = this.footer.getEl();
37624             te.setWidth(width);
37625             height -= te.getHeight();
37626         }
37627         
37628         
37629         if(this.adjustments){
37630             width += this.adjustments[0];
37631             height += this.adjustments[1];
37632         }
37633         return {"width": width, "height": height};
37634     },
37635     
37636     setSize : function(width, height){
37637         if(this.fitToFrame && !this.ignoreResize(width, height)){
37638             if(this.fitContainer && this.resizeEl != this.el){
37639                 this.el.setSize(width, height);
37640             }
37641             var size = this.adjustForComponents(width, height);
37642             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37643             this.fireEvent('resize', this, size.width, size.height);
37644         }
37645     },
37646     
37647     /**
37648      * Returns this panel's title
37649      * @return {String} 
37650      */
37651     getTitle : function(){
37652         
37653         if (typeof(this.title) != 'object') {
37654             return this.title;
37655         }
37656         
37657         var t = '';
37658         for (var k in this.title) {
37659             if (!this.title.hasOwnProperty(k)) {
37660                 continue;
37661             }
37662             
37663             if (k.indexOf('-') >= 0) {
37664                 var s = k.split('-');
37665                 for (var i = 0; i<s.length; i++) {
37666                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37667                 }
37668             } else {
37669                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37670             }
37671         }
37672         return t;
37673     },
37674     
37675     /**
37676      * Set this panel's title
37677      * @param {String} title
37678      */
37679     setTitle : function(title){
37680         this.title = title;
37681         if(this.region){
37682             this.region.updatePanelTitle(this, title);
37683         }
37684     },
37685     
37686     /**
37687      * Returns true is this panel was configured to be closable
37688      * @return {Boolean} 
37689      */
37690     isClosable : function(){
37691         return this.closable;
37692     },
37693     
37694     beforeSlide : function(){
37695         this.el.clip();
37696         this.resizeEl.clip();
37697     },
37698     
37699     afterSlide : function(){
37700         this.el.unclip();
37701         this.resizeEl.unclip();
37702     },
37703     
37704     /**
37705      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37706      *   Will fail silently if the {@link #setUrl} method has not been called.
37707      *   This does not activate the panel, just updates its content.
37708      */
37709     refresh : function(){
37710         if(this.refreshDelegate){
37711            this.loaded = false;
37712            this.refreshDelegate();
37713         }
37714     },
37715     
37716     /**
37717      * Destroys this panel
37718      */
37719     destroy : function(){
37720         this.el.removeAllListeners();
37721         var tempEl = document.createElement("span");
37722         tempEl.appendChild(this.el.dom);
37723         tempEl.innerHTML = "";
37724         this.el.remove();
37725         this.el = null;
37726     },
37727     
37728     /**
37729      * form - if the content panel contains a form - this is a reference to it.
37730      * @type {Roo.form.Form}
37731      */
37732     form : false,
37733     /**
37734      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37735      *    This contains a reference to it.
37736      * @type {Roo.View}
37737      */
37738     view : false,
37739     
37740       /**
37741      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37742      * <pre><code>
37743
37744 layout.addxtype({
37745        xtype : 'Form',
37746        items: [ .... ]
37747    }
37748 );
37749
37750 </code></pre>
37751      * @param {Object} cfg Xtype definition of item to add.
37752      */
37753     
37754     
37755     getChildContainer: function () {
37756         return this.getEl();
37757     }
37758     
37759     
37760     /*
37761         var  ret = new Roo.factory(cfg);
37762         return ret;
37763         
37764         
37765         // add form..
37766         if (cfg.xtype.match(/^Form$/)) {
37767             
37768             var el;
37769             //if (this.footer) {
37770             //    el = this.footer.container.insertSibling(false, 'before');
37771             //} else {
37772                 el = this.el.createChild();
37773             //}
37774
37775             this.form = new  Roo.form.Form(cfg);
37776             
37777             
37778             if ( this.form.allItems.length) {
37779                 this.form.render(el.dom);
37780             }
37781             return this.form;
37782         }
37783         // should only have one of theses..
37784         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37785             // views.. should not be just added - used named prop 'view''
37786             
37787             cfg.el = this.el.appendChild(document.createElement("div"));
37788             // factory?
37789             
37790             var ret = new Roo.factory(cfg);
37791              
37792              ret.render && ret.render(false, ''); // render blank..
37793             this.view = ret;
37794             return ret;
37795         }
37796         return false;
37797     }
37798     \*/
37799 });
37800  
37801 /**
37802  * @class Roo.bootstrap.panel.Grid
37803  * @extends Roo.bootstrap.panel.Content
37804  * @constructor
37805  * Create a new GridPanel.
37806  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37807  * @param {Object} config A the config object
37808   
37809  */
37810
37811
37812
37813 Roo.bootstrap.panel.Grid = function(config)
37814 {
37815     
37816       
37817     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37818         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37819
37820     config.el = this.wrapper;
37821     //this.el = this.wrapper;
37822     
37823       if (config.container) {
37824         // ctor'ed from a Border/panel.grid
37825         
37826         
37827         this.wrapper.setStyle("overflow", "hidden");
37828         this.wrapper.addClass('roo-grid-container');
37829
37830     }
37831     
37832     
37833     if(config.toolbar){
37834         var tool_el = this.wrapper.createChild();    
37835         this.toolbar = Roo.factory(config.toolbar);
37836         var ti = [];
37837         if (config.toolbar.items) {
37838             ti = config.toolbar.items ;
37839             delete config.toolbar.items ;
37840         }
37841         
37842         var nitems = [];
37843         this.toolbar.render(tool_el);
37844         for(var i =0;i < ti.length;i++) {
37845           //  Roo.log(['add child', items[i]]);
37846             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37847         }
37848         this.toolbar.items = nitems;
37849         
37850         delete config.toolbar;
37851     }
37852     
37853     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37854     config.grid.scrollBody = true;;
37855     config.grid.monitorWindowResize = false; // turn off autosizing
37856     config.grid.autoHeight = false;
37857     config.grid.autoWidth = false;
37858     
37859     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37860     
37861     if (config.background) {
37862         // render grid on panel activation (if panel background)
37863         this.on('activate', function(gp) {
37864             if (!gp.grid.rendered) {
37865                 gp.grid.render(this.wrapper);
37866                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37867             }
37868         });
37869             
37870     } else {
37871         this.grid.render(this.wrapper);
37872         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37873
37874     }
37875     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37876     // ??? needed ??? config.el = this.wrapper;
37877     
37878     
37879     
37880   
37881     // xtype created footer. - not sure if will work as we normally have to render first..
37882     if (this.footer && !this.footer.el && this.footer.xtype) {
37883         
37884         var ctr = this.grid.getView().getFooterPanel(true);
37885         this.footer.dataSource = this.grid.dataSource;
37886         this.footer = Roo.factory(this.footer, Roo);
37887         this.footer.render(ctr);
37888         
37889     }
37890     
37891     
37892     
37893     
37894      
37895 };
37896
37897 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37898     getId : function(){
37899         return this.grid.id;
37900     },
37901     
37902     /**
37903      * Returns the grid for this panel
37904      * @return {Roo.bootstrap.Table} 
37905      */
37906     getGrid : function(){
37907         return this.grid;    
37908     },
37909     
37910     setSize : function(width, height){
37911         if(!this.ignoreResize(width, height)){
37912             var grid = this.grid;
37913             var size = this.adjustForComponents(width, height);
37914             var gridel = grid.getGridEl();
37915             gridel.setSize(size.width, size.height);
37916             /*
37917             var thd = grid.getGridEl().select('thead',true).first();
37918             var tbd = grid.getGridEl().select('tbody', true).first();
37919             if (tbd) {
37920                 tbd.setSize(width, height - thd.getHeight());
37921             }
37922             */
37923             grid.autoSize();
37924         }
37925     },
37926      
37927     
37928     
37929     beforeSlide : function(){
37930         this.grid.getView().scroller.clip();
37931     },
37932     
37933     afterSlide : function(){
37934         this.grid.getView().scroller.unclip();
37935     },
37936     
37937     destroy : function(){
37938         this.grid.destroy();
37939         delete this.grid;
37940         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37941     }
37942 });
37943
37944 /**
37945  * @class Roo.bootstrap.panel.Nest
37946  * @extends Roo.bootstrap.panel.Content
37947  * @constructor
37948  * Create a new Panel, that can contain a layout.Border.
37949  * 
37950  * 
37951  * @param {Roo.BorderLayout} layout The layout for this panel
37952  * @param {String/Object} config A string to set only the title or a config object
37953  */
37954 Roo.bootstrap.panel.Nest = function(config)
37955 {
37956     // construct with only one argument..
37957     /* FIXME - implement nicer consturctors
37958     if (layout.layout) {
37959         config = layout;
37960         layout = config.layout;
37961         delete config.layout;
37962     }
37963     if (layout.xtype && !layout.getEl) {
37964         // then layout needs constructing..
37965         layout = Roo.factory(layout, Roo);
37966     }
37967     */
37968     
37969     config.el =  config.layout.getEl();
37970     
37971     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37972     
37973     config.layout.monitorWindowResize = false; // turn off autosizing
37974     this.layout = config.layout;
37975     this.layout.getEl().addClass("roo-layout-nested-layout");
37976     this.layout.parent = this;
37977     
37978     
37979     
37980     
37981 };
37982
37983 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37984
37985     setSize : function(width, height){
37986         if(!this.ignoreResize(width, height)){
37987             var size = this.adjustForComponents(width, height);
37988             var el = this.layout.getEl();
37989             if (size.height < 1) {
37990                 el.setWidth(size.width);   
37991             } else {
37992                 el.setSize(size.width, size.height);
37993             }
37994             var touch = el.dom.offsetWidth;
37995             this.layout.layout();
37996             // ie requires a double layout on the first pass
37997             if(Roo.isIE && !this.initialized){
37998                 this.initialized = true;
37999                 this.layout.layout();
38000             }
38001         }
38002     },
38003     
38004     // activate all subpanels if not currently active..
38005     
38006     setActiveState : function(active){
38007         this.active = active;
38008         this.setActiveClass(active);
38009         
38010         if(!active){
38011             this.fireEvent("deactivate", this);
38012             return;
38013         }
38014         
38015         this.fireEvent("activate", this);
38016         // not sure if this should happen before or after..
38017         if (!this.layout) {
38018             return; // should not happen..
38019         }
38020         var reg = false;
38021         for (var r in this.layout.regions) {
38022             reg = this.layout.getRegion(r);
38023             if (reg.getActivePanel()) {
38024                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38025                 reg.setActivePanel(reg.getActivePanel());
38026                 continue;
38027             }
38028             if (!reg.panels.length) {
38029                 continue;
38030             }
38031             reg.showPanel(reg.getPanel(0));
38032         }
38033         
38034         
38035         
38036         
38037     },
38038     
38039     /**
38040      * Returns the nested BorderLayout for this panel
38041      * @return {Roo.BorderLayout} 
38042      */
38043     getLayout : function(){
38044         return this.layout;
38045     },
38046     
38047      /**
38048      * Adds a xtype elements to the layout of the nested panel
38049      * <pre><code>
38050
38051 panel.addxtype({
38052        xtype : 'ContentPanel',
38053        region: 'west',
38054        items: [ .... ]
38055    }
38056 );
38057
38058 panel.addxtype({
38059         xtype : 'NestedLayoutPanel',
38060         region: 'west',
38061         layout: {
38062            center: { },
38063            west: { }   
38064         },
38065         items : [ ... list of content panels or nested layout panels.. ]
38066    }
38067 );
38068 </code></pre>
38069      * @param {Object} cfg Xtype definition of item to add.
38070      */
38071     addxtype : function(cfg) {
38072         return this.layout.addxtype(cfg);
38073     
38074     }
38075 });/*
38076  * Based on:
38077  * Ext JS Library 1.1.1
38078  * Copyright(c) 2006-2007, Ext JS, LLC.
38079  *
38080  * Originally Released Under LGPL - original licence link has changed is not relivant.
38081  *
38082  * Fork - LGPL
38083  * <script type="text/javascript">
38084  */
38085 /**
38086  * @class Roo.TabPanel
38087  * @extends Roo.util.Observable
38088  * A lightweight tab container.
38089  * <br><br>
38090  * Usage:
38091  * <pre><code>
38092 // basic tabs 1, built from existing content
38093 var tabs = new Roo.TabPanel("tabs1");
38094 tabs.addTab("script", "View Script");
38095 tabs.addTab("markup", "View Markup");
38096 tabs.activate("script");
38097
38098 // more advanced tabs, built from javascript
38099 var jtabs = new Roo.TabPanel("jtabs");
38100 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38101
38102 // set up the UpdateManager
38103 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38104 var updater = tab2.getUpdateManager();
38105 updater.setDefaultUrl("ajax1.htm");
38106 tab2.on('activate', updater.refresh, updater, true);
38107
38108 // Use setUrl for Ajax loading
38109 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38110 tab3.setUrl("ajax2.htm", null, true);
38111
38112 // Disabled tab
38113 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38114 tab4.disable();
38115
38116 jtabs.activate("jtabs-1");
38117  * </code></pre>
38118  * @constructor
38119  * Create a new TabPanel.
38120  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38121  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38122  */
38123 Roo.bootstrap.panel.Tabs = function(config){
38124     /**
38125     * The container element for this TabPanel.
38126     * @type Roo.Element
38127     */
38128     this.el = Roo.get(config.el);
38129     delete config.el;
38130     if(config){
38131         if(typeof config == "boolean"){
38132             this.tabPosition = config ? "bottom" : "top";
38133         }else{
38134             Roo.apply(this, config);
38135         }
38136     }
38137     
38138     if(this.tabPosition == "bottom"){
38139         // if tabs are at the bottom = create the body first.
38140         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38141         this.el.addClass("roo-tabs-bottom");
38142     }
38143     // next create the tabs holders
38144     
38145     if (this.tabPosition == "west"){
38146         
38147         var reg = this.region; // fake it..
38148         while (reg) {
38149             if (!reg.mgr.parent) {
38150                 break;
38151             }
38152             reg = reg.mgr.parent.region;
38153         }
38154         Roo.log("got nest?");
38155         Roo.log(reg);
38156         if (reg.mgr.getRegion('west')) {
38157             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38158             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38159             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38160             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38161             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38162         
38163             
38164         }
38165         
38166         
38167     } else {
38168      
38169         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38170         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38171         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38172         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38173     }
38174     
38175     
38176     if(Roo.isIE){
38177         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38178     }
38179     
38180     // finally - if tabs are at the top, then create the body last..
38181     if(this.tabPosition != "bottom"){
38182         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38183          * @type Roo.Element
38184          */
38185         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38186         this.el.addClass("roo-tabs-top");
38187     }
38188     this.items = [];
38189
38190     this.bodyEl.setStyle("position", "relative");
38191
38192     this.active = null;
38193     this.activateDelegate = this.activate.createDelegate(this);
38194
38195     this.addEvents({
38196         /**
38197          * @event tabchange
38198          * Fires when the active tab changes
38199          * @param {Roo.TabPanel} this
38200          * @param {Roo.TabPanelItem} activePanel The new active tab
38201          */
38202         "tabchange": true,
38203         /**
38204          * @event beforetabchange
38205          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38206          * @param {Roo.TabPanel} this
38207          * @param {Object} e Set cancel to true on this object to cancel the tab change
38208          * @param {Roo.TabPanelItem} tab The tab being changed to
38209          */
38210         "beforetabchange" : true
38211     });
38212
38213     Roo.EventManager.onWindowResize(this.onResize, this);
38214     this.cpad = this.el.getPadding("lr");
38215     this.hiddenCount = 0;
38216
38217
38218     // toolbar on the tabbar support...
38219     if (this.toolbar) {
38220         alert("no toolbar support yet");
38221         this.toolbar  = false;
38222         /*
38223         var tcfg = this.toolbar;
38224         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38225         this.toolbar = new Roo.Toolbar(tcfg);
38226         if (Roo.isSafari) {
38227             var tbl = tcfg.container.child('table', true);
38228             tbl.setAttribute('width', '100%');
38229         }
38230         */
38231         
38232     }
38233    
38234
38235
38236     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38237 };
38238
38239 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38240     /*
38241      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38242      */
38243     tabPosition : "top",
38244     /*
38245      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38246      */
38247     currentTabWidth : 0,
38248     /*
38249      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38250      */
38251     minTabWidth : 40,
38252     /*
38253      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38254      */
38255     maxTabWidth : 250,
38256     /*
38257      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38258      */
38259     preferredTabWidth : 175,
38260     /*
38261      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38262      */
38263     resizeTabs : false,
38264     /*
38265      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38266      */
38267     monitorResize : true,
38268     /*
38269      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38270      */
38271     toolbar : false,  // set by caller..
38272     
38273     region : false, /// set by caller
38274     
38275     disableTooltips : true, // not used yet...
38276
38277     /**
38278      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38279      * @param {String} id The id of the div to use <b>or create</b>
38280      * @param {String} text The text for the tab
38281      * @param {String} content (optional) Content to put in the TabPanelItem body
38282      * @param {Boolean} closable (optional) True to create a close icon on the tab
38283      * @return {Roo.TabPanelItem} The created TabPanelItem
38284      */
38285     addTab : function(id, text, content, closable, tpl)
38286     {
38287         var item = new Roo.bootstrap.panel.TabItem({
38288             panel: this,
38289             id : id,
38290             text : text,
38291             closable : closable,
38292             tpl : tpl
38293         });
38294         this.addTabItem(item);
38295         if(content){
38296             item.setContent(content);
38297         }
38298         return item;
38299     },
38300
38301     /**
38302      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38303      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38304      * @return {Roo.TabPanelItem}
38305      */
38306     getTab : function(id){
38307         return this.items[id];
38308     },
38309
38310     /**
38311      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38312      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38313      */
38314     hideTab : function(id){
38315         var t = this.items[id];
38316         if(!t.isHidden()){
38317            t.setHidden(true);
38318            this.hiddenCount++;
38319            this.autoSizeTabs();
38320         }
38321     },
38322
38323     /**
38324      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38325      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38326      */
38327     unhideTab : function(id){
38328         var t = this.items[id];
38329         if(t.isHidden()){
38330            t.setHidden(false);
38331            this.hiddenCount--;
38332            this.autoSizeTabs();
38333         }
38334     },
38335
38336     /**
38337      * Adds an existing {@link Roo.TabPanelItem}.
38338      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38339      */
38340     addTabItem : function(item)
38341     {
38342         this.items[item.id] = item;
38343         this.items.push(item);
38344         this.autoSizeTabs();
38345       //  if(this.resizeTabs){
38346     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38347   //         this.autoSizeTabs();
38348 //        }else{
38349 //            item.autoSize();
38350        // }
38351     },
38352
38353     /**
38354      * Removes a {@link Roo.TabPanelItem}.
38355      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38356      */
38357     removeTab : function(id){
38358         var items = this.items;
38359         var tab = items[id];
38360         if(!tab) { return; }
38361         var index = items.indexOf(tab);
38362         if(this.active == tab && items.length > 1){
38363             var newTab = this.getNextAvailable(index);
38364             if(newTab) {
38365                 newTab.activate();
38366             }
38367         }
38368         this.stripEl.dom.removeChild(tab.pnode.dom);
38369         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38370             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38371         }
38372         items.splice(index, 1);
38373         delete this.items[tab.id];
38374         tab.fireEvent("close", tab);
38375         tab.purgeListeners();
38376         this.autoSizeTabs();
38377     },
38378
38379     getNextAvailable : function(start){
38380         var items = this.items;
38381         var index = start;
38382         // look for a next tab that will slide over to
38383         // replace the one being removed
38384         while(index < items.length){
38385             var item = items[++index];
38386             if(item && !item.isHidden()){
38387                 return item;
38388             }
38389         }
38390         // if one isn't found select the previous tab (on the left)
38391         index = start;
38392         while(index >= 0){
38393             var item = items[--index];
38394             if(item && !item.isHidden()){
38395                 return item;
38396             }
38397         }
38398         return null;
38399     },
38400
38401     /**
38402      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38403      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38404      */
38405     disableTab : function(id){
38406         var tab = this.items[id];
38407         if(tab && this.active != tab){
38408             tab.disable();
38409         }
38410     },
38411
38412     /**
38413      * Enables a {@link Roo.TabPanelItem} that is disabled.
38414      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38415      */
38416     enableTab : function(id){
38417         var tab = this.items[id];
38418         tab.enable();
38419     },
38420
38421     /**
38422      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38423      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38424      * @return {Roo.TabPanelItem} The TabPanelItem.
38425      */
38426     activate : function(id)
38427     {
38428         //Roo.log('activite:'  + id);
38429         
38430         var tab = this.items[id];
38431         if(!tab){
38432             return null;
38433         }
38434         if(tab == this.active || tab.disabled){
38435             return tab;
38436         }
38437         var e = {};
38438         this.fireEvent("beforetabchange", this, e, tab);
38439         if(e.cancel !== true && !tab.disabled){
38440             if(this.active){
38441                 this.active.hide();
38442             }
38443             this.active = this.items[id];
38444             this.active.show();
38445             this.fireEvent("tabchange", this, this.active);
38446         }
38447         return tab;
38448     },
38449
38450     /**
38451      * Gets the active {@link Roo.TabPanelItem}.
38452      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38453      */
38454     getActiveTab : function(){
38455         return this.active;
38456     },
38457
38458     /**
38459      * Updates the tab body element to fit the height of the container element
38460      * for overflow scrolling
38461      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38462      */
38463     syncHeight : function(targetHeight){
38464         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38465         var bm = this.bodyEl.getMargins();
38466         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38467         this.bodyEl.setHeight(newHeight);
38468         return newHeight;
38469     },
38470
38471     onResize : function(){
38472         if(this.monitorResize){
38473             this.autoSizeTabs();
38474         }
38475     },
38476
38477     /**
38478      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38479      */
38480     beginUpdate : function(){
38481         this.updating = true;
38482     },
38483
38484     /**
38485      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38486      */
38487     endUpdate : function(){
38488         this.updating = false;
38489         this.autoSizeTabs();
38490     },
38491
38492     /**
38493      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38494      */
38495     autoSizeTabs : function()
38496     {
38497         var count = this.items.length;
38498         var vcount = count - this.hiddenCount;
38499         
38500         if (vcount < 2) {
38501             this.stripEl.hide();
38502         } else {
38503             this.stripEl.show();
38504         }
38505         
38506         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38507             return;
38508         }
38509         
38510         
38511         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38512         var availWidth = Math.floor(w / vcount);
38513         var b = this.stripBody;
38514         if(b.getWidth() > w){
38515             var tabs = this.items;
38516             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38517             if(availWidth < this.minTabWidth){
38518                 /*if(!this.sleft){    // incomplete scrolling code
38519                     this.createScrollButtons();
38520                 }
38521                 this.showScroll();
38522                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38523             }
38524         }else{
38525             if(this.currentTabWidth < this.preferredTabWidth){
38526                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38527             }
38528         }
38529     },
38530
38531     /**
38532      * Returns the number of tabs in this TabPanel.
38533      * @return {Number}
38534      */
38535      getCount : function(){
38536          return this.items.length;
38537      },
38538
38539     /**
38540      * Resizes all the tabs to the passed width
38541      * @param {Number} The new width
38542      */
38543     setTabWidth : function(width){
38544         this.currentTabWidth = width;
38545         for(var i = 0, len = this.items.length; i < len; i++) {
38546                 if(!this.items[i].isHidden()) {
38547                 this.items[i].setWidth(width);
38548             }
38549         }
38550     },
38551
38552     /**
38553      * Destroys this TabPanel
38554      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38555      */
38556     destroy : function(removeEl){
38557         Roo.EventManager.removeResizeListener(this.onResize, this);
38558         for(var i = 0, len = this.items.length; i < len; i++){
38559             this.items[i].purgeListeners();
38560         }
38561         if(removeEl === true){
38562             this.el.update("");
38563             this.el.remove();
38564         }
38565     },
38566     
38567     createStrip : function(container)
38568     {
38569         var strip = document.createElement("nav");
38570         strip.className = Roo.bootstrap.version == 4 ?
38571             "navbar-light bg-light" : 
38572             "navbar navbar-default"; //"x-tabs-wrap";
38573         container.appendChild(strip);
38574         return strip;
38575     },
38576     
38577     createStripList : function(strip)
38578     {
38579         // div wrapper for retard IE
38580         // returns the "tr" element.
38581         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38582         //'<div class="x-tabs-strip-wrap">'+
38583           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38584           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38585         return strip.firstChild; //.firstChild.firstChild.firstChild;
38586     },
38587     createBody : function(container)
38588     {
38589         var body = document.createElement("div");
38590         Roo.id(body, "tab-body");
38591         //Roo.fly(body).addClass("x-tabs-body");
38592         Roo.fly(body).addClass("tab-content");
38593         container.appendChild(body);
38594         return body;
38595     },
38596     createItemBody :function(bodyEl, id){
38597         var body = Roo.getDom(id);
38598         if(!body){
38599             body = document.createElement("div");
38600             body.id = id;
38601         }
38602         //Roo.fly(body).addClass("x-tabs-item-body");
38603         Roo.fly(body).addClass("tab-pane");
38604          bodyEl.insertBefore(body, bodyEl.firstChild);
38605         return body;
38606     },
38607     /** @private */
38608     createStripElements :  function(stripEl, text, closable, tpl)
38609     {
38610         var td = document.createElement("li"); // was td..
38611         td.className = 'nav-item';
38612         
38613         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38614         
38615         
38616         stripEl.appendChild(td);
38617         /*if(closable){
38618             td.className = "x-tabs-closable";
38619             if(!this.closeTpl){
38620                 this.closeTpl = new Roo.Template(
38621                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38622                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38623                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38624                 );
38625             }
38626             var el = this.closeTpl.overwrite(td, {"text": text});
38627             var close = el.getElementsByTagName("div")[0];
38628             var inner = el.getElementsByTagName("em")[0];
38629             return {"el": el, "close": close, "inner": inner};
38630         } else {
38631         */
38632         // not sure what this is..
38633 //            if(!this.tabTpl){
38634                 //this.tabTpl = new Roo.Template(
38635                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38636                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38637                 //);
38638 //                this.tabTpl = new Roo.Template(
38639 //                   '<a href="#">' +
38640 //                   '<span unselectable="on"' +
38641 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38642 //                            ' >{text}</span></a>'
38643 //                );
38644 //                
38645 //            }
38646
38647
38648             var template = tpl || this.tabTpl || false;
38649             
38650             if(!template){
38651                 template =  new Roo.Template(
38652                         Roo.bootstrap.version == 4 ? 
38653                             (
38654                                 '<a class="nav-link" href="#" unselectable="on"' +
38655                                      (this.disableTooltips ? '' : ' title="{text}"') +
38656                                      ' >{text}</a>'
38657                             ) : (
38658                                 '<a class="nav-link" href="#">' +
38659                                 '<span unselectable="on"' +
38660                                          (this.disableTooltips ? '' : ' title="{text}"') +
38661                                     ' >{text}</span></a>'
38662                             )
38663                 );
38664             }
38665             
38666             switch (typeof(template)) {
38667                 case 'object' :
38668                     break;
38669                 case 'string' :
38670                     template = new Roo.Template(template);
38671                     break;
38672                 default :
38673                     break;
38674             }
38675             
38676             var el = template.overwrite(td, {"text": text});
38677             
38678             var inner = el.getElementsByTagName("span")[0];
38679             
38680             return {"el": el, "inner": inner};
38681             
38682     }
38683         
38684     
38685 });
38686
38687 /**
38688  * @class Roo.TabPanelItem
38689  * @extends Roo.util.Observable
38690  * Represents an individual item (tab plus body) in a TabPanel.
38691  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38692  * @param {String} id The id of this TabPanelItem
38693  * @param {String} text The text for the tab of this TabPanelItem
38694  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38695  */
38696 Roo.bootstrap.panel.TabItem = function(config){
38697     /**
38698      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38699      * @type Roo.TabPanel
38700      */
38701     this.tabPanel = config.panel;
38702     /**
38703      * The id for this TabPanelItem
38704      * @type String
38705      */
38706     this.id = config.id;
38707     /** @private */
38708     this.disabled = false;
38709     /** @private */
38710     this.text = config.text;
38711     /** @private */
38712     this.loaded = false;
38713     this.closable = config.closable;
38714
38715     /**
38716      * The body element for this TabPanelItem.
38717      * @type Roo.Element
38718      */
38719     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38720     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38721     this.bodyEl.setStyle("display", "block");
38722     this.bodyEl.setStyle("zoom", "1");
38723     //this.hideAction();
38724
38725     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38726     /** @private */
38727     this.el = Roo.get(els.el);
38728     this.inner = Roo.get(els.inner, true);
38729      this.textEl = Roo.bootstrap.version == 4 ?
38730         this.el : Roo.get(this.el.dom.firstChild, true);
38731
38732     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38733     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38734
38735     
38736 //    this.el.on("mousedown", this.onTabMouseDown, this);
38737     this.el.on("click", this.onTabClick, this);
38738     /** @private */
38739     if(config.closable){
38740         var c = Roo.get(els.close, true);
38741         c.dom.title = this.closeText;
38742         c.addClassOnOver("close-over");
38743         c.on("click", this.closeClick, this);
38744      }
38745
38746     this.addEvents({
38747          /**
38748          * @event activate
38749          * Fires when this tab becomes the active tab.
38750          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38751          * @param {Roo.TabPanelItem} this
38752          */
38753         "activate": true,
38754         /**
38755          * @event beforeclose
38756          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38757          * @param {Roo.TabPanelItem} this
38758          * @param {Object} e Set cancel to true on this object to cancel the close.
38759          */
38760         "beforeclose": true,
38761         /**
38762          * @event close
38763          * Fires when this tab is closed.
38764          * @param {Roo.TabPanelItem} this
38765          */
38766          "close": true,
38767         /**
38768          * @event deactivate
38769          * Fires when this tab is no longer the active tab.
38770          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38771          * @param {Roo.TabPanelItem} this
38772          */
38773          "deactivate" : true
38774     });
38775     this.hidden = false;
38776
38777     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38778 };
38779
38780 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38781            {
38782     purgeListeners : function(){
38783        Roo.util.Observable.prototype.purgeListeners.call(this);
38784        this.el.removeAllListeners();
38785     },
38786     /**
38787      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38788      */
38789     show : function(){
38790         this.status_node.addClass("active");
38791         this.showAction();
38792         if(Roo.isOpera){
38793             this.tabPanel.stripWrap.repaint();
38794         }
38795         this.fireEvent("activate", this.tabPanel, this);
38796     },
38797
38798     /**
38799      * Returns true if this tab is the active tab.
38800      * @return {Boolean}
38801      */
38802     isActive : function(){
38803         return this.tabPanel.getActiveTab() == this;
38804     },
38805
38806     /**
38807      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38808      */
38809     hide : function(){
38810         this.status_node.removeClass("active");
38811         this.hideAction();
38812         this.fireEvent("deactivate", this.tabPanel, this);
38813     },
38814
38815     hideAction : function(){
38816         this.bodyEl.hide();
38817         this.bodyEl.setStyle("position", "absolute");
38818         this.bodyEl.setLeft("-20000px");
38819         this.bodyEl.setTop("-20000px");
38820     },
38821
38822     showAction : function(){
38823         this.bodyEl.setStyle("position", "relative");
38824         this.bodyEl.setTop("");
38825         this.bodyEl.setLeft("");
38826         this.bodyEl.show();
38827     },
38828
38829     /**
38830      * Set the tooltip for the tab.
38831      * @param {String} tooltip The tab's tooltip
38832      */
38833     setTooltip : function(text){
38834         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38835             this.textEl.dom.qtip = text;
38836             this.textEl.dom.removeAttribute('title');
38837         }else{
38838             this.textEl.dom.title = text;
38839         }
38840     },
38841
38842     onTabClick : function(e){
38843         e.preventDefault();
38844         this.tabPanel.activate(this.id);
38845     },
38846
38847     onTabMouseDown : function(e){
38848         e.preventDefault();
38849         this.tabPanel.activate(this.id);
38850     },
38851 /*
38852     getWidth : function(){
38853         return this.inner.getWidth();
38854     },
38855
38856     setWidth : function(width){
38857         var iwidth = width - this.linode.getPadding("lr");
38858         this.inner.setWidth(iwidth);
38859         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38860         this.linode.setWidth(width);
38861     },
38862 */
38863     /**
38864      * Show or hide the tab
38865      * @param {Boolean} hidden True to hide or false to show.
38866      */
38867     setHidden : function(hidden){
38868         this.hidden = hidden;
38869         this.linode.setStyle("display", hidden ? "none" : "");
38870     },
38871
38872     /**
38873      * Returns true if this tab is "hidden"
38874      * @return {Boolean}
38875      */
38876     isHidden : function(){
38877         return this.hidden;
38878     },
38879
38880     /**
38881      * Returns the text for this tab
38882      * @return {String}
38883      */
38884     getText : function(){
38885         return this.text;
38886     },
38887     /*
38888     autoSize : function(){
38889         //this.el.beginMeasure();
38890         this.textEl.setWidth(1);
38891         /*
38892          *  #2804 [new] Tabs in Roojs
38893          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38894          */
38895         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38896         //this.el.endMeasure();
38897     //},
38898
38899     /**
38900      * Sets the text for the tab (Note: this also sets the tooltip text)
38901      * @param {String} text The tab's text and tooltip
38902      */
38903     setText : function(text){
38904         this.text = text;
38905         this.textEl.update(text);
38906         this.setTooltip(text);
38907         //if(!this.tabPanel.resizeTabs){
38908         //    this.autoSize();
38909         //}
38910     },
38911     /**
38912      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38913      */
38914     activate : function(){
38915         this.tabPanel.activate(this.id);
38916     },
38917
38918     /**
38919      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38920      */
38921     disable : function(){
38922         if(this.tabPanel.active != this){
38923             this.disabled = true;
38924             this.status_node.addClass("disabled");
38925         }
38926     },
38927
38928     /**
38929      * Enables this TabPanelItem if it was previously disabled.
38930      */
38931     enable : function(){
38932         this.disabled = false;
38933         this.status_node.removeClass("disabled");
38934     },
38935
38936     /**
38937      * Sets the content for this TabPanelItem.
38938      * @param {String} content The content
38939      * @param {Boolean} loadScripts true to look for and load scripts
38940      */
38941     setContent : function(content, loadScripts){
38942         this.bodyEl.update(content, loadScripts);
38943     },
38944
38945     /**
38946      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38947      * @return {Roo.UpdateManager} The UpdateManager
38948      */
38949     getUpdateManager : function(){
38950         return this.bodyEl.getUpdateManager();
38951     },
38952
38953     /**
38954      * Set a URL to be used to load the content for this TabPanelItem.
38955      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38956      * @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)
38957      * @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)
38958      * @return {Roo.UpdateManager} The UpdateManager
38959      */
38960     setUrl : function(url, params, loadOnce){
38961         if(this.refreshDelegate){
38962             this.un('activate', this.refreshDelegate);
38963         }
38964         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38965         this.on("activate", this.refreshDelegate);
38966         return this.bodyEl.getUpdateManager();
38967     },
38968
38969     /** @private */
38970     _handleRefresh : function(url, params, loadOnce){
38971         if(!loadOnce || !this.loaded){
38972             var updater = this.bodyEl.getUpdateManager();
38973             updater.update(url, params, this._setLoaded.createDelegate(this));
38974         }
38975     },
38976
38977     /**
38978      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38979      *   Will fail silently if the setUrl method has not been called.
38980      *   This does not activate the panel, just updates its content.
38981      */
38982     refresh : function(){
38983         if(this.refreshDelegate){
38984            this.loaded = false;
38985            this.refreshDelegate();
38986         }
38987     },
38988
38989     /** @private */
38990     _setLoaded : function(){
38991         this.loaded = true;
38992     },
38993
38994     /** @private */
38995     closeClick : function(e){
38996         var o = {};
38997         e.stopEvent();
38998         this.fireEvent("beforeclose", this, o);
38999         if(o.cancel !== true){
39000             this.tabPanel.removeTab(this.id);
39001         }
39002     },
39003     /**
39004      * The text displayed in the tooltip for the close icon.
39005      * @type String
39006      */
39007     closeText : "Close this tab"
39008 });
39009 /**
39010 *    This script refer to:
39011 *    Title: International Telephone Input
39012 *    Author: Jack O'Connor
39013 *    Code version:  v12.1.12
39014 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39015 **/
39016
39017 Roo.bootstrap.PhoneInputData = function() {
39018     var d = [
39019       [
39020         "Afghanistan (‫افغانستان‬‎)",
39021         "af",
39022         "93"
39023       ],
39024       [
39025         "Albania (Shqipëri)",
39026         "al",
39027         "355"
39028       ],
39029       [
39030         "Algeria (‫الجزائر‬‎)",
39031         "dz",
39032         "213"
39033       ],
39034       [
39035         "American Samoa",
39036         "as",
39037         "1684"
39038       ],
39039       [
39040         "Andorra",
39041         "ad",
39042         "376"
39043       ],
39044       [
39045         "Angola",
39046         "ao",
39047         "244"
39048       ],
39049       [
39050         "Anguilla",
39051         "ai",
39052         "1264"
39053       ],
39054       [
39055         "Antigua and Barbuda",
39056         "ag",
39057         "1268"
39058       ],
39059       [
39060         "Argentina",
39061         "ar",
39062         "54"
39063       ],
39064       [
39065         "Armenia (Հայաստան)",
39066         "am",
39067         "374"
39068       ],
39069       [
39070         "Aruba",
39071         "aw",
39072         "297"
39073       ],
39074       [
39075         "Australia",
39076         "au",
39077         "61",
39078         0
39079       ],
39080       [
39081         "Austria (Österreich)",
39082         "at",
39083         "43"
39084       ],
39085       [
39086         "Azerbaijan (Azərbaycan)",
39087         "az",
39088         "994"
39089       ],
39090       [
39091         "Bahamas",
39092         "bs",
39093         "1242"
39094       ],
39095       [
39096         "Bahrain (‫البحرين‬‎)",
39097         "bh",
39098         "973"
39099       ],
39100       [
39101         "Bangladesh (বাংলাদেশ)",
39102         "bd",
39103         "880"
39104       ],
39105       [
39106         "Barbados",
39107         "bb",
39108         "1246"
39109       ],
39110       [
39111         "Belarus (Беларусь)",
39112         "by",
39113         "375"
39114       ],
39115       [
39116         "Belgium (België)",
39117         "be",
39118         "32"
39119       ],
39120       [
39121         "Belize",
39122         "bz",
39123         "501"
39124       ],
39125       [
39126         "Benin (Bénin)",
39127         "bj",
39128         "229"
39129       ],
39130       [
39131         "Bermuda",
39132         "bm",
39133         "1441"
39134       ],
39135       [
39136         "Bhutan (འབྲུག)",
39137         "bt",
39138         "975"
39139       ],
39140       [
39141         "Bolivia",
39142         "bo",
39143         "591"
39144       ],
39145       [
39146         "Bosnia and Herzegovina (Босна и Херцеговина)",
39147         "ba",
39148         "387"
39149       ],
39150       [
39151         "Botswana",
39152         "bw",
39153         "267"
39154       ],
39155       [
39156         "Brazil (Brasil)",
39157         "br",
39158         "55"
39159       ],
39160       [
39161         "British Indian Ocean Territory",
39162         "io",
39163         "246"
39164       ],
39165       [
39166         "British Virgin Islands",
39167         "vg",
39168         "1284"
39169       ],
39170       [
39171         "Brunei",
39172         "bn",
39173         "673"
39174       ],
39175       [
39176         "Bulgaria (България)",
39177         "bg",
39178         "359"
39179       ],
39180       [
39181         "Burkina Faso",
39182         "bf",
39183         "226"
39184       ],
39185       [
39186         "Burundi (Uburundi)",
39187         "bi",
39188         "257"
39189       ],
39190       [
39191         "Cambodia (កម្ពុជា)",
39192         "kh",
39193         "855"
39194       ],
39195       [
39196         "Cameroon (Cameroun)",
39197         "cm",
39198         "237"
39199       ],
39200       [
39201         "Canada",
39202         "ca",
39203         "1",
39204         1,
39205         ["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"]
39206       ],
39207       [
39208         "Cape Verde (Kabu Verdi)",
39209         "cv",
39210         "238"
39211       ],
39212       [
39213         "Caribbean Netherlands",
39214         "bq",
39215         "599",
39216         1
39217       ],
39218       [
39219         "Cayman Islands",
39220         "ky",
39221         "1345"
39222       ],
39223       [
39224         "Central African Republic (République centrafricaine)",
39225         "cf",
39226         "236"
39227       ],
39228       [
39229         "Chad (Tchad)",
39230         "td",
39231         "235"
39232       ],
39233       [
39234         "Chile",
39235         "cl",
39236         "56"
39237       ],
39238       [
39239         "China (中国)",
39240         "cn",
39241         "86"
39242       ],
39243       [
39244         "Christmas Island",
39245         "cx",
39246         "61",
39247         2
39248       ],
39249       [
39250         "Cocos (Keeling) Islands",
39251         "cc",
39252         "61",
39253         1
39254       ],
39255       [
39256         "Colombia",
39257         "co",
39258         "57"
39259       ],
39260       [
39261         "Comoros (‫جزر القمر‬‎)",
39262         "km",
39263         "269"
39264       ],
39265       [
39266         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39267         "cd",
39268         "243"
39269       ],
39270       [
39271         "Congo (Republic) (Congo-Brazzaville)",
39272         "cg",
39273         "242"
39274       ],
39275       [
39276         "Cook Islands",
39277         "ck",
39278         "682"
39279       ],
39280       [
39281         "Costa Rica",
39282         "cr",
39283         "506"
39284       ],
39285       [
39286         "Côte d’Ivoire",
39287         "ci",
39288         "225"
39289       ],
39290       [
39291         "Croatia (Hrvatska)",
39292         "hr",
39293         "385"
39294       ],
39295       [
39296         "Cuba",
39297         "cu",
39298         "53"
39299       ],
39300       [
39301         "Curaçao",
39302         "cw",
39303         "599",
39304         0
39305       ],
39306       [
39307         "Cyprus (Κύπρος)",
39308         "cy",
39309         "357"
39310       ],
39311       [
39312         "Czech Republic (Česká republika)",
39313         "cz",
39314         "420"
39315       ],
39316       [
39317         "Denmark (Danmark)",
39318         "dk",
39319         "45"
39320       ],
39321       [
39322         "Djibouti",
39323         "dj",
39324         "253"
39325       ],
39326       [
39327         "Dominica",
39328         "dm",
39329         "1767"
39330       ],
39331       [
39332         "Dominican Republic (República Dominicana)",
39333         "do",
39334         "1",
39335         2,
39336         ["809", "829", "849"]
39337       ],
39338       [
39339         "Ecuador",
39340         "ec",
39341         "593"
39342       ],
39343       [
39344         "Egypt (‫مصر‬‎)",
39345         "eg",
39346         "20"
39347       ],
39348       [
39349         "El Salvador",
39350         "sv",
39351         "503"
39352       ],
39353       [
39354         "Equatorial Guinea (Guinea Ecuatorial)",
39355         "gq",
39356         "240"
39357       ],
39358       [
39359         "Eritrea",
39360         "er",
39361         "291"
39362       ],
39363       [
39364         "Estonia (Eesti)",
39365         "ee",
39366         "372"
39367       ],
39368       [
39369         "Ethiopia",
39370         "et",
39371         "251"
39372       ],
39373       [
39374         "Falkland Islands (Islas Malvinas)",
39375         "fk",
39376         "500"
39377       ],
39378       [
39379         "Faroe Islands (Føroyar)",
39380         "fo",
39381         "298"
39382       ],
39383       [
39384         "Fiji",
39385         "fj",
39386         "679"
39387       ],
39388       [
39389         "Finland (Suomi)",
39390         "fi",
39391         "358",
39392         0
39393       ],
39394       [
39395         "France",
39396         "fr",
39397         "33"
39398       ],
39399       [
39400         "French Guiana (Guyane française)",
39401         "gf",
39402         "594"
39403       ],
39404       [
39405         "French Polynesia (Polynésie française)",
39406         "pf",
39407         "689"
39408       ],
39409       [
39410         "Gabon",
39411         "ga",
39412         "241"
39413       ],
39414       [
39415         "Gambia",
39416         "gm",
39417         "220"
39418       ],
39419       [
39420         "Georgia (საქართველო)",
39421         "ge",
39422         "995"
39423       ],
39424       [
39425         "Germany (Deutschland)",
39426         "de",
39427         "49"
39428       ],
39429       [
39430         "Ghana (Gaana)",
39431         "gh",
39432         "233"
39433       ],
39434       [
39435         "Gibraltar",
39436         "gi",
39437         "350"
39438       ],
39439       [
39440         "Greece (Ελλάδα)",
39441         "gr",
39442         "30"
39443       ],
39444       [
39445         "Greenland (Kalaallit Nunaat)",
39446         "gl",
39447         "299"
39448       ],
39449       [
39450         "Grenada",
39451         "gd",
39452         "1473"
39453       ],
39454       [
39455         "Guadeloupe",
39456         "gp",
39457         "590",
39458         0
39459       ],
39460       [
39461         "Guam",
39462         "gu",
39463         "1671"
39464       ],
39465       [
39466         "Guatemala",
39467         "gt",
39468         "502"
39469       ],
39470       [
39471         "Guernsey",
39472         "gg",
39473         "44",
39474         1
39475       ],
39476       [
39477         "Guinea (Guinée)",
39478         "gn",
39479         "224"
39480       ],
39481       [
39482         "Guinea-Bissau (Guiné Bissau)",
39483         "gw",
39484         "245"
39485       ],
39486       [
39487         "Guyana",
39488         "gy",
39489         "592"
39490       ],
39491       [
39492         "Haiti",
39493         "ht",
39494         "509"
39495       ],
39496       [
39497         "Honduras",
39498         "hn",
39499         "504"
39500       ],
39501       [
39502         "Hong Kong (香港)",
39503         "hk",
39504         "852"
39505       ],
39506       [
39507         "Hungary (Magyarország)",
39508         "hu",
39509         "36"
39510       ],
39511       [
39512         "Iceland (Ísland)",
39513         "is",
39514         "354"
39515       ],
39516       [
39517         "India (भारत)",
39518         "in",
39519         "91"
39520       ],
39521       [
39522         "Indonesia",
39523         "id",
39524         "62"
39525       ],
39526       [
39527         "Iran (‫ایران‬‎)",
39528         "ir",
39529         "98"
39530       ],
39531       [
39532         "Iraq (‫العراق‬‎)",
39533         "iq",
39534         "964"
39535       ],
39536       [
39537         "Ireland",
39538         "ie",
39539         "353"
39540       ],
39541       [
39542         "Isle of Man",
39543         "im",
39544         "44",
39545         2
39546       ],
39547       [
39548         "Israel (‫ישראל‬‎)",
39549         "il",
39550         "972"
39551       ],
39552       [
39553         "Italy (Italia)",
39554         "it",
39555         "39",
39556         0
39557       ],
39558       [
39559         "Jamaica",
39560         "jm",
39561         "1876"
39562       ],
39563       [
39564         "Japan (日本)",
39565         "jp",
39566         "81"
39567       ],
39568       [
39569         "Jersey",
39570         "je",
39571         "44",
39572         3
39573       ],
39574       [
39575         "Jordan (‫الأردن‬‎)",
39576         "jo",
39577         "962"
39578       ],
39579       [
39580         "Kazakhstan (Казахстан)",
39581         "kz",
39582         "7",
39583         1
39584       ],
39585       [
39586         "Kenya",
39587         "ke",
39588         "254"
39589       ],
39590       [
39591         "Kiribati",
39592         "ki",
39593         "686"
39594       ],
39595       [
39596         "Kosovo",
39597         "xk",
39598         "383"
39599       ],
39600       [
39601         "Kuwait (‫الكويت‬‎)",
39602         "kw",
39603         "965"
39604       ],
39605       [
39606         "Kyrgyzstan (Кыргызстан)",
39607         "kg",
39608         "996"
39609       ],
39610       [
39611         "Laos (ລາວ)",
39612         "la",
39613         "856"
39614       ],
39615       [
39616         "Latvia (Latvija)",
39617         "lv",
39618         "371"
39619       ],
39620       [
39621         "Lebanon (‫لبنان‬‎)",
39622         "lb",
39623         "961"
39624       ],
39625       [
39626         "Lesotho",
39627         "ls",
39628         "266"
39629       ],
39630       [
39631         "Liberia",
39632         "lr",
39633         "231"
39634       ],
39635       [
39636         "Libya (‫ليبيا‬‎)",
39637         "ly",
39638         "218"
39639       ],
39640       [
39641         "Liechtenstein",
39642         "li",
39643         "423"
39644       ],
39645       [
39646         "Lithuania (Lietuva)",
39647         "lt",
39648         "370"
39649       ],
39650       [
39651         "Luxembourg",
39652         "lu",
39653         "352"
39654       ],
39655       [
39656         "Macau (澳門)",
39657         "mo",
39658         "853"
39659       ],
39660       [
39661         "Macedonia (FYROM) (Македонија)",
39662         "mk",
39663         "389"
39664       ],
39665       [
39666         "Madagascar (Madagasikara)",
39667         "mg",
39668         "261"
39669       ],
39670       [
39671         "Malawi",
39672         "mw",
39673         "265"
39674       ],
39675       [
39676         "Malaysia",
39677         "my",
39678         "60"
39679       ],
39680       [
39681         "Maldives",
39682         "mv",
39683         "960"
39684       ],
39685       [
39686         "Mali",
39687         "ml",
39688         "223"
39689       ],
39690       [
39691         "Malta",
39692         "mt",
39693         "356"
39694       ],
39695       [
39696         "Marshall Islands",
39697         "mh",
39698         "692"
39699       ],
39700       [
39701         "Martinique",
39702         "mq",
39703         "596"
39704       ],
39705       [
39706         "Mauritania (‫موريتانيا‬‎)",
39707         "mr",
39708         "222"
39709       ],
39710       [
39711         "Mauritius (Moris)",
39712         "mu",
39713         "230"
39714       ],
39715       [
39716         "Mayotte",
39717         "yt",
39718         "262",
39719         1
39720       ],
39721       [
39722         "Mexico (México)",
39723         "mx",
39724         "52"
39725       ],
39726       [
39727         "Micronesia",
39728         "fm",
39729         "691"
39730       ],
39731       [
39732         "Moldova (Republica Moldova)",
39733         "md",
39734         "373"
39735       ],
39736       [
39737         "Monaco",
39738         "mc",
39739         "377"
39740       ],
39741       [
39742         "Mongolia (Монгол)",
39743         "mn",
39744         "976"
39745       ],
39746       [
39747         "Montenegro (Crna Gora)",
39748         "me",
39749         "382"
39750       ],
39751       [
39752         "Montserrat",
39753         "ms",
39754         "1664"
39755       ],
39756       [
39757         "Morocco (‫المغرب‬‎)",
39758         "ma",
39759         "212",
39760         0
39761       ],
39762       [
39763         "Mozambique (Moçambique)",
39764         "mz",
39765         "258"
39766       ],
39767       [
39768         "Myanmar (Burma) (မြန်မာ)",
39769         "mm",
39770         "95"
39771       ],
39772       [
39773         "Namibia (Namibië)",
39774         "na",
39775         "264"
39776       ],
39777       [
39778         "Nauru",
39779         "nr",
39780         "674"
39781       ],
39782       [
39783         "Nepal (नेपाल)",
39784         "np",
39785         "977"
39786       ],
39787       [
39788         "Netherlands (Nederland)",
39789         "nl",
39790         "31"
39791       ],
39792       [
39793         "New Caledonia (Nouvelle-Calédonie)",
39794         "nc",
39795         "687"
39796       ],
39797       [
39798         "New Zealand",
39799         "nz",
39800         "64"
39801       ],
39802       [
39803         "Nicaragua",
39804         "ni",
39805         "505"
39806       ],
39807       [
39808         "Niger (Nijar)",
39809         "ne",
39810         "227"
39811       ],
39812       [
39813         "Nigeria",
39814         "ng",
39815         "234"
39816       ],
39817       [
39818         "Niue",
39819         "nu",
39820         "683"
39821       ],
39822       [
39823         "Norfolk Island",
39824         "nf",
39825         "672"
39826       ],
39827       [
39828         "North Korea (조선 민주주의 인민 공화국)",
39829         "kp",
39830         "850"
39831       ],
39832       [
39833         "Northern Mariana Islands",
39834         "mp",
39835         "1670"
39836       ],
39837       [
39838         "Norway (Norge)",
39839         "no",
39840         "47",
39841         0
39842       ],
39843       [
39844         "Oman (‫عُمان‬‎)",
39845         "om",
39846         "968"
39847       ],
39848       [
39849         "Pakistan (‫پاکستان‬‎)",
39850         "pk",
39851         "92"
39852       ],
39853       [
39854         "Palau",
39855         "pw",
39856         "680"
39857       ],
39858       [
39859         "Palestine (‫فلسطين‬‎)",
39860         "ps",
39861         "970"
39862       ],
39863       [
39864         "Panama (Panamá)",
39865         "pa",
39866         "507"
39867       ],
39868       [
39869         "Papua New Guinea",
39870         "pg",
39871         "675"
39872       ],
39873       [
39874         "Paraguay",
39875         "py",
39876         "595"
39877       ],
39878       [
39879         "Peru (Perú)",
39880         "pe",
39881         "51"
39882       ],
39883       [
39884         "Philippines",
39885         "ph",
39886         "63"
39887       ],
39888       [
39889         "Poland (Polska)",
39890         "pl",
39891         "48"
39892       ],
39893       [
39894         "Portugal",
39895         "pt",
39896         "351"
39897       ],
39898       [
39899         "Puerto Rico",
39900         "pr",
39901         "1",
39902         3,
39903         ["787", "939"]
39904       ],
39905       [
39906         "Qatar (‫قطر‬‎)",
39907         "qa",
39908         "974"
39909       ],
39910       [
39911         "Réunion (La Réunion)",
39912         "re",
39913         "262",
39914         0
39915       ],
39916       [
39917         "Romania (România)",
39918         "ro",
39919         "40"
39920       ],
39921       [
39922         "Russia (Россия)",
39923         "ru",
39924         "7",
39925         0
39926       ],
39927       [
39928         "Rwanda",
39929         "rw",
39930         "250"
39931       ],
39932       [
39933         "Saint Barthélemy",
39934         "bl",
39935         "590",
39936         1
39937       ],
39938       [
39939         "Saint Helena",
39940         "sh",
39941         "290"
39942       ],
39943       [
39944         "Saint Kitts and Nevis",
39945         "kn",
39946         "1869"
39947       ],
39948       [
39949         "Saint Lucia",
39950         "lc",
39951         "1758"
39952       ],
39953       [
39954         "Saint Martin (Saint-Martin (partie française))",
39955         "mf",
39956         "590",
39957         2
39958       ],
39959       [
39960         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39961         "pm",
39962         "508"
39963       ],
39964       [
39965         "Saint Vincent and the Grenadines",
39966         "vc",
39967         "1784"
39968       ],
39969       [
39970         "Samoa",
39971         "ws",
39972         "685"
39973       ],
39974       [
39975         "San Marino",
39976         "sm",
39977         "378"
39978       ],
39979       [
39980         "São Tomé and Príncipe (São Tomé e Príncipe)",
39981         "st",
39982         "239"
39983       ],
39984       [
39985         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39986         "sa",
39987         "966"
39988       ],
39989       [
39990         "Senegal (Sénégal)",
39991         "sn",
39992         "221"
39993       ],
39994       [
39995         "Serbia (Србија)",
39996         "rs",
39997         "381"
39998       ],
39999       [
40000         "Seychelles",
40001         "sc",
40002         "248"
40003       ],
40004       [
40005         "Sierra Leone",
40006         "sl",
40007         "232"
40008       ],
40009       [
40010         "Singapore",
40011         "sg",
40012         "65"
40013       ],
40014       [
40015         "Sint Maarten",
40016         "sx",
40017         "1721"
40018       ],
40019       [
40020         "Slovakia (Slovensko)",
40021         "sk",
40022         "421"
40023       ],
40024       [
40025         "Slovenia (Slovenija)",
40026         "si",
40027         "386"
40028       ],
40029       [
40030         "Solomon Islands",
40031         "sb",
40032         "677"
40033       ],
40034       [
40035         "Somalia (Soomaaliya)",
40036         "so",
40037         "252"
40038       ],
40039       [
40040         "South Africa",
40041         "za",
40042         "27"
40043       ],
40044       [
40045         "South Korea (대한민국)",
40046         "kr",
40047         "82"
40048       ],
40049       [
40050         "South Sudan (‫جنوب السودان‬‎)",
40051         "ss",
40052         "211"
40053       ],
40054       [
40055         "Spain (España)",
40056         "es",
40057         "34"
40058       ],
40059       [
40060         "Sri Lanka (ශ්‍රී ලංකාව)",
40061         "lk",
40062         "94"
40063       ],
40064       [
40065         "Sudan (‫السودان‬‎)",
40066         "sd",
40067         "249"
40068       ],
40069       [
40070         "Suriname",
40071         "sr",
40072         "597"
40073       ],
40074       [
40075         "Svalbard and Jan Mayen",
40076         "sj",
40077         "47",
40078         1
40079       ],
40080       [
40081         "Swaziland",
40082         "sz",
40083         "268"
40084       ],
40085       [
40086         "Sweden (Sverige)",
40087         "se",
40088         "46"
40089       ],
40090       [
40091         "Switzerland (Schweiz)",
40092         "ch",
40093         "41"
40094       ],
40095       [
40096         "Syria (‫سوريا‬‎)",
40097         "sy",
40098         "963"
40099       ],
40100       [
40101         "Taiwan (台灣)",
40102         "tw",
40103         "886"
40104       ],
40105       [
40106         "Tajikistan",
40107         "tj",
40108         "992"
40109       ],
40110       [
40111         "Tanzania",
40112         "tz",
40113         "255"
40114       ],
40115       [
40116         "Thailand (ไทย)",
40117         "th",
40118         "66"
40119       ],
40120       [
40121         "Timor-Leste",
40122         "tl",
40123         "670"
40124       ],
40125       [
40126         "Togo",
40127         "tg",
40128         "228"
40129       ],
40130       [
40131         "Tokelau",
40132         "tk",
40133         "690"
40134       ],
40135       [
40136         "Tonga",
40137         "to",
40138         "676"
40139       ],
40140       [
40141         "Trinidad and Tobago",
40142         "tt",
40143         "1868"
40144       ],
40145       [
40146         "Tunisia (‫تونس‬‎)",
40147         "tn",
40148         "216"
40149       ],
40150       [
40151         "Turkey (Türkiye)",
40152         "tr",
40153         "90"
40154       ],
40155       [
40156         "Turkmenistan",
40157         "tm",
40158         "993"
40159       ],
40160       [
40161         "Turks and Caicos Islands",
40162         "tc",
40163         "1649"
40164       ],
40165       [
40166         "Tuvalu",
40167         "tv",
40168         "688"
40169       ],
40170       [
40171         "U.S. Virgin Islands",
40172         "vi",
40173         "1340"
40174       ],
40175       [
40176         "Uganda",
40177         "ug",
40178         "256"
40179       ],
40180       [
40181         "Ukraine (Україна)",
40182         "ua",
40183         "380"
40184       ],
40185       [
40186         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40187         "ae",
40188         "971"
40189       ],
40190       [
40191         "United Kingdom",
40192         "gb",
40193         "44",
40194         0
40195       ],
40196       [
40197         "United States",
40198         "us",
40199         "1",
40200         0
40201       ],
40202       [
40203         "Uruguay",
40204         "uy",
40205         "598"
40206       ],
40207       [
40208         "Uzbekistan (Oʻzbekiston)",
40209         "uz",
40210         "998"
40211       ],
40212       [
40213         "Vanuatu",
40214         "vu",
40215         "678"
40216       ],
40217       [
40218         "Vatican City (Città del Vaticano)",
40219         "va",
40220         "39",
40221         1
40222       ],
40223       [
40224         "Venezuela",
40225         "ve",
40226         "58"
40227       ],
40228       [
40229         "Vietnam (Việt Nam)",
40230         "vn",
40231         "84"
40232       ],
40233       [
40234         "Wallis and Futuna (Wallis-et-Futuna)",
40235         "wf",
40236         "681"
40237       ],
40238       [
40239         "Western Sahara (‫الصحراء الغربية‬‎)",
40240         "eh",
40241         "212",
40242         1
40243       ],
40244       [
40245         "Yemen (‫اليمن‬‎)",
40246         "ye",
40247         "967"
40248       ],
40249       [
40250         "Zambia",
40251         "zm",
40252         "260"
40253       ],
40254       [
40255         "Zimbabwe",
40256         "zw",
40257         "263"
40258       ],
40259       [
40260         "Åland Islands",
40261         "ax",
40262         "358",
40263         1
40264       ]
40265   ];
40266   
40267   return d;
40268 }/**
40269 *    This script refer to:
40270 *    Title: International Telephone Input
40271 *    Author: Jack O'Connor
40272 *    Code version:  v12.1.12
40273 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40274 **/
40275
40276 /**
40277  * @class Roo.bootstrap.PhoneInput
40278  * @extends Roo.bootstrap.TriggerField
40279  * An input with International dial-code selection
40280  
40281  * @cfg {String} defaultDialCode default '+852'
40282  * @cfg {Array} preferedCountries default []
40283   
40284  * @constructor
40285  * Create a new PhoneInput.
40286  * @param {Object} config Configuration options
40287  */
40288
40289 Roo.bootstrap.PhoneInput = function(config) {
40290     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40291 };
40292
40293 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40294         
40295         listWidth: undefined,
40296         
40297         selectedClass: 'active',
40298         
40299         invalidClass : "has-warning",
40300         
40301         validClass: 'has-success',
40302         
40303         allowed: '0123456789',
40304         
40305         max_length: 15,
40306         
40307         /**
40308          * @cfg {String} defaultDialCode The default dial code when initializing the input
40309          */
40310         defaultDialCode: '+852',
40311         
40312         /**
40313          * @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
40314          */
40315         preferedCountries: false,
40316         
40317         getAutoCreate : function()
40318         {
40319             var data = Roo.bootstrap.PhoneInputData();
40320             var align = this.labelAlign || this.parentLabelAlign();
40321             var id = Roo.id();
40322             
40323             this.allCountries = [];
40324             this.dialCodeMapping = [];
40325             
40326             for (var i = 0; i < data.length; i++) {
40327               var c = data[i];
40328               this.allCountries[i] = {
40329                 name: c[0],
40330                 iso2: c[1],
40331                 dialCode: c[2],
40332                 priority: c[3] || 0,
40333                 areaCodes: c[4] || null
40334               };
40335               this.dialCodeMapping[c[2]] = {
40336                   name: c[0],
40337                   iso2: c[1],
40338                   priority: c[3] || 0,
40339                   areaCodes: c[4] || null
40340               };
40341             }
40342             
40343             var cfg = {
40344                 cls: 'form-group',
40345                 cn: []
40346             };
40347             
40348             var input =  {
40349                 tag: 'input',
40350                 id : id,
40351                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40352                 maxlength: this.max_length,
40353                 cls : 'form-control tel-input',
40354                 autocomplete: 'new-password'
40355             };
40356             
40357             var hiddenInput = {
40358                 tag: 'input',
40359                 type: 'hidden',
40360                 cls: 'hidden-tel-input'
40361             };
40362             
40363             if (this.name) {
40364                 hiddenInput.name = this.name;
40365             }
40366             
40367             if (this.disabled) {
40368                 input.disabled = true;
40369             }
40370             
40371             var flag_container = {
40372                 tag: 'div',
40373                 cls: 'flag-box',
40374                 cn: [
40375                     {
40376                         tag: 'div',
40377                         cls: 'flag'
40378                     },
40379                     {
40380                         tag: 'div',
40381                         cls: 'caret'
40382                     }
40383                 ]
40384             };
40385             
40386             var box = {
40387                 tag: 'div',
40388                 cls: this.hasFeedback ? 'has-feedback' : '',
40389                 cn: [
40390                     hiddenInput,
40391                     input,
40392                     {
40393                         tag: 'input',
40394                         cls: 'dial-code-holder',
40395                         disabled: true
40396                     }
40397                 ]
40398             };
40399             
40400             var container = {
40401                 cls: 'roo-select2-container input-group',
40402                 cn: [
40403                     flag_container,
40404                     box
40405                 ]
40406             };
40407             
40408             if (this.fieldLabel.length) {
40409                 var indicator = {
40410                     tag: 'i',
40411                     tooltip: 'This field is required'
40412                 };
40413                 
40414                 var label = {
40415                     tag: 'label',
40416                     'for':  id,
40417                     cls: 'control-label',
40418                     cn: []
40419                 };
40420                 
40421                 var label_text = {
40422                     tag: 'span',
40423                     html: this.fieldLabel
40424                 };
40425                 
40426                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40427                 label.cn = [
40428                     indicator,
40429                     label_text
40430                 ];
40431                 
40432                 if(this.indicatorpos == 'right') {
40433                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40434                     label.cn = [
40435                         label_text,
40436                         indicator
40437                     ];
40438                 }
40439                 
40440                 if(align == 'left') {
40441                     container = {
40442                         tag: 'div',
40443                         cn: [
40444                             container
40445                         ]
40446                     };
40447                     
40448                     if(this.labelWidth > 12){
40449                         label.style = "width: " + this.labelWidth + 'px';
40450                     }
40451                     if(this.labelWidth < 13 && this.labelmd == 0){
40452                         this.labelmd = this.labelWidth;
40453                     }
40454                     if(this.labellg > 0){
40455                         label.cls += ' col-lg-' + this.labellg;
40456                         input.cls += ' col-lg-' + (12 - this.labellg);
40457                     }
40458                     if(this.labelmd > 0){
40459                         label.cls += ' col-md-' + this.labelmd;
40460                         container.cls += ' col-md-' + (12 - this.labelmd);
40461                     }
40462                     if(this.labelsm > 0){
40463                         label.cls += ' col-sm-' + this.labelsm;
40464                         container.cls += ' col-sm-' + (12 - this.labelsm);
40465                     }
40466                     if(this.labelxs > 0){
40467                         label.cls += ' col-xs-' + this.labelxs;
40468                         container.cls += ' col-xs-' + (12 - this.labelxs);
40469                     }
40470                 }
40471             }
40472             
40473             cfg.cn = [
40474                 label,
40475                 container
40476             ];
40477             
40478             var settings = this;
40479             
40480             ['xs','sm','md','lg'].map(function(size){
40481                 if (settings[size]) {
40482                     cfg.cls += ' col-' + size + '-' + settings[size];
40483                 }
40484             });
40485             
40486             this.store = new Roo.data.Store({
40487                 proxy : new Roo.data.MemoryProxy({}),
40488                 reader : new Roo.data.JsonReader({
40489                     fields : [
40490                         {
40491                             'name' : 'name',
40492                             'type' : 'string'
40493                         },
40494                         {
40495                             'name' : 'iso2',
40496                             'type' : 'string'
40497                         },
40498                         {
40499                             'name' : 'dialCode',
40500                             'type' : 'string'
40501                         },
40502                         {
40503                             'name' : 'priority',
40504                             'type' : 'string'
40505                         },
40506                         {
40507                             'name' : 'areaCodes',
40508                             'type' : 'string'
40509                         }
40510                     ]
40511                 })
40512             });
40513             
40514             if(!this.preferedCountries) {
40515                 this.preferedCountries = [
40516                     'hk',
40517                     'gb',
40518                     'us'
40519                 ];
40520             }
40521             
40522             var p = this.preferedCountries.reverse();
40523             
40524             if(p) {
40525                 for (var i = 0; i < p.length; i++) {
40526                     for (var j = 0; j < this.allCountries.length; j++) {
40527                         if(this.allCountries[j].iso2 == p[i]) {
40528                             var t = this.allCountries[j];
40529                             this.allCountries.splice(j,1);
40530                             this.allCountries.unshift(t);
40531                         }
40532                     } 
40533                 }
40534             }
40535             
40536             this.store.proxy.data = {
40537                 success: true,
40538                 data: this.allCountries
40539             };
40540             
40541             return cfg;
40542         },
40543         
40544         initEvents : function()
40545         {
40546             this.createList();
40547             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40548             
40549             this.indicator = this.indicatorEl();
40550             this.flag = this.flagEl();
40551             this.dialCodeHolder = this.dialCodeHolderEl();
40552             
40553             this.trigger = this.el.select('div.flag-box',true).first();
40554             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40555             
40556             var _this = this;
40557             
40558             (function(){
40559                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40560                 _this.list.setWidth(lw);
40561             }).defer(100);
40562             
40563             this.list.on('mouseover', this.onViewOver, this);
40564             this.list.on('mousemove', this.onViewMove, this);
40565             this.inputEl().on("keyup", this.onKeyUp, this);
40566             this.inputEl().on("keypress", this.onKeyPress, this);
40567             
40568             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40569
40570             this.view = new Roo.View(this.list, this.tpl, {
40571                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40572             });
40573             
40574             this.view.on('click', this.onViewClick, this);
40575             this.setValue(this.defaultDialCode);
40576         },
40577         
40578         onTriggerClick : function(e)
40579         {
40580             Roo.log('trigger click');
40581             if(this.disabled){
40582                 return;
40583             }
40584             
40585             if(this.isExpanded()){
40586                 this.collapse();
40587                 this.hasFocus = false;
40588             }else {
40589                 this.store.load({});
40590                 this.hasFocus = true;
40591                 this.expand();
40592             }
40593         },
40594         
40595         isExpanded : function()
40596         {
40597             return this.list.isVisible();
40598         },
40599         
40600         collapse : function()
40601         {
40602             if(!this.isExpanded()){
40603                 return;
40604             }
40605             this.list.hide();
40606             Roo.get(document).un('mousedown', this.collapseIf, this);
40607             Roo.get(document).un('mousewheel', this.collapseIf, this);
40608             this.fireEvent('collapse', this);
40609             this.validate();
40610         },
40611         
40612         expand : function()
40613         {
40614             Roo.log('expand');
40615
40616             if(this.isExpanded() || !this.hasFocus){
40617                 return;
40618             }
40619             
40620             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40621             this.list.setWidth(lw);
40622             
40623             this.list.show();
40624             this.restrictHeight();
40625             
40626             Roo.get(document).on('mousedown', this.collapseIf, this);
40627             Roo.get(document).on('mousewheel', this.collapseIf, this);
40628             
40629             this.fireEvent('expand', this);
40630         },
40631         
40632         restrictHeight : function()
40633         {
40634             this.list.alignTo(this.inputEl(), this.listAlign);
40635             this.list.alignTo(this.inputEl(), this.listAlign);
40636         },
40637         
40638         onViewOver : function(e, t)
40639         {
40640             if(this.inKeyMode){
40641                 return;
40642             }
40643             var item = this.view.findItemFromChild(t);
40644             
40645             if(item){
40646                 var index = this.view.indexOf(item);
40647                 this.select(index, false);
40648             }
40649         },
40650
40651         // private
40652         onViewClick : function(view, doFocus, el, e)
40653         {
40654             var index = this.view.getSelectedIndexes()[0];
40655             
40656             var r = this.store.getAt(index);
40657             
40658             if(r){
40659                 this.onSelect(r, index);
40660             }
40661             if(doFocus !== false && !this.blockFocus){
40662                 this.inputEl().focus();
40663             }
40664         },
40665         
40666         onViewMove : function(e, t)
40667         {
40668             this.inKeyMode = false;
40669         },
40670         
40671         select : function(index, scrollIntoView)
40672         {
40673             this.selectedIndex = index;
40674             this.view.select(index);
40675             if(scrollIntoView !== false){
40676                 var el = this.view.getNode(index);
40677                 if(el){
40678                     this.list.scrollChildIntoView(el, false);
40679                 }
40680             }
40681         },
40682         
40683         createList : function()
40684         {
40685             this.list = Roo.get(document.body).createChild({
40686                 tag: 'ul',
40687                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40688                 style: 'display:none'
40689             });
40690             
40691             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40692         },
40693         
40694         collapseIf : function(e)
40695         {
40696             var in_combo  = e.within(this.el);
40697             var in_list =  e.within(this.list);
40698             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40699             
40700             if (in_combo || in_list || is_list) {
40701                 return;
40702             }
40703             this.collapse();
40704         },
40705         
40706         onSelect : function(record, index)
40707         {
40708             if(this.fireEvent('beforeselect', this, record, index) !== false){
40709                 
40710                 this.setFlagClass(record.data.iso2);
40711                 this.setDialCode(record.data.dialCode);
40712                 this.hasFocus = false;
40713                 this.collapse();
40714                 this.fireEvent('select', this, record, index);
40715             }
40716         },
40717         
40718         flagEl : function()
40719         {
40720             var flag = this.el.select('div.flag',true).first();
40721             if(!flag){
40722                 return false;
40723             }
40724             return flag;
40725         },
40726         
40727         dialCodeHolderEl : function()
40728         {
40729             var d = this.el.select('input.dial-code-holder',true).first();
40730             if(!d){
40731                 return false;
40732             }
40733             return d;
40734         },
40735         
40736         setDialCode : function(v)
40737         {
40738             this.dialCodeHolder.dom.value = '+'+v;
40739         },
40740         
40741         setFlagClass : function(n)
40742         {
40743             this.flag.dom.className = 'flag '+n;
40744         },
40745         
40746         getValue : function()
40747         {
40748             var v = this.inputEl().getValue();
40749             if(this.dialCodeHolder) {
40750                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40751             }
40752             return v;
40753         },
40754         
40755         setValue : function(v)
40756         {
40757             var d = this.getDialCode(v);
40758             
40759             //invalid dial code
40760             if(v.length == 0 || !d || d.length == 0) {
40761                 if(this.rendered){
40762                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40763                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40764                 }
40765                 return;
40766             }
40767             
40768             //valid dial code
40769             this.setFlagClass(this.dialCodeMapping[d].iso2);
40770             this.setDialCode(d);
40771             this.inputEl().dom.value = v.replace('+'+d,'');
40772             this.hiddenEl().dom.value = this.getValue();
40773             
40774             this.validate();
40775         },
40776         
40777         getDialCode : function(v)
40778         {
40779             v = v ||  '';
40780             
40781             if (v.length == 0) {
40782                 return this.dialCodeHolder.dom.value;
40783             }
40784             
40785             var dialCode = "";
40786             if (v.charAt(0) != "+") {
40787                 return false;
40788             }
40789             var numericChars = "";
40790             for (var i = 1; i < v.length; i++) {
40791               var c = v.charAt(i);
40792               if (!isNaN(c)) {
40793                 numericChars += c;
40794                 if (this.dialCodeMapping[numericChars]) {
40795                   dialCode = v.substr(1, i);
40796                 }
40797                 if (numericChars.length == 4) {
40798                   break;
40799                 }
40800               }
40801             }
40802             return dialCode;
40803         },
40804         
40805         reset : function()
40806         {
40807             this.setValue(this.defaultDialCode);
40808             this.validate();
40809         },
40810         
40811         hiddenEl : function()
40812         {
40813             return this.el.select('input.hidden-tel-input',true).first();
40814         },
40815         
40816         // after setting val
40817         onKeyUp : function(e){
40818             this.setValue(this.getValue());
40819         },
40820         
40821         onKeyPress : function(e){
40822             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40823                 e.stopEvent();
40824             }
40825         }
40826         
40827 });
40828 /**
40829  * @class Roo.bootstrap.MoneyField
40830  * @extends Roo.bootstrap.ComboBox
40831  * Bootstrap MoneyField class
40832  * 
40833  * @constructor
40834  * Create a new MoneyField.
40835  * @param {Object} config Configuration options
40836  */
40837
40838 Roo.bootstrap.MoneyField = function(config) {
40839     
40840     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40841     
40842 };
40843
40844 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40845     
40846     /**
40847      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40848      */
40849     allowDecimals : true,
40850     /**
40851      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40852      */
40853     decimalSeparator : ".",
40854     /**
40855      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40856      */
40857     decimalPrecision : 0,
40858     /**
40859      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40860      */
40861     allowNegative : true,
40862     /**
40863      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40864      */
40865     allowZero: true,
40866     /**
40867      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40868      */
40869     minValue : Number.NEGATIVE_INFINITY,
40870     /**
40871      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40872      */
40873     maxValue : Number.MAX_VALUE,
40874     /**
40875      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40876      */
40877     minText : "The minimum value for this field is {0}",
40878     /**
40879      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40880      */
40881     maxText : "The maximum value for this field is {0}",
40882     /**
40883      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40884      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40885      */
40886     nanText : "{0} is not a valid number",
40887     /**
40888      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40889      */
40890     castInt : true,
40891     /**
40892      * @cfg {String} defaults currency of the MoneyField
40893      * value should be in lkey
40894      */
40895     defaultCurrency : false,
40896     /**
40897      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40898      */
40899     thousandsDelimiter : false,
40900     /**
40901      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40902      */
40903     max_length: false,
40904     
40905     inputlg : 9,
40906     inputmd : 9,
40907     inputsm : 9,
40908     inputxs : 6,
40909     
40910     store : false,
40911     
40912     getAutoCreate : function()
40913     {
40914         var align = this.labelAlign || this.parentLabelAlign();
40915         
40916         var id = Roo.id();
40917
40918         var cfg = {
40919             cls: 'form-group',
40920             cn: []
40921         };
40922
40923         var input =  {
40924             tag: 'input',
40925             id : id,
40926             cls : 'form-control roo-money-amount-input',
40927             autocomplete: 'new-password'
40928         };
40929         
40930         var hiddenInput = {
40931             tag: 'input',
40932             type: 'hidden',
40933             id: Roo.id(),
40934             cls: 'hidden-number-input'
40935         };
40936         
40937         if(this.max_length) {
40938             input.maxlength = this.max_length; 
40939         }
40940         
40941         if (this.name) {
40942             hiddenInput.name = this.name;
40943         }
40944
40945         if (this.disabled) {
40946             input.disabled = true;
40947         }
40948
40949         var clg = 12 - this.inputlg;
40950         var cmd = 12 - this.inputmd;
40951         var csm = 12 - this.inputsm;
40952         var cxs = 12 - this.inputxs;
40953         
40954         var container = {
40955             tag : 'div',
40956             cls : 'row roo-money-field',
40957             cn : [
40958                 {
40959                     tag : 'div',
40960                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40961                     cn : [
40962                         {
40963                             tag : 'div',
40964                             cls: 'roo-select2-container input-group',
40965                             cn: [
40966                                 {
40967                                     tag : 'input',
40968                                     cls : 'form-control roo-money-currency-input',
40969                                     autocomplete: 'new-password',
40970                                     readOnly : 1,
40971                                     name : this.currencyName
40972                                 },
40973                                 {
40974                                     tag :'span',
40975                                     cls : 'input-group-addon',
40976                                     cn : [
40977                                         {
40978                                             tag: 'span',
40979                                             cls: 'caret'
40980                                         }
40981                                     ]
40982                                 }
40983                             ]
40984                         }
40985                     ]
40986                 },
40987                 {
40988                     tag : 'div',
40989                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40990                     cn : [
40991                         {
40992                             tag: 'div',
40993                             cls: this.hasFeedback ? 'has-feedback' : '',
40994                             cn: [
40995                                 input
40996                             ]
40997                         }
40998                     ]
40999                 }
41000             ]
41001             
41002         };
41003         
41004         if (this.fieldLabel.length) {
41005             var indicator = {
41006                 tag: 'i',
41007                 tooltip: 'This field is required'
41008             };
41009
41010             var label = {
41011                 tag: 'label',
41012                 'for':  id,
41013                 cls: 'control-label',
41014                 cn: []
41015             };
41016
41017             var label_text = {
41018                 tag: 'span',
41019                 html: this.fieldLabel
41020             };
41021
41022             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41023             label.cn = [
41024                 indicator,
41025                 label_text
41026             ];
41027
41028             if(this.indicatorpos == 'right') {
41029                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41030                 label.cn = [
41031                     label_text,
41032                     indicator
41033                 ];
41034             }
41035
41036             if(align == 'left') {
41037                 container = {
41038                     tag: 'div',
41039                     cn: [
41040                         container
41041                     ]
41042                 };
41043
41044                 if(this.labelWidth > 12){
41045                     label.style = "width: " + this.labelWidth + 'px';
41046                 }
41047                 if(this.labelWidth < 13 && this.labelmd == 0){
41048                     this.labelmd = this.labelWidth;
41049                 }
41050                 if(this.labellg > 0){
41051                     label.cls += ' col-lg-' + this.labellg;
41052                     input.cls += ' col-lg-' + (12 - this.labellg);
41053                 }
41054                 if(this.labelmd > 0){
41055                     label.cls += ' col-md-' + this.labelmd;
41056                     container.cls += ' col-md-' + (12 - this.labelmd);
41057                 }
41058                 if(this.labelsm > 0){
41059                     label.cls += ' col-sm-' + this.labelsm;
41060                     container.cls += ' col-sm-' + (12 - this.labelsm);
41061                 }
41062                 if(this.labelxs > 0){
41063                     label.cls += ' col-xs-' + this.labelxs;
41064                     container.cls += ' col-xs-' + (12 - this.labelxs);
41065                 }
41066             }
41067         }
41068
41069         cfg.cn = [
41070             label,
41071             container,
41072             hiddenInput
41073         ];
41074         
41075         var settings = this;
41076
41077         ['xs','sm','md','lg'].map(function(size){
41078             if (settings[size]) {
41079                 cfg.cls += ' col-' + size + '-' + settings[size];
41080             }
41081         });
41082         
41083         return cfg;
41084     },
41085     
41086     initEvents : function()
41087     {
41088         this.indicator = this.indicatorEl();
41089         
41090         this.initCurrencyEvent();
41091         
41092         this.initNumberEvent();
41093     },
41094     
41095     initCurrencyEvent : function()
41096     {
41097         if (!this.store) {
41098             throw "can not find store for combo";
41099         }
41100         
41101         this.store = Roo.factory(this.store, Roo.data);
41102         this.store.parent = this;
41103         
41104         this.createList();
41105         
41106         this.triggerEl = this.el.select('.input-group-addon', true).first();
41107         
41108         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41109         
41110         var _this = this;
41111         
41112         (function(){
41113             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41114             _this.list.setWidth(lw);
41115         }).defer(100);
41116         
41117         this.list.on('mouseover', this.onViewOver, this);
41118         this.list.on('mousemove', this.onViewMove, this);
41119         this.list.on('scroll', this.onViewScroll, this);
41120         
41121         if(!this.tpl){
41122             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41123         }
41124         
41125         this.view = new Roo.View(this.list, this.tpl, {
41126             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41127         });
41128         
41129         this.view.on('click', this.onViewClick, this);
41130         
41131         this.store.on('beforeload', this.onBeforeLoad, this);
41132         this.store.on('load', this.onLoad, this);
41133         this.store.on('loadexception', this.onLoadException, this);
41134         
41135         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41136             "up" : function(e){
41137                 this.inKeyMode = true;
41138                 this.selectPrev();
41139             },
41140
41141             "down" : function(e){
41142                 if(!this.isExpanded()){
41143                     this.onTriggerClick();
41144                 }else{
41145                     this.inKeyMode = true;
41146                     this.selectNext();
41147                 }
41148             },
41149
41150             "enter" : function(e){
41151                 this.collapse();
41152                 
41153                 if(this.fireEvent("specialkey", this, e)){
41154                     this.onViewClick(false);
41155                 }
41156                 
41157                 return true;
41158             },
41159
41160             "esc" : function(e){
41161                 this.collapse();
41162             },
41163
41164             "tab" : function(e){
41165                 this.collapse();
41166                 
41167                 if(this.fireEvent("specialkey", this, e)){
41168                     this.onViewClick(false);
41169                 }
41170                 
41171                 return true;
41172             },
41173
41174             scope : this,
41175
41176             doRelay : function(foo, bar, hname){
41177                 if(hname == 'down' || this.scope.isExpanded()){
41178                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41179                 }
41180                 return true;
41181             },
41182
41183             forceKeyDown: true
41184         });
41185         
41186         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41187         
41188     },
41189     
41190     initNumberEvent : function(e)
41191     {
41192         this.inputEl().on("keydown" , this.fireKey,  this);
41193         this.inputEl().on("focus", this.onFocus,  this);
41194         this.inputEl().on("blur", this.onBlur,  this);
41195         
41196         this.inputEl().relayEvent('keyup', this);
41197         
41198         if(this.indicator){
41199             this.indicator.addClass('invisible');
41200         }
41201  
41202         this.originalValue = this.getValue();
41203         
41204         if(this.validationEvent == 'keyup'){
41205             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41206             this.inputEl().on('keyup', this.filterValidation, this);
41207         }
41208         else if(this.validationEvent !== false){
41209             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41210         }
41211         
41212         if(this.selectOnFocus){
41213             this.on("focus", this.preFocus, this);
41214             
41215         }
41216         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41217             this.inputEl().on("keypress", this.filterKeys, this);
41218         } else {
41219             this.inputEl().relayEvent('keypress', this);
41220         }
41221         
41222         var allowed = "0123456789";
41223         
41224         if(this.allowDecimals){
41225             allowed += this.decimalSeparator;
41226         }
41227         
41228         if(this.allowNegative){
41229             allowed += "-";
41230         }
41231         
41232         if(this.thousandsDelimiter) {
41233             allowed += ",";
41234         }
41235         
41236         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41237         
41238         var keyPress = function(e){
41239             
41240             var k = e.getKey();
41241             
41242             var c = e.getCharCode();
41243             
41244             if(
41245                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41246                     allowed.indexOf(String.fromCharCode(c)) === -1
41247             ){
41248                 e.stopEvent();
41249                 return;
41250             }
41251             
41252             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41253                 return;
41254             }
41255             
41256             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41257                 e.stopEvent();
41258             }
41259         };
41260         
41261         this.inputEl().on("keypress", keyPress, this);
41262         
41263     },
41264     
41265     onTriggerClick : function(e)
41266     {   
41267         if(this.disabled){
41268             return;
41269         }
41270         
41271         this.page = 0;
41272         this.loadNext = false;
41273         
41274         if(this.isExpanded()){
41275             this.collapse();
41276             return;
41277         }
41278         
41279         this.hasFocus = true;
41280         
41281         if(this.triggerAction == 'all') {
41282             this.doQuery(this.allQuery, true);
41283             return;
41284         }
41285         
41286         this.doQuery(this.getRawValue());
41287     },
41288     
41289     getCurrency : function()
41290     {   
41291         var v = this.currencyEl().getValue();
41292         
41293         return v;
41294     },
41295     
41296     restrictHeight : function()
41297     {
41298         this.list.alignTo(this.currencyEl(), this.listAlign);
41299         this.list.alignTo(this.currencyEl(), this.listAlign);
41300     },
41301     
41302     onViewClick : function(view, doFocus, el, e)
41303     {
41304         var index = this.view.getSelectedIndexes()[0];
41305         
41306         var r = this.store.getAt(index);
41307         
41308         if(r){
41309             this.onSelect(r, index);
41310         }
41311     },
41312     
41313     onSelect : function(record, index){
41314         
41315         if(this.fireEvent('beforeselect', this, record, index) !== false){
41316         
41317             this.setFromCurrencyData(index > -1 ? record.data : false);
41318             
41319             this.collapse();
41320             
41321             this.fireEvent('select', this, record, index);
41322         }
41323     },
41324     
41325     setFromCurrencyData : function(o)
41326     {
41327         var currency = '';
41328         
41329         this.lastCurrency = o;
41330         
41331         if (this.currencyField) {
41332             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41333         } else {
41334             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41335         }
41336         
41337         this.lastSelectionText = currency;
41338         
41339         //setting default currency
41340         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41341             this.setCurrency(this.defaultCurrency);
41342             return;
41343         }
41344         
41345         this.setCurrency(currency);
41346     },
41347     
41348     setFromData : function(o)
41349     {
41350         var c = {};
41351         
41352         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41353         
41354         this.setFromCurrencyData(c);
41355         
41356         var value = '';
41357         
41358         if (this.name) {
41359             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41360         } else {
41361             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41362         }
41363         
41364         this.setValue(value);
41365         
41366     },
41367     
41368     setCurrency : function(v)
41369     {   
41370         this.currencyValue = v;
41371         
41372         if(this.rendered){
41373             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41374             this.validate();
41375         }
41376     },
41377     
41378     setValue : function(v)
41379     {
41380         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41381         
41382         this.value = v;
41383         
41384         if(this.rendered){
41385             
41386             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41387             
41388             this.inputEl().dom.value = (v == '') ? '' :
41389                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41390             
41391             if(!this.allowZero && v === '0') {
41392                 this.hiddenEl().dom.value = '';
41393                 this.inputEl().dom.value = '';
41394             }
41395             
41396             this.validate();
41397         }
41398     },
41399     
41400     getRawValue : function()
41401     {
41402         var v = this.inputEl().getValue();
41403         
41404         return v;
41405     },
41406     
41407     getValue : function()
41408     {
41409         return this.fixPrecision(this.parseValue(this.getRawValue()));
41410     },
41411     
41412     parseValue : function(value)
41413     {
41414         if(this.thousandsDelimiter) {
41415             value += "";
41416             r = new RegExp(",", "g");
41417             value = value.replace(r, "");
41418         }
41419         
41420         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41421         return isNaN(value) ? '' : value;
41422         
41423     },
41424     
41425     fixPrecision : function(value)
41426     {
41427         if(this.thousandsDelimiter) {
41428             value += "";
41429             r = new RegExp(",", "g");
41430             value = value.replace(r, "");
41431         }
41432         
41433         var nan = isNaN(value);
41434         
41435         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41436             return nan ? '' : value;
41437         }
41438         return parseFloat(value).toFixed(this.decimalPrecision);
41439     },
41440     
41441     decimalPrecisionFcn : function(v)
41442     {
41443         return Math.floor(v);
41444     },
41445     
41446     validateValue : function(value)
41447     {
41448         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41449             return false;
41450         }
41451         
41452         var num = this.parseValue(value);
41453         
41454         if(isNaN(num)){
41455             this.markInvalid(String.format(this.nanText, value));
41456             return false;
41457         }
41458         
41459         if(num < this.minValue){
41460             this.markInvalid(String.format(this.minText, this.minValue));
41461             return false;
41462         }
41463         
41464         if(num > this.maxValue){
41465             this.markInvalid(String.format(this.maxText, this.maxValue));
41466             return false;
41467         }
41468         
41469         return true;
41470     },
41471     
41472     validate : function()
41473     {
41474         if(this.disabled || this.allowBlank){
41475             this.markValid();
41476             return true;
41477         }
41478         
41479         var currency = this.getCurrency();
41480         
41481         if(this.validateValue(this.getRawValue()) && currency.length){
41482             this.markValid();
41483             return true;
41484         }
41485         
41486         this.markInvalid();
41487         return false;
41488     },
41489     
41490     getName: function()
41491     {
41492         return this.name;
41493     },
41494     
41495     beforeBlur : function()
41496     {
41497         if(!this.castInt){
41498             return;
41499         }
41500         
41501         var v = this.parseValue(this.getRawValue());
41502         
41503         if(v || v == 0){
41504             this.setValue(v);
41505         }
41506     },
41507     
41508     onBlur : function()
41509     {
41510         this.beforeBlur();
41511         
41512         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41513             //this.el.removeClass(this.focusClass);
41514         }
41515         
41516         this.hasFocus = false;
41517         
41518         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41519             this.validate();
41520         }
41521         
41522         var v = this.getValue();
41523         
41524         if(String(v) !== String(this.startValue)){
41525             this.fireEvent('change', this, v, this.startValue);
41526         }
41527         
41528         this.fireEvent("blur", this);
41529     },
41530     
41531     inputEl : function()
41532     {
41533         return this.el.select('.roo-money-amount-input', true).first();
41534     },
41535     
41536     currencyEl : function()
41537     {
41538         return this.el.select('.roo-money-currency-input', true).first();
41539     },
41540     
41541     hiddenEl : function()
41542     {
41543         return this.el.select('input.hidden-number-input',true).first();
41544     }
41545     
41546 });/**
41547 *    This script refer to:
41548 *    Title: Signature Pad
41549 *    Author: szimek
41550 *    Availability: https://github.com/szimek/signature_pad
41551 **/
41552
41553 /**
41554  * @class Roo.bootstrap.BezierSignature
41555  * @extends Roo.bootstrap.Component
41556  * Bootstrap BezierSignature class
41557  * 
41558  * @constructor
41559  * Create a new BezierSignature
41560  * @param {Object} config The config object
41561  */
41562
41563 Roo.bootstrap.BezierSignature = function(config){
41564     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41565     this.addEvents({
41566         "resize" : true
41567     });
41568 };
41569
41570 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,  {
41571     
41572     curve_data: [],
41573     
41574     is_empty: true,
41575     
41576     mouse_btn_down: true,
41577     
41578     /**
41579      * @cfg(int) canvas height
41580      */
41581     canvas_height: '200px',
41582     
41583     /**
41584      * @cfg(float or function) Radius of a single dot.
41585      */ 
41586     dot_size: false,
41587     
41588     /**
41589      * @cfg(float) Minimum width of a line. Defaults to 0.5.
41590      */
41591     min_width: 0.5,
41592     
41593     /**
41594      * @cfg(float) Maximum width of a line. Defaults to 2.5.
41595      */
41596     max_width: 2.5,
41597     
41598     /**
41599      * @cfg(integer) Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41600      */
41601     throttle: 16,
41602     
41603     /**
41604      * @cfg(integer) Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41605      */
41606     min_distance: 5,
41607     
41608     /**
41609      * @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.
41610      */
41611     bg_color: 'rgba(0, 0, 0, 0)',
41612     
41613     /**
41614      * @cfg(string) Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41615      */
41616     dot_color: 'black',
41617     
41618     /**
41619      * @cfg(float) Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41620      */
41621     velocity_filter_weight: 0.7,
41622     
41623     /**
41624      * @cfg(function) Callback when stroke begin.
41625      */
41626     onBegin: false,
41627     
41628     /**
41629      * @cfg(function) Callback when stroke end.
41630      */
41631     onEnd: false,
41632     
41633     getAutoCreate : function()
41634     {
41635         var cls = 'roo-signature column';
41636         
41637         if(this.cls){
41638             cls += ' ' + this.cls;
41639         }
41640         
41641         var col_sizes = [
41642             'lg',
41643             'md',
41644             'sm',
41645             'xs'
41646         ];
41647         
41648         for(var i = 0; i < col_sizes.length; i++) {
41649             if(this[col_sizes[i]]) {
41650                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41651             }
41652         }
41653         
41654         var cfg = {
41655             tag: 'div',
41656             cls: cls,
41657             cn: [
41658                 {
41659                     tag: 'div',
41660                     cls: 'roo-signature-body',
41661                     cn: [
41662                         {
41663                             tag: 'canvas',
41664                             cls: 'roo-signature-body-canvas',
41665                             height: this.canvas_height,
41666                             width: this.canvas_width
41667                         }
41668                     ]
41669                 },
41670                 {
41671                     tag: 'input',
41672                     type: 'file',
41673                     style: 'display: none'
41674                 }
41675             ]
41676         };
41677         
41678         return cfg;
41679     },
41680     
41681     initEvents: function() 
41682     {
41683         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41684         
41685         var canvas = this.canvasEl();
41686         
41687         // mouse && touch event swapping...
41688         canvas.dom.style.touchAction = 'none';
41689         canvas.dom.style.msTouchAction = 'none';
41690         
41691         this.mouse_btn_down = false;
41692         canvas.on('mousedown', this._handleMouseDown, this);
41693         canvas.on('mousemove', this._handleMouseMove, this);
41694         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41695         
41696         if (window.PointerEvent) {
41697             canvas.on('pointerdown', this._handleMouseDown, this);
41698             canvas.on('pointermove', this._handleMouseMove, this);
41699             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41700         }
41701         
41702         if ('ontouchstart' in window) {
41703             canvas.on('touchstart', this._handleTouchStart, this);
41704             canvas.on('touchmove', this._handleTouchMove, this);
41705             canvas.on('touchend', this._handleTouchEnd, this);
41706         }
41707         
41708         Roo.EventManager.onWindowResize(this.resize, this, true);
41709         
41710         // file input event
41711         this.fileEl().on('change', this.uploadImage, this);
41712         
41713         this.clear();
41714         
41715         this.resize();
41716     },
41717     
41718     resize: function(){
41719         
41720         var canvas = this.canvasEl().dom;
41721         var ctx = this.canvasElCtx();
41722         var img_data = false;
41723         
41724         if(canvas.width > 0) {
41725             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41726         }
41727         // setting canvas width will clean img data
41728         canvas.width = 0;
41729         
41730         var style = window.getComputedStyle ? 
41731             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41732             
41733         var padding_left = parseInt(style.paddingLeft) || 0;
41734         var padding_right = parseInt(style.paddingRight) || 0;
41735         
41736         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41737         
41738         if(img_data) {
41739             ctx.putImageData(img_data, 0, 0);
41740         }
41741     },
41742     
41743     _handleMouseDown: function(e)
41744     {
41745         if (e.browserEvent.which === 1) {
41746             this.mouse_btn_down = true;
41747             this.strokeBegin(e);
41748         }
41749     },
41750     
41751     _handleMouseMove: function (e)
41752     {
41753         if (this.mouse_btn_down) {
41754             this.strokeMoveUpdate(e);
41755         }
41756     },
41757     
41758     _handleMouseUp: function (e)
41759     {
41760         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41761             this.mouse_btn_down = false;
41762             this.strokeEnd(e);
41763         }
41764     },
41765     
41766     _handleTouchStart: function (e) {
41767         
41768         e.preventDefault();
41769         if (e.browserEvent.targetTouches.length === 1) {
41770             // var touch = e.browserEvent.changedTouches[0];
41771             // this.strokeBegin(touch);
41772             
41773              this.strokeBegin(e); // assume e catching the correct xy...
41774         }
41775     },
41776     
41777     _handleTouchMove: function (e) {
41778         e.preventDefault();
41779         // var touch = event.targetTouches[0];
41780         // _this._strokeMoveUpdate(touch);
41781         this.strokeMoveUpdate(e);
41782     },
41783     
41784     _handleTouchEnd: function (e) {
41785         var wasCanvasTouched = e.target === this.canvasEl().dom;
41786         if (wasCanvasTouched) {
41787             e.preventDefault();
41788             // var touch = event.changedTouches[0];
41789             // _this._strokeEnd(touch);
41790             this.strokeEnd(e);
41791         }
41792     },
41793     
41794     reset: function () {
41795         this._lastPoints = [];
41796         this._lastVelocity = 0;
41797         this._lastWidth = (this.min_width + this.max_width) / 2;
41798         this.canvasElCtx().fillStyle = this.dot_color;
41799     },
41800     
41801     strokeMoveUpdate: function(e)
41802     {
41803         this.strokeUpdate(e);
41804         
41805         if (this.throttle) {
41806             this.throttle(this.strokeUpdate, this.throttle);
41807         }
41808         else {
41809             this.strokeUpdate(e);
41810         }
41811     },
41812     
41813     strokeBegin: function(e)
41814     {
41815         var newPointGroup = {
41816             color: this.dot_color,
41817             points: []
41818         };
41819         
41820         if (typeof this.onBegin === 'function') {
41821             this.onBegin(e);
41822         }
41823         
41824         this.curve_data.push(newPointGroup);
41825         this.reset();
41826         this.strokeUpdate(e);
41827     },
41828     
41829     strokeUpdate: function(e)
41830     {
41831         var rect = this.canvasEl().dom.getBoundingClientRect();
41832         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41833         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41834         var lastPoints = lastPointGroup.points;
41835         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41836         var isLastPointTooClose = lastPoint
41837             ? point.distanceTo(lastPoint) <= this.min_distance
41838             : false;
41839         var color = lastPointGroup.color;
41840         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41841             var curve = this.addPoint(point);
41842             if (!lastPoint) {
41843                 this.drawDot({color: color, point: point});
41844             }
41845             else if (curve) {
41846                 this.drawCurve({color: color, curve: curve});
41847             }
41848             lastPoints.push({
41849                 time: point.time,
41850                 x: point.x,
41851                 y: point.y
41852             });
41853         }
41854     },
41855     
41856     strokeEnd: function(e)
41857     {
41858         this.strokeUpdate(e);
41859         if (typeof this.onEnd === 'function') {
41860             this.onEnd(e);
41861         }
41862     },
41863     
41864     addPoint:  function (point) {
41865         var _lastPoints = this._lastPoints;
41866         _lastPoints.push(point);
41867         if (_lastPoints.length > 2) {
41868             if (_lastPoints.length === 3) {
41869                 _lastPoints.unshift(_lastPoints[0]);
41870             }
41871             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41872             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41873             _lastPoints.shift();
41874             return curve;
41875         }
41876         return null;
41877     },
41878     
41879     calculateCurveWidths: function (startPoint, endPoint) {
41880         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41881             (1 - this.velocity_filter_weight) * this._lastVelocity;
41882
41883         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41884         var widths = {
41885             end: newWidth,
41886             start: this._lastWidth
41887         };
41888         
41889         this._lastVelocity = velocity;
41890         this._lastWidth = newWidth;
41891         return widths;
41892     },
41893     
41894     drawDot: function (_a) {
41895         var color = _a.color, point = _a.point;
41896         var ctx = this.canvasElCtx();
41897         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41898         ctx.beginPath();
41899         this.drawCurveSegment(point.x, point.y, width);
41900         ctx.closePath();
41901         ctx.fillStyle = color;
41902         ctx.fill();
41903     },
41904     
41905     drawCurve: function (_a) {
41906         var color = _a.color, curve = _a.curve;
41907         var ctx = this.canvasElCtx();
41908         var widthDelta = curve.endWidth - curve.startWidth;
41909         var drawSteps = Math.floor(curve.length()) * 2;
41910         ctx.beginPath();
41911         ctx.fillStyle = color;
41912         for (var i = 0; i < drawSteps; i += 1) {
41913         var t = i / drawSteps;
41914         var tt = t * t;
41915         var ttt = tt * t;
41916         var u = 1 - t;
41917         var uu = u * u;
41918         var uuu = uu * u;
41919         var x = uuu * curve.startPoint.x;
41920         x += 3 * uu * t * curve.control1.x;
41921         x += 3 * u * tt * curve.control2.x;
41922         x += ttt * curve.endPoint.x;
41923         var y = uuu * curve.startPoint.y;
41924         y += 3 * uu * t * curve.control1.y;
41925         y += 3 * u * tt * curve.control2.y;
41926         y += ttt * curve.endPoint.y;
41927         var width = curve.startWidth + ttt * widthDelta;
41928         this.drawCurveSegment(x, y, width);
41929         }
41930         ctx.closePath();
41931         ctx.fill();
41932     },
41933     
41934     drawCurveSegment: function (x, y, width) {
41935         var ctx = this.canvasElCtx();
41936         ctx.moveTo(x, y);
41937         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41938         this.is_empty = false;
41939     },
41940     
41941     clear: function()
41942     {
41943         var ctx = this.canvasElCtx();
41944         var canvas = this.canvasEl().dom;
41945         ctx.fillStyle = this.bg_color;
41946         ctx.clearRect(0, 0, canvas.width, canvas.height);
41947         ctx.fillRect(0, 0, canvas.width, canvas.height);
41948         this.curve_data = [];
41949         this.reset();
41950         this.is_empty = true;
41951     },
41952     
41953     fileEl: function()
41954     {
41955         return  this.el.select('input',true).first();
41956     },
41957     
41958     canvasEl: function()
41959     {
41960         return this.el.select('canvas',true).first();
41961     },
41962     
41963     canvasElCtx: function()
41964     {
41965         return this.el.select('canvas',true).first().dom.getContext('2d');
41966     },
41967     
41968     getImage: function(type)
41969     {
41970         if(this.is_empty) {
41971             return false;
41972         }
41973         
41974         // encryption ?
41975         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41976     },
41977     
41978     drawFromImage: function(img_src)
41979     {
41980         var img = new Image();
41981         
41982         img.onload = function(){
41983             this.canvasElCtx().drawImage(img, 0, 0);
41984         }.bind(this);
41985         
41986         img.src = img_src;
41987         
41988         this.is_empty = false;
41989     },
41990     
41991     selectImage: function()
41992     {
41993         this.fileEl().dom.click();
41994     },
41995     
41996     uploadImage: function(e)
41997     {
41998         var reader = new FileReader();
41999         
42000         reader.onload = function(e){
42001             var img = new Image();
42002             img.onload = function(){
42003                 this.reset();
42004                 this.canvasElCtx().drawImage(img, 0, 0);
42005             }.bind(this);
42006             img.src = e.target.result;
42007         }.bind(this);
42008         
42009         reader.readAsDataURL(e.target.files[0]);
42010     },
42011     
42012     // Bezier Point Constructor
42013     Point: (function () {
42014         function Point(x, y, time) {
42015             this.x = x;
42016             this.y = y;
42017             this.time = time || Date.now();
42018         }
42019         Point.prototype.distanceTo = function (start) {
42020             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42021         };
42022         Point.prototype.equals = function (other) {
42023             return this.x === other.x && this.y === other.y && this.time === other.time;
42024         };
42025         Point.prototype.velocityFrom = function (start) {
42026             return this.time !== start.time
42027             ? this.distanceTo(start) / (this.time - start.time)
42028             : 0;
42029         };
42030         return Point;
42031     }()),
42032     
42033     
42034     // Bezier Constructor
42035     Bezier: (function () {
42036         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42037             this.startPoint = startPoint;
42038             this.control2 = control2;
42039             this.control1 = control1;
42040             this.endPoint = endPoint;
42041             this.startWidth = startWidth;
42042             this.endWidth = endWidth;
42043         }
42044         Bezier.fromPoints = function (points, widths, scope) {
42045             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42046             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42047             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42048         };
42049         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42050             var dx1 = s1.x - s2.x;
42051             var dy1 = s1.y - s2.y;
42052             var dx2 = s2.x - s3.x;
42053             var dy2 = s2.y - s3.y;
42054             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42055             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42056             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42057             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42058             var dxm = m1.x - m2.x;
42059             var dym = m1.y - m2.y;
42060             var k = l2 / (l1 + l2);
42061             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42062             var tx = s2.x - cm.x;
42063             var ty = s2.y - cm.y;
42064             return {
42065                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42066                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42067             };
42068         };
42069         Bezier.prototype.length = function () {
42070             var steps = 10;
42071             var length = 0;
42072             var px;
42073             var py;
42074             for (var i = 0; i <= steps; i += 1) {
42075                 var t = i / steps;
42076                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42077                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42078                 if (i > 0) {
42079                     var xdiff = cx - px;
42080                     var ydiff = cy - py;
42081                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42082                 }
42083                 px = cx;
42084                 py = cy;
42085             }
42086             return length;
42087         };
42088         Bezier.prototype.point = function (t, start, c1, c2, end) {
42089             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42090             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42091             + (3.0 * c2 * (1.0 - t) * t * t)
42092             + (end * t * t * t);
42093         };
42094         return Bezier;
42095     }()),
42096     
42097     throttle: function(fn, wait) {
42098       if (wait === void 0) { wait = 250; }
42099       var previous = 0;
42100       var timeout = null;
42101       var result;
42102       var storedContext;
42103       var storedArgs;
42104       var later = function () {
42105           previous = Date.now();
42106           timeout = null;
42107           result = fn.apply(storedContext, storedArgs);
42108           if (!timeout) {
42109               storedContext = null;
42110               storedArgs = [];
42111           }
42112       };
42113       return function wrapper() {
42114           var args = [];
42115           for (var _i = 0; _i < arguments.length; _i++) {
42116               args[_i] = arguments[_i];
42117           }
42118           var now = Date.now();
42119           var remaining = wait - (now - previous);
42120           storedContext = this;
42121           storedArgs = args;
42122           if (remaining <= 0 || remaining > wait) {
42123               if (timeout) {
42124                   clearTimeout(timeout);
42125                   timeout = null;
42126               }
42127               previous = now;
42128               result = fn.apply(storedContext, storedArgs);
42129               if (!timeout) {
42130                   storedContext = null;
42131                   storedArgs = [];
42132               }
42133           }
42134           else if (!timeout) {
42135               timeout = window.setTimeout(later, remaining);
42136           }
42137           return result;
42138       };
42139   }
42140   
42141 });
42142
42143  
42144
42145