Roo/bootstrap/Component.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174     /**
175      * This is really a wrapper for addxtypeChild
176      * it handles stuff relating to flexy:foreach / flexy:if
177      * = some of our projects use a flat rendering of the output, and try and overlay it with dynamic data.
178      *  -- this is a bit of a nightmare... and is even more confusing to debug..
179      *
180      *  
181      *
182      */
183     addxtype  : function(tree,cntr)
184     {
185         var cn = this;
186         
187         cn = Roo.factory(tree);
188         //Roo.log(['addxtype', cn]);
189            
190         cn.parentType = this.xtype; //??
191         cn.parentId = this.id;
192         
193         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
194         if (typeof(cn.container_method) == 'string') {
195             cntr = cn.container_method;
196         }
197         
198         
199         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
200         
201         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
202         
203         var build_from_html =  Roo.XComponent.build_from_html;
204           
205         var is_body  = (tree.xtype == 'Body') ;
206           
207         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
208           
209         var self_cntr_el = Roo.get(this[cntr](false));
210         
211         // do not try and build conditional elements 
212         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
213             return false;
214         }
215         
216         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
217             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
218                 return this.addxtypeChild(tree,cntr, is_body);
219             }
220             
221             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
222                 
223             if(echild){
224                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
225             }
226             
227             Roo.log('skipping render');
228             return cn;
229             
230         }
231         
232         var ret = false;
233         if (!build_from_html) {
234             return false;
235         }
236         
237         // this i think handles overlaying multiple children of the same type
238         // with the sam eelement.. - which might be buggy..
239         while (true) {
240             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
241             
242             if (!echild) {
243                 break;
244             }
245             
246             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
247                 break;
248             }
249             
250             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
251         }
252        
253         return ret;
254     },
255     /**
256      * add a child to this element
257      *   - turn the child.cfg into a child_instance
258      *   - call child_instance.render( this { getContainerMethod()} )
259      *   - loop through the children, and call addxtype.. (reall this) on newly created child.
260      *  
261      */
262     
263     addxtypeChild : function (tree, cntr, is_body)
264     {
265         Roo.debug && Roo.log('addxtypeChild:' + cntr);
266         var cn = this;
267         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
268         
269         
270         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
271                     (typeof(tree['flexy:foreach']) != 'undefined');
272           
273     
274         
275         skip_children = false;
276         // render the element if it's not BODY.
277         if (!is_body) {
278             
279             // if parent was disabled, then do not try and create the children..
280             if(!this[cntr](true)){
281                 tree.items = [];
282                 return tree;
283             }
284            
285             cn = Roo.factory(tree);
286            
287             cn.parentType = this.xtype; //??
288             cn.parentId = this.id;
289             
290             var build_from_html =  Roo.XComponent.build_from_html;
291             
292             
293             // does the container contain child eleemnts with 'xtype' attributes.
294             // that match this xtype..
295             // note - when we render we create these as well..
296             // so we should check to see if body has xtype set.
297             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
298                
299                 var self_cntr_el = Roo.get(this[cntr](false));
300                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
301                 if (echild) { 
302                     //Roo.log(Roo.XComponent.build_from_html);
303                     //Roo.log("got echild:");
304                     //Roo.log(echild);
305                 }
306                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
307                 // and are not displayed -this causes this to use up the wrong element when matching.
308                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
309                 
310                 
311                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
312                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
313                   
314                   
315                   
316                     cn.el = echild;
317                   //  Roo.log("GOT");
318                     //echild.dom.removeAttribute('xtype');
319                 } else {
320                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
321                     Roo.debug && Roo.log(self_cntr_el);
322                     Roo.debug && Roo.log(echild);
323                     Roo.debug && Roo.log(cn);
324                 }
325             }
326            
327             
328            
329             // if object has flexy:if - then it may or may not be rendered.
330             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
331                 // skip a flexy if element.
332                 Roo.debug && Roo.log('skipping render');
333                 Roo.debug && Roo.log(tree);
334                 if (!cn.el) {
335                     Roo.debug && Roo.log('skipping all children');
336                     skip_children = true;
337                 }
338                 
339              } else {
340                  
341                 // actually if flexy:foreach is found, we really want to create 
342                 // multiple copies here...
343                 //Roo.log('render');
344                 //Roo.log(this[cntr]());
345                 // some elements do not have render methods.. like the layouts...
346                 /*
347                 if(this[cntr](true) === false){
348                     cn.items = [];
349                     return cn;
350                 }
351                 */
352                 cn.render && cn.render(this[cntr](true));
353                 
354              }
355             // then add the element..
356         }
357         
358         
359         
360         
361         
362         cn.addxtypeChildren(tree.items, skip_children);
363         delete tree.items;
364         return cn;
365     },
366     
367     /**
368      *  add a number of children to this object,
369      *     which in turn calls render...
370      *  
371      */
372     
373     addxtypeChildren: function(child_array, skip_children)
374     {
375         var nitems = [];
376         if (!child_array || !child_array.length ) {
377             this.items = nitems;
378             return;
379         }
380         
381         for(var i =0;i < child_array.length;i++) {
382             if (skip_children) {
383                 break;
384             }
385             //  Roo.log(['add child', items[i]]);
386             nitems.push(this.addxtype(Roo.apply({}, child_array[i])));
387         }
388         this.items = nitems;
389         
390         this.fireEvent('childrenrendered', this);
391          
392     },
393     
394     
395     
396     /**
397      * Set the element that will be used to show or hide
398      */
399     setVisibilityEl : function(el)
400     {
401         this.visibilityEl = el;
402     },
403     
404      /**
405      * Get the element that will be used to show or hide
406      */
407     getVisibilityEl : function()
408     {
409         if (typeof(this.visibilityEl) == 'object') {
410             return this.visibilityEl;
411         }
412         
413         if (typeof(this.visibilityEl) == 'string') {
414             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
415         }
416         
417         return this.getEl();
418     },
419     
420     /**
421      * Show a component - removes 'hidden' class
422      */
423     show : function()
424     {
425         if(!this.getVisibilityEl()){
426             return;
427         }
428          
429         this.getVisibilityEl().removeClass(['hidden','d-none']);
430         
431         this.fireEvent('show', this);
432         
433         
434     },
435     /**
436      * Hide a component - adds 'hidden' class
437      */
438     hide: function()
439     {
440         if(!this.getVisibilityEl()){
441             return;
442         }
443         
444         this.getVisibilityEl().addClass(['hidden','d-none']);
445         
446         this.fireEvent('hide', this);
447         
448     }
449 });
450
451  /*
452  * - LGPL
453  *
454  * Body
455  *
456  */
457
458 /**
459  * @class Roo.bootstrap.Body
460  * @extends Roo.bootstrap.Component
461  * Bootstrap Body class
462  *
463  * @constructor
464  * Create a new body
465  * @param {Object} config The config object
466  */
467
468 Roo.bootstrap.Body = function(config){
469
470     config = config || {};
471
472     Roo.bootstrap.Body.superclass.constructor.call(this, config);
473     this.el = Roo.get(config.el ? config.el : document.body );
474     if (this.cls && this.cls.length) {
475         Roo.get(document.body).addClass(this.cls);
476     }
477 };
478
479 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
480
481     is_body : true,// just to make sure it's constructed?
482
483         autoCreate : {
484         cls: 'container'
485     },
486     onRender : function(ct, position)
487     {
488        /* Roo.log("Roo.bootstrap.Body - onRender");
489         if (this.cls && this.cls.length) {
490             Roo.get(document.body).addClass(this.cls);
491         }
492         // style??? xttr???
493         */
494     }
495
496
497
498
499 });
500 /*
501  * - LGPL
502  *
503  * button group
504  * 
505  */
506
507
508 /**
509  * @class Roo.bootstrap.ButtonGroup
510  * @extends Roo.bootstrap.Component
511  * Bootstrap ButtonGroup class
512  * @cfg {String} size lg | sm | xs (default empty normal)
513  * @cfg {String} align vertical | justified  (default none)
514  * @cfg {String} direction up | down (default down)
515  * @cfg {Boolean} toolbar false | true
516  * @cfg {Boolean} btn true | false
517  * 
518  * 
519  * @constructor
520  * Create a new Input
521  * @param {Object} config The config object
522  */
523
524 Roo.bootstrap.ButtonGroup = function(config){
525     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
526 };
527
528 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
529     
530     size: '',
531     align: '',
532     direction: '',
533     toolbar: false,
534     btn: true,
535
536     getAutoCreate : function(){
537         var cfg = {
538             cls: 'btn-group',
539             html : null
540         };
541         
542         cfg.html = this.html || cfg.html;
543         
544         if (this.toolbar) {
545             cfg = {
546                 cls: 'btn-toolbar',
547                 html: null
548             };
549             
550             return cfg;
551         }
552         
553         if (['vertical','justified'].indexOf(this.align)!==-1) {
554             cfg.cls = 'btn-group-' + this.align;
555             
556             if (this.align == 'justified') {
557                 console.log(this.items);
558             }
559         }
560         
561         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
562             cfg.cls += ' btn-group-' + this.size;
563         }
564         
565         if (this.direction == 'up') {
566             cfg.cls += ' dropup' ;
567         }
568         
569         return cfg;
570     },
571     /**
572      * Add a button to the group (similar to NavItem API.)
573      */
574     addItem : function(cfg)
575     {
576         var cn = new Roo.bootstrap.Button(cfg);
577         //this.register(cn);
578         cn.parentId = this.id;
579         cn.onRender(this.el, null);
580         return cn;
581     }
582    
583 });
584
585  /*
586  * - LGPL
587  *
588  * button
589  * 
590  */
591
592 /**
593  * @class Roo.bootstrap.Button
594  * @extends Roo.bootstrap.Component
595  * Bootstrap Button class
596  * @cfg {String} html The button content
597  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
598  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
599  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
600  * @cfg {String} size ( lg | sm | xs)
601  * @cfg {String} tag ( a | input | submit)
602  * @cfg {String} href empty or href
603  * @cfg {Boolean} disabled default false;
604  * @cfg {Boolean} isClose default false;
605  * @cfg {String} glyphicon depricated - use fa
606  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
607  * @cfg {String} badge text for badge
608  * @cfg {String} theme (default|glow)  
609  * @cfg {Boolean} inverse dark themed version
610  * @cfg {Boolean} toggle is it a slidy toggle button
611  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
612  * @cfg {String} ontext text for on slidy toggle state
613  * @cfg {String} offtext text for off slidy toggle state
614  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
615  * @cfg {Boolean} removeClass remove the standard class..
616  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
617  * 
618  * @constructor
619  * Create a new button
620  * @param {Object} config The config object
621  */
622
623
624 Roo.bootstrap.Button = function(config){
625     Roo.bootstrap.Button.superclass.constructor.call(this, config);
626     this.weightClass = ["btn-default btn-outline-secondary", 
627                        "btn-primary", 
628                        "btn-success", 
629                        "btn-info", 
630                        "btn-warning",
631                        "btn-danger",
632                        "btn-link"
633                       ],  
634     this.addEvents({
635         // raw events
636         /**
637          * @event click
638          * When a butotn is pressed
639          * @param {Roo.bootstrap.Button} btn
640          * @param {Roo.EventObject} e
641          */
642         "click" : true,
643          /**
644          * @event toggle
645          * After the button has been toggles
646          * @param {Roo.bootstrap.Button} btn
647          * @param {Roo.EventObject} e
648          * @param {boolean} pressed (also available as button.pressed)
649          */
650         "toggle" : true
651     });
652 };
653
654 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
655     html: false,
656     active: false,
657     weight: '',
658     badge_weight: '',
659     outline : false,
660     size: '',
661     tag: 'button',
662     href: '',
663     disabled: false,
664     isClose: false,
665     glyphicon: '',
666     fa: '',
667     badge: '',
668     theme: 'default',
669     inverse: false,
670     
671     toggle: false,
672     ontext: 'ON',
673     offtext: 'OFF',
674     defaulton: true,
675     preventDefault: true,
676     removeClass: false,
677     name: false,
678     target: false,
679      
680     pressed : null,
681      
682     
683     getAutoCreate : function(){
684         
685         var cfg = {
686             tag : 'button',
687             cls : 'roo-button',
688             html: ''
689         };
690         
691         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
692             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
693             this.tag = 'button';
694         } else {
695             cfg.tag = this.tag;
696         }
697         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
698         
699         if (this.toggle == true) {
700             cfg={
701                 tag: 'div',
702                 cls: 'slider-frame roo-button',
703                 cn: [
704                     {
705                         tag: 'span',
706                         'data-on-text':'ON',
707                         'data-off-text':'OFF',
708                         cls: 'slider-button',
709                         html: this.offtext
710                     }
711                 ]
712             };
713             
714             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
715                 cfg.cls += ' '+this.weight;
716             }
717             
718             return cfg;
719         }
720         
721         if (this.isClose) {
722             cfg.cls += ' close';
723             
724             cfg["aria-hidden"] = true;
725             
726             cfg.html = "&times;";
727             
728             return cfg;
729         }
730         
731          
732         if (this.theme==='default') {
733             cfg.cls = 'btn roo-button';
734             
735             //if (this.parentType != 'Navbar') {
736             this.weight = this.weight.length ?  this.weight : 'default';
737             //}
738             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
739                 
740                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
741                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
742                 cfg.cls += ' btn-' + outline + weight;
743                 if (this.weight == 'default') {
744                     // BC
745                     cfg.cls += ' btn-' + this.weight;
746                 }
747             }
748         } else if (this.theme==='glow') {
749             
750             cfg.tag = 'a';
751             cfg.cls = 'btn-glow roo-button';
752             
753             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
754                 
755                 cfg.cls += ' ' + this.weight;
756             }
757         }
758    
759         
760         if (this.inverse) {
761             this.cls += ' inverse';
762         }
763         
764         
765         if (this.active || this.pressed === true) {
766             cfg.cls += ' active';
767         }
768         
769         if (this.disabled) {
770             cfg.disabled = 'disabled';
771         }
772         
773         if (this.items) {
774             Roo.log('changing to ul' );
775             cfg.tag = 'ul';
776             this.glyphicon = 'caret';
777             if (Roo.bootstrap.version == 4) {
778                 this.fa = 'caret-down';
779             }
780             
781         }
782         
783         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
784          
785         //gsRoo.log(this.parentType);
786         if (this.parentType === 'Navbar' && !this.parent().bar) {
787             Roo.log('changing to li?');
788             
789             cfg.tag = 'li';
790             
791             cfg.cls = '';
792             cfg.cn =  [{
793                 tag : 'a',
794                 cls : 'roo-button',
795                 html : this.html,
796                 href : this.href || '#'
797             }];
798             if (this.menu) {
799                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
800                 cfg.cls += ' dropdown';
801             }   
802             
803             delete cfg.html;
804             
805         }
806         
807        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
808         
809         if (this.glyphicon) {
810             cfg.html = ' ' + cfg.html;
811             
812             cfg.cn = [
813                 {
814                     tag: 'span',
815                     cls: 'glyphicon glyphicon-' + this.glyphicon
816                 }
817             ];
818         }
819         if (this.fa) {
820             cfg.html = ' ' + cfg.html;
821             
822             cfg.cn = [
823                 {
824                     tag: 'i',
825                     cls: 'fa fas fa-' + this.fa
826                 }
827             ];
828         }
829         
830         if (this.badge) {
831             cfg.html += ' ';
832             
833             cfg.tag = 'a';
834             
835 //            cfg.cls='btn roo-button';
836             
837             cfg.href=this.href;
838             
839             var value = cfg.html;
840             
841             if(this.glyphicon){
842                 value = {
843                     tag: 'span',
844                     cls: 'glyphicon glyphicon-' + this.glyphicon,
845                     html: this.html
846                 };
847             }
848             if(this.fa){
849                 value = {
850                     tag: 'i',
851                     cls: 'fa fas fa-' + this.fa,
852                     html: this.html
853                 };
854             }
855             
856             var bw = this.badge_weight.length ? this.badge_weight :
857                 (this.weight.length ? this.weight : 'secondary');
858             bw = bw == 'default' ? 'secondary' : bw;
859             
860             cfg.cn = [
861                 value,
862                 {
863                     tag: 'span',
864                     cls: 'badge badge-' + bw,
865                     html: this.badge
866                 }
867             ];
868             
869             cfg.html='';
870         }
871         
872         if (this.menu) {
873             cfg.cls += ' dropdown';
874             cfg.html = typeof(cfg.html) != 'undefined' ?
875                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
876         }
877         
878         if (cfg.tag !== 'a' && this.href !== '') {
879             throw "Tag must be a to set href.";
880         } else if (this.href.length > 0) {
881             cfg.href = this.href;
882         }
883         
884         if(this.removeClass){
885             cfg.cls = '';
886         }
887         
888         if(this.target){
889             cfg.target = this.target;
890         }
891         
892         return cfg;
893     },
894     initEvents: function() {
895        // Roo.log('init events?');
896 //        Roo.log(this.el.dom);
897         // add the menu...
898         
899         if (typeof (this.menu) != 'undefined') {
900             this.menu.parentType = this.xtype;
901             this.menu.triggerEl = this.el;
902             this.addxtype(Roo.apply({}, this.menu));
903         }
904
905
906        if (this.el.hasClass('roo-button')) {
907             this.el.on('click', this.onClick, this);
908        } else {
909             this.el.select('.roo-button').on('click', this.onClick, this);
910        }
911        
912        if(this.removeClass){
913            this.el.on('click', this.onClick, this);
914        }
915        
916        this.el.enableDisplayMode();
917         
918     },
919     onClick : function(e)
920     {
921         if (this.disabled) {
922             return;
923         }
924         
925         Roo.log('button on click ');
926         if(this.preventDefault){
927             e.preventDefault();
928         }
929         
930         if (this.pressed === true || this.pressed === false) {
931             this.toggleActive(e);
932         }
933         
934         
935         this.fireEvent('click', this, e);
936     },
937     
938     /**
939      * Enables this button
940      */
941     enable : function()
942     {
943         this.disabled = false;
944         this.el.removeClass('disabled');
945     },
946     
947     /**
948      * Disable this button
949      */
950     disable : function()
951     {
952         this.disabled = true;
953         this.el.addClass('disabled');
954     },
955      /**
956      * sets the active state on/off, 
957      * @param {Boolean} state (optional) Force a particular state
958      */
959     setActive : function(v) {
960         
961         this.el[v ? 'addClass' : 'removeClass']('active');
962         this.pressed = v;
963     },
964      /**
965      * toggles the current active state 
966      */
967     toggleActive : function(e)
968     {
969         this.setActive(!this.pressed);
970         this.fireEvent('toggle', this, e, !this.pressed);
971     },
972      /**
973      * get the current active state
974      * @return {boolean} true if it's active
975      */
976     isActive : function()
977     {
978         return this.el.hasClass('active');
979     },
980     /**
981      * set the text of the first selected button
982      */
983     setText : function(str)
984     {
985         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
986     },
987     /**
988      * get the text of the first selected button
989      */
990     getText : function()
991     {
992         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
993     },
994     
995     setWeight : function(str)
996     {
997         this.el.removeClass(this.weightClass);
998         this.weight = str;
999         var outline = this.outline ? 'outline-' : '';
1000         if (str == 'default') {
1001             this.el.addClass('btn-default btn-outline-secondary');        
1002             return;
1003         }
1004         this.el.addClass('btn-' + outline + str);        
1005     }
1006     
1007     
1008 });
1009
1010  /*
1011  * - LGPL
1012  *
1013  * column
1014  * 
1015  */
1016
1017 /**
1018  * @class Roo.bootstrap.Column
1019  * @extends Roo.bootstrap.Component
1020  * Bootstrap Column class
1021  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1022  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1023  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1024  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1025  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1026  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1027  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1028  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1029  *
1030  * 
1031  * @cfg {Boolean} hidden (true|false) hide the element
1032  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1033  * @cfg {String} fa (ban|check|...) font awesome icon
1034  * @cfg {Number} fasize (1|2|....) font awsome size
1035
1036  * @cfg {String} icon (info-sign|check|...) glyphicon name
1037
1038  * @cfg {String} html content of column.
1039  * 
1040  * @constructor
1041  * Create a new Column
1042  * @param {Object} config The config object
1043  */
1044
1045 Roo.bootstrap.Column = function(config){
1046     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1047 };
1048
1049 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1050     
1051     xs: false,
1052     sm: false,
1053     md: false,
1054     lg: false,
1055     xsoff: false,
1056     smoff: false,
1057     mdoff: false,
1058     lgoff: false,
1059     html: '',
1060     offset: 0,
1061     alert: false,
1062     fa: false,
1063     icon : false,
1064     hidden : false,
1065     fasize : 1,
1066     
1067     getAutoCreate : function(){
1068         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1069         
1070         cfg = {
1071             tag: 'div',
1072             cls: 'column'
1073         };
1074         
1075         var settings=this;
1076         ['xs','sm','md','lg'].map(function(size){
1077             //Roo.log( size + ':' + settings[size]);
1078             
1079             if (settings[size+'off'] !== false) {
1080                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1081             }
1082             
1083             if (settings[size] === false) {
1084                 return;
1085             }
1086             
1087             if (!settings[size]) { // 0 = hidden
1088                 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1089                 return;
1090             }
1091             cfg.cls += ' col-' + size + '-' + settings[size] + (
1092                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1093             );
1094             
1095         });
1096         
1097         if (this.hidden) {
1098             cfg.cls += ' hidden';
1099         }
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         
1106         if (this.html.length) {
1107             cfg.html = this.html;
1108         }
1109         if (this.fa) {
1110             var fasize = '';
1111             if (this.fasize > 1) {
1112                 fasize = ' fa-' + this.fasize + 'x';
1113             }
1114             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1115             
1116             
1117         }
1118         if (this.icon) {
1119             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1120         }
1121         
1122         return cfg;
1123     }
1124    
1125 });
1126
1127  
1128
1129  /*
1130  * - LGPL
1131  *
1132  * page container.
1133  * 
1134  */
1135
1136
1137 /**
1138  * @class Roo.bootstrap.Container
1139  * @extends Roo.bootstrap.Component
1140  * Bootstrap Container class
1141  * @cfg {Boolean} jumbotron is it a jumbotron element
1142  * @cfg {String} html content of element
1143  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1144  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1145  * @cfg {String} header content of header (for panel)
1146  * @cfg {String} footer content of footer (for panel)
1147  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1148  * @cfg {String} tag (header|aside|section) type of HTML tag.
1149  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1150  * @cfg {String} fa font awesome icon
1151  * @cfg {String} icon (info-sign|check|...) glyphicon name
1152  * @cfg {Boolean} hidden (true|false) hide the element
1153  * @cfg {Boolean} expandable (true|false) default false
1154  * @cfg {Boolean} expanded (true|false) default true
1155  * @cfg {String} rheader contet on the right of header
1156  * @cfg {Boolean} clickable (true|false) default false
1157
1158  *     
1159  * @constructor
1160  * Create a new Container
1161  * @param {Object} config The config object
1162  */
1163
1164 Roo.bootstrap.Container = function(config){
1165     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1166     
1167     this.addEvents({
1168         // raw events
1169          /**
1170          * @event expand
1171          * After the panel has been expand
1172          * 
1173          * @param {Roo.bootstrap.Container} this
1174          */
1175         "expand" : true,
1176         /**
1177          * @event collapse
1178          * After the panel has been collapsed
1179          * 
1180          * @param {Roo.bootstrap.Container} this
1181          */
1182         "collapse" : true,
1183         /**
1184          * @event click
1185          * When a element is chick
1186          * @param {Roo.bootstrap.Container} this
1187          * @param {Roo.EventObject} e
1188          */
1189         "click" : true
1190     });
1191 };
1192
1193 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1194     
1195     jumbotron : false,
1196     well: '',
1197     panel : '',
1198     header: '',
1199     footer : '',
1200     sticky: '',
1201     tag : false,
1202     alert : false,
1203     fa: false,
1204     icon : false,
1205     expandable : false,
1206     rheader : '',
1207     expanded : true,
1208     clickable: false,
1209   
1210      
1211     getChildContainer : function() {
1212         
1213         if(!this.el){
1214             return false;
1215         }
1216         
1217         if (this.panel.length) {
1218             return this.el.select('.panel-body',true).first();
1219         }
1220         
1221         return this.el;
1222     },
1223     
1224     
1225     getAutoCreate : function(){
1226         
1227         var cfg = {
1228             tag : this.tag || 'div',
1229             html : '',
1230             cls : ''
1231         };
1232         if (this.jumbotron) {
1233             cfg.cls = 'jumbotron';
1234         }
1235         
1236         
1237         
1238         // - this is applied by the parent..
1239         //if (this.cls) {
1240         //    cfg.cls = this.cls + '';
1241         //}
1242         
1243         if (this.sticky.length) {
1244             
1245             var bd = Roo.get(document.body);
1246             if (!bd.hasClass('bootstrap-sticky')) {
1247                 bd.addClass('bootstrap-sticky');
1248                 Roo.select('html',true).setStyle('height', '100%');
1249             }
1250              
1251             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1252         }
1253         
1254         
1255         if (this.well.length) {
1256             switch (this.well) {
1257                 case 'lg':
1258                 case 'sm':
1259                     cfg.cls +=' well well-' +this.well;
1260                     break;
1261                 default:
1262                     cfg.cls +=' well';
1263                     break;
1264             }
1265         }
1266         
1267         if (this.hidden) {
1268             cfg.cls += ' hidden';
1269         }
1270         
1271         
1272         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1273             cfg.cls +=' alert alert-' + this.alert;
1274         }
1275         
1276         var body = cfg;
1277         
1278         if (this.panel.length) {
1279             cfg.cls += ' panel panel-' + this.panel;
1280             cfg.cn = [];
1281             if (this.header.length) {
1282                 
1283                 var h = [];
1284                 
1285                 if(this.expandable){
1286                     
1287                     cfg.cls = cfg.cls + ' expandable';
1288                     
1289                     h.push({
1290                         tag: 'i',
1291                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1292                     });
1293                     
1294                 }
1295                 
1296                 h.push(
1297                     {
1298                         tag: 'span',
1299                         cls : 'panel-title',
1300                         html : (this.expandable ? '&nbsp;' : '') + this.header
1301                     },
1302                     {
1303                         tag: 'span',
1304                         cls: 'panel-header-right',
1305                         html: this.rheader
1306                     }
1307                 );
1308                 
1309                 cfg.cn.push({
1310                     cls : 'panel-heading',
1311                     style : this.expandable ? 'cursor: pointer' : '',
1312                     cn : h
1313                 });
1314                 
1315             }
1316             
1317             body = false;
1318             cfg.cn.push({
1319                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1320                 html : this.html
1321             });
1322             
1323             
1324             if (this.footer.length) {
1325                 cfg.cn.push({
1326                     cls : 'panel-footer',
1327                     html : this.footer
1328                     
1329                 });
1330             }
1331             
1332         }
1333         
1334         if (body) {
1335             body.html = this.html || cfg.html;
1336             // prefix with the icons..
1337             if (this.fa) {
1338                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1339             }
1340             if (this.icon) {
1341                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1342             }
1343             
1344             
1345         }
1346         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1347             cfg.cls =  'container';
1348         }
1349         
1350         return cfg;
1351     },
1352     
1353     initEvents: function() 
1354     {
1355         if(this.expandable){
1356             var headerEl = this.headerEl();
1357         
1358             if(headerEl){
1359                 headerEl.on('click', this.onToggleClick, this);
1360             }
1361         }
1362         
1363         if(this.clickable){
1364             this.el.on('click', this.onClick, this);
1365         }
1366         
1367     },
1368     
1369     onToggleClick : function()
1370     {
1371         var headerEl = this.headerEl();
1372         
1373         if(!headerEl){
1374             return;
1375         }
1376         
1377         if(this.expanded){
1378             this.collapse();
1379             return;
1380         }
1381         
1382         this.expand();
1383     },
1384     
1385     expand : function()
1386     {
1387         if(this.fireEvent('expand', this)) {
1388             
1389             this.expanded = true;
1390             
1391             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1392             
1393             this.el.select('.panel-body',true).first().removeClass('hide');
1394             
1395             var toggleEl = this.toggleEl();
1396
1397             if(!toggleEl){
1398                 return;
1399             }
1400
1401             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1402         }
1403         
1404     },
1405     
1406     collapse : function()
1407     {
1408         if(this.fireEvent('collapse', this)) {
1409             
1410             this.expanded = false;
1411             
1412             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1413             this.el.select('.panel-body',true).first().addClass('hide');
1414         
1415             var toggleEl = this.toggleEl();
1416
1417             if(!toggleEl){
1418                 return;
1419             }
1420
1421             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1422         }
1423     },
1424     
1425     toggleEl : function()
1426     {
1427         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1428             return;
1429         }
1430         
1431         return this.el.select('.panel-heading .fa',true).first();
1432     },
1433     
1434     headerEl : function()
1435     {
1436         if(!this.el || !this.panel.length || !this.header.length){
1437             return;
1438         }
1439         
1440         return this.el.select('.panel-heading',true).first()
1441     },
1442     
1443     bodyEl : function()
1444     {
1445         if(!this.el || !this.panel.length){
1446             return;
1447         }
1448         
1449         return this.el.select('.panel-body',true).first()
1450     },
1451     
1452     titleEl : function()
1453     {
1454         if(!this.el || !this.panel.length || !this.header.length){
1455             return;
1456         }
1457         
1458         return this.el.select('.panel-title',true).first();
1459     },
1460     
1461     setTitle : function(v)
1462     {
1463         var titleEl = this.titleEl();
1464         
1465         if(!titleEl){
1466             return;
1467         }
1468         
1469         titleEl.dom.innerHTML = v;
1470     },
1471     
1472     getTitle : function()
1473     {
1474         
1475         var titleEl = this.titleEl();
1476         
1477         if(!titleEl){
1478             return '';
1479         }
1480         
1481         return titleEl.dom.innerHTML;
1482     },
1483     
1484     setRightTitle : function(v)
1485     {
1486         var t = this.el.select('.panel-header-right',true).first();
1487         
1488         if(!t){
1489             return;
1490         }
1491         
1492         t.dom.innerHTML = v;
1493     },
1494     
1495     onClick : function(e)
1496     {
1497         e.preventDefault();
1498         
1499         this.fireEvent('click', this, e);
1500     }
1501 });
1502
1503  /*
1504  * - LGPL
1505  *
1506  * image
1507  * 
1508  */
1509
1510
1511 /**
1512  * @class Roo.bootstrap.Img
1513  * @extends Roo.bootstrap.Component
1514  * Bootstrap Img class
1515  * @cfg {Boolean} imgResponsive false | true
1516  * @cfg {String} border rounded | circle | thumbnail
1517  * @cfg {String} src image source
1518  * @cfg {String} alt image alternative text
1519  * @cfg {String} href a tag href
1520  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1521  * @cfg {String} xsUrl xs image source
1522  * @cfg {String} smUrl sm image source
1523  * @cfg {String} mdUrl md image source
1524  * @cfg {String} lgUrl lg image source
1525  * 
1526  * @constructor
1527  * Create a new Input
1528  * @param {Object} config The config object
1529  */
1530
1531 Roo.bootstrap.Img = function(config){
1532     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1533     
1534     this.addEvents({
1535         // img events
1536         /**
1537          * @event click
1538          * The img click event for the img.
1539          * @param {Roo.EventObject} e
1540          */
1541         "click" : true
1542     });
1543 };
1544
1545 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1546     
1547     imgResponsive: true,
1548     border: '',
1549     src: 'about:blank',
1550     href: false,
1551     target: false,
1552     xsUrl: '',
1553     smUrl: '',
1554     mdUrl: '',
1555     lgUrl: '',
1556
1557     getAutoCreate : function()
1558     {   
1559         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1560             return this.createSingleImg();
1561         }
1562         
1563         var cfg = {
1564             tag: 'div',
1565             cls: 'roo-image-responsive-group',
1566             cn: []
1567         };
1568         var _this = this;
1569         
1570         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1571             
1572             if(!_this[size + 'Url']){
1573                 return;
1574             }
1575             
1576             var img = {
1577                 tag: 'img',
1578                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1579                 html: _this.html || cfg.html,
1580                 src: _this[size + 'Url']
1581             };
1582             
1583             img.cls += ' roo-image-responsive-' + size;
1584             
1585             var s = ['xs', 'sm', 'md', 'lg'];
1586             
1587             s.splice(s.indexOf(size), 1);
1588             
1589             Roo.each(s, function(ss){
1590                 img.cls += ' hidden-' + ss;
1591             });
1592             
1593             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1594                 cfg.cls += ' img-' + _this.border;
1595             }
1596             
1597             if(_this.alt){
1598                 cfg.alt = _this.alt;
1599             }
1600             
1601             if(_this.href){
1602                 var a = {
1603                     tag: 'a',
1604                     href: _this.href,
1605                     cn: [
1606                         img
1607                     ]
1608                 };
1609
1610                 if(this.target){
1611                     a.target = _this.target;
1612                 }
1613             }
1614             
1615             cfg.cn.push((_this.href) ? a : img);
1616             
1617         });
1618         
1619         return cfg;
1620     },
1621     
1622     createSingleImg : function()
1623     {
1624         var cfg = {
1625             tag: 'img',
1626             cls: (this.imgResponsive) ? 'img-responsive' : '',
1627             html : null,
1628             src : 'about:blank'  // just incase src get's set to undefined?!?
1629         };
1630         
1631         cfg.html = this.html || cfg.html;
1632         
1633         cfg.src = this.src || cfg.src;
1634         
1635         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1636             cfg.cls += ' img-' + this.border;
1637         }
1638         
1639         if(this.alt){
1640             cfg.alt = this.alt;
1641         }
1642         
1643         if(this.href){
1644             var a = {
1645                 tag: 'a',
1646                 href: this.href,
1647                 cn: [
1648                     cfg
1649                 ]
1650             };
1651             
1652             if(this.target){
1653                 a.target = this.target;
1654             }
1655             
1656         }
1657         
1658         return (this.href) ? a : cfg;
1659     },
1660     
1661     initEvents: function() 
1662     {
1663         if(!this.href){
1664             this.el.on('click', this.onClick, this);
1665         }
1666         
1667     },
1668     
1669     onClick : function(e)
1670     {
1671         Roo.log('img onclick');
1672         this.fireEvent('click', this, e);
1673     },
1674     /**
1675      * Sets the url of the image - used to update it
1676      * @param {String} url the url of the image
1677      */
1678     
1679     setSrc : function(url)
1680     {
1681         this.src =  url;
1682         
1683         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1684             this.el.dom.src =  url;
1685             return;
1686         }
1687         
1688         this.el.select('img', true).first().dom.src =  url;
1689     }
1690     
1691     
1692    
1693 });
1694
1695  /*
1696  * - LGPL
1697  *
1698  * image
1699  * 
1700  */
1701
1702
1703 /**
1704  * @class Roo.bootstrap.Link
1705  * @extends Roo.bootstrap.Component
1706  * Bootstrap Link Class
1707  * @cfg {String} alt image alternative text
1708  * @cfg {String} href a tag href
1709  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1710  * @cfg {String} html the content of the link.
1711  * @cfg {String} anchor name for the anchor link
1712  * @cfg {String} fa - favicon
1713
1714  * @cfg {Boolean} preventDefault (true | false) default false
1715
1716  * 
1717  * @constructor
1718  * Create a new Input
1719  * @param {Object} config The config object
1720  */
1721
1722 Roo.bootstrap.Link = function(config){
1723     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1724     
1725     this.addEvents({
1726         // img events
1727         /**
1728          * @event click
1729          * The img click event for the img.
1730          * @param {Roo.EventObject} e
1731          */
1732         "click" : true
1733     });
1734 };
1735
1736 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1737     
1738     href: false,
1739     target: false,
1740     preventDefault: false,
1741     anchor : false,
1742     alt : false,
1743     fa: false,
1744
1745
1746     getAutoCreate : function()
1747     {
1748         var html = this.html || '';
1749         
1750         if (this.fa !== false) {
1751             html = '<i class="fa fa-' + this.fa + '"></i>';
1752         }
1753         var cfg = {
1754             tag: 'a'
1755         };
1756         // anchor's do not require html/href...
1757         if (this.anchor === false) {
1758             cfg.html = html;
1759             cfg.href = this.href || '#';
1760         } else {
1761             cfg.name = this.anchor;
1762             if (this.html !== false || this.fa !== false) {
1763                 cfg.html = html;
1764             }
1765             if (this.href !== false) {
1766                 cfg.href = this.href;
1767             }
1768         }
1769         
1770         if(this.alt !== false){
1771             cfg.alt = this.alt;
1772         }
1773         
1774         
1775         if(this.target !== false) {
1776             cfg.target = this.target;
1777         }
1778         
1779         return cfg;
1780     },
1781     
1782     initEvents: function() {
1783         
1784         if(!this.href || this.preventDefault){
1785             this.el.on('click', this.onClick, this);
1786         }
1787     },
1788     
1789     onClick : function(e)
1790     {
1791         if(this.preventDefault){
1792             e.preventDefault();
1793         }
1794         //Roo.log('img onclick');
1795         this.fireEvent('click', this, e);
1796     }
1797    
1798 });
1799
1800  /*
1801  * - LGPL
1802  *
1803  * header
1804  * 
1805  */
1806
1807 /**
1808  * @class Roo.bootstrap.Header
1809  * @extends Roo.bootstrap.Component
1810  * Bootstrap Header class
1811  * @cfg {String} html content of header
1812  * @cfg {Number} level (1|2|3|4|5|6) default 1
1813  * 
1814  * @constructor
1815  * Create a new Header
1816  * @param {Object} config The config object
1817  */
1818
1819
1820 Roo.bootstrap.Header  = function(config){
1821     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1822 };
1823
1824 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1825     
1826     //href : false,
1827     html : false,
1828     level : 1,
1829     
1830     
1831     
1832     getAutoCreate : function(){
1833         
1834         
1835         
1836         var cfg = {
1837             tag: 'h' + (1 *this.level),
1838             html: this.html || ''
1839         } ;
1840         
1841         return cfg;
1842     }
1843    
1844 });
1845
1846  
1847
1848  /*
1849  * Based on:
1850  * Ext JS Library 1.1.1
1851  * Copyright(c) 2006-2007, Ext JS, LLC.
1852  *
1853  * Originally Released Under LGPL - original licence link has changed is not relivant.
1854  *
1855  * Fork - LGPL
1856  * <script type="text/javascript">
1857  */
1858  
1859 /**
1860  * @class Roo.bootstrap.MenuMgr
1861  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1862  * @singleton
1863  */
1864 Roo.bootstrap.MenuMgr = function(){
1865    var menus, active, groups = {}, attached = false, lastShow = new Date();
1866
1867    // private - called when first menu is created
1868    function init(){
1869        menus = {};
1870        active = new Roo.util.MixedCollection();
1871        Roo.get(document).addKeyListener(27, function(){
1872            if(active.length > 0){
1873                hideAll();
1874            }
1875        });
1876    }
1877
1878    // private
1879    function hideAll(){
1880        if(active && active.length > 0){
1881            var c = active.clone();
1882            c.each(function(m){
1883                m.hide();
1884            });
1885        }
1886    }
1887
1888    // private
1889    function onHide(m){
1890        active.remove(m);
1891        if(active.length < 1){
1892            Roo.get(document).un("mouseup", onMouseDown);
1893             
1894            attached = false;
1895        }
1896    }
1897
1898    // private
1899    function onShow(m){
1900        var last = active.last();
1901        lastShow = new Date();
1902        active.add(m);
1903        if(!attached){
1904           Roo.get(document).on("mouseup", onMouseDown);
1905            
1906            attached = true;
1907        }
1908        if(m.parentMenu){
1909           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1910           m.parentMenu.activeChild = m;
1911        }else if(last && last.isVisible()){
1912           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1913        }
1914    }
1915
1916    // private
1917    function onBeforeHide(m){
1918        if(m.activeChild){
1919            m.activeChild.hide();
1920        }
1921        if(m.autoHideTimer){
1922            clearTimeout(m.autoHideTimer);
1923            delete m.autoHideTimer;
1924        }
1925    }
1926
1927    // private
1928    function onBeforeShow(m){
1929        var pm = m.parentMenu;
1930        if(!pm && !m.allowOtherMenus){
1931            hideAll();
1932        }else if(pm && pm.activeChild && active != m){
1933            pm.activeChild.hide();
1934        }
1935    }
1936
1937    // private this should really trigger on mouseup..
1938    function onMouseDown(e){
1939         Roo.log("on Mouse Up");
1940         
1941         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1942             Roo.log("MenuManager hideAll");
1943             hideAll();
1944             e.stopEvent();
1945         }
1946         
1947         
1948    }
1949
1950    // private
1951    function onBeforeCheck(mi, state){
1952        if(state){
1953            var g = groups[mi.group];
1954            for(var i = 0, l = g.length; i < l; i++){
1955                if(g[i] != mi){
1956                    g[i].setChecked(false);
1957                }
1958            }
1959        }
1960    }
1961
1962    return {
1963
1964        /**
1965         * Hides all menus that are currently visible
1966         */
1967        hideAll : function(){
1968             hideAll();  
1969        },
1970
1971        // private
1972        register : function(menu){
1973            if(!menus){
1974                init();
1975            }
1976            menus[menu.id] = menu;
1977            menu.on("beforehide", onBeforeHide);
1978            menu.on("hide", onHide);
1979            menu.on("beforeshow", onBeforeShow);
1980            menu.on("show", onShow);
1981            var g = menu.group;
1982            if(g && menu.events["checkchange"]){
1983                if(!groups[g]){
1984                    groups[g] = [];
1985                }
1986                groups[g].push(menu);
1987                menu.on("checkchange", onCheck);
1988            }
1989        },
1990
1991         /**
1992          * Returns a {@link Roo.menu.Menu} object
1993          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1994          * be used to generate and return a new Menu instance.
1995          */
1996        get : function(menu){
1997            if(typeof menu == "string"){ // menu id
1998                return menus[menu];
1999            }else if(menu.events){  // menu instance
2000                return menu;
2001            }
2002            /*else if(typeof menu.length == 'number'){ // array of menu items?
2003                return new Roo.bootstrap.Menu({items:menu});
2004            }else{ // otherwise, must be a config
2005                return new Roo.bootstrap.Menu(menu);
2006            }
2007            */
2008            return false;
2009        },
2010
2011        // private
2012        unregister : function(menu){
2013            delete menus[menu.id];
2014            menu.un("beforehide", onBeforeHide);
2015            menu.un("hide", onHide);
2016            menu.un("beforeshow", onBeforeShow);
2017            menu.un("show", onShow);
2018            var g = menu.group;
2019            if(g && menu.events["checkchange"]){
2020                groups[g].remove(menu);
2021                menu.un("checkchange", onCheck);
2022            }
2023        },
2024
2025        // private
2026        registerCheckable : function(menuItem){
2027            var g = menuItem.group;
2028            if(g){
2029                if(!groups[g]){
2030                    groups[g] = [];
2031                }
2032                groups[g].push(menuItem);
2033                menuItem.on("beforecheckchange", onBeforeCheck);
2034            }
2035        },
2036
2037        // private
2038        unregisterCheckable : function(menuItem){
2039            var g = menuItem.group;
2040            if(g){
2041                groups[g].remove(menuItem);
2042                menuItem.un("beforecheckchange", onBeforeCheck);
2043            }
2044        }
2045    };
2046 }();/*
2047  * - LGPL
2048  *
2049  * menu
2050  * 
2051  */
2052
2053 /**
2054  * @class Roo.bootstrap.Menu
2055  * @extends Roo.bootstrap.Component
2056  * Bootstrap Menu class - container for MenuItems
2057  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2058  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2059  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2060  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2061  * 
2062  * @constructor
2063  * Create a new Menu
2064  * @param {Object} config The config object
2065  */
2066
2067
2068 Roo.bootstrap.Menu = function(config){
2069     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2070     if (this.registerMenu && this.type != 'treeview')  {
2071         Roo.bootstrap.MenuMgr.register(this);
2072     }
2073     
2074     
2075     this.addEvents({
2076         /**
2077          * @event beforeshow
2078          * Fires before this menu is displayed (return false to block)
2079          * @param {Roo.menu.Menu} this
2080          */
2081         beforeshow : true,
2082         /**
2083          * @event beforehide
2084          * Fires before this menu is hidden (return false to block)
2085          * @param {Roo.menu.Menu} this
2086          */
2087         beforehide : true,
2088         /**
2089          * @event show
2090          * Fires after this menu is displayed
2091          * @param {Roo.menu.Menu} this
2092          */
2093         show : true,
2094         /**
2095          * @event hide
2096          * Fires after this menu is hidden
2097          * @param {Roo.menu.Menu} this
2098          */
2099         hide : true,
2100         /**
2101          * @event click
2102          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2105          * @param {Roo.EventObject} e
2106          */
2107         click : true,
2108         /**
2109          * @event mouseover
2110          * Fires when the mouse is hovering over this menu
2111          * @param {Roo.menu.Menu} this
2112          * @param {Roo.EventObject} e
2113          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2114          */
2115         mouseover : true,
2116         /**
2117          * @event mouseout
2118          * Fires when the mouse exits this menu
2119          * @param {Roo.menu.Menu} this
2120          * @param {Roo.EventObject} e
2121          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2122          */
2123         mouseout : true,
2124         /**
2125          * @event itemclick
2126          * Fires when a menu item contained in this menu is clicked
2127          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2128          * @param {Roo.EventObject} e
2129          */
2130         itemclick: true
2131     });
2132     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2133 };
2134
2135 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2136     
2137    /// html : false,
2138     //align : '',
2139     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2140     type: false,
2141     /**
2142      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2143      */
2144     registerMenu : true,
2145     
2146     menuItems :false, // stores the menu items..
2147     
2148     hidden:true,
2149         
2150     parentMenu : false,
2151     
2152     stopEvent : true,
2153     
2154     isLink : false,
2155     
2156     getChildContainer : function() {
2157         return this.el;  
2158     },
2159     
2160     getAutoCreate : function(){
2161          
2162         //if (['right'].indexOf(this.align)!==-1) {
2163         //    cfg.cn[1].cls += ' pull-right'
2164         //}
2165         
2166         
2167         var cfg = {
2168             tag : 'ul',
2169             cls : 'dropdown-menu' ,
2170             style : 'z-index:1000'
2171             
2172         };
2173         
2174         if (this.type === 'submenu') {
2175             cfg.cls = 'submenu active';
2176         }
2177         if (this.type === 'treeview') {
2178             cfg.cls = 'treeview-menu';
2179         }
2180         
2181         return cfg;
2182     },
2183     initEvents : function() {
2184         
2185        // Roo.log("ADD event");
2186        // Roo.log(this.triggerEl.dom);
2187         
2188         this.triggerEl.on('click', this.onTriggerClick, this);
2189         
2190         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2191         
2192         
2193         if (this.triggerEl.hasClass('nav-item')) {
2194             // dropdown toggle on the 'a' in BS4?
2195             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2196         } else {
2197             this.triggerEl.addClass('dropdown-toggle');
2198         }
2199         if (Roo.isTouch) {
2200             this.el.on('touchstart'  , this.onTouch, this);
2201         }
2202         this.el.on('click' , this.onClick, this);
2203
2204         this.el.on("mouseover", this.onMouseOver, this);
2205         this.el.on("mouseout", this.onMouseOut, this);
2206         
2207     },
2208     
2209     findTargetItem : function(e)
2210     {
2211         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2212         if(!t){
2213             return false;
2214         }
2215         //Roo.log(t);         Roo.log(t.id);
2216         if(t && t.id){
2217             //Roo.log(this.menuitems);
2218             return this.menuitems.get(t.id);
2219             
2220             //return this.items.get(t.menuItemId);
2221         }
2222         
2223         return false;
2224     },
2225     
2226     onTouch : function(e) 
2227     {
2228         Roo.log("menu.onTouch");
2229         //e.stopEvent(); this make the user popdown broken
2230         this.onClick(e);
2231     },
2232     
2233     onClick : function(e)
2234     {
2235         Roo.log("menu.onClick");
2236         
2237         var t = this.findTargetItem(e);
2238         if(!t || t.isContainer){
2239             return;
2240         }
2241         Roo.log(e);
2242         /*
2243         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2244             if(t == this.activeItem && t.shouldDeactivate(e)){
2245                 this.activeItem.deactivate();
2246                 delete this.activeItem;
2247                 return;
2248             }
2249             if(t.canActivate){
2250                 this.setActiveItem(t, true);
2251             }
2252             return;
2253             
2254             
2255         }
2256         */
2257        
2258         Roo.log('pass click event');
2259         
2260         t.onClick(e);
2261         
2262         this.fireEvent("click", this, t, e);
2263         
2264         var _this = this;
2265         
2266         if(!t.href.length || t.href == '#'){
2267             (function() { _this.hide(); }).defer(100);
2268         }
2269         
2270     },
2271     
2272     onMouseOver : function(e){
2273         var t  = this.findTargetItem(e);
2274         //Roo.log(t);
2275         //if(t){
2276         //    if(t.canActivate && !t.disabled){
2277         //        this.setActiveItem(t, true);
2278         //    }
2279         //}
2280         
2281         this.fireEvent("mouseover", this, e, t);
2282     },
2283     isVisible : function(){
2284         return !this.hidden;
2285     },
2286     onMouseOut : function(e){
2287         var t  = this.findTargetItem(e);
2288         
2289         //if(t ){
2290         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2291         //        this.activeItem.deactivate();
2292         //        delete this.activeItem;
2293         //    }
2294         //}
2295         this.fireEvent("mouseout", this, e, t);
2296     },
2297     
2298     
2299     /**
2300      * Displays this menu relative to another element
2301      * @param {String/HTMLElement/Roo.Element} element The element to align to
2302      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2303      * the element (defaults to this.defaultAlign)
2304      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2305      */
2306     show : function(el, pos, parentMenu)
2307     {
2308         if (false === this.fireEvent("beforeshow", this)) {
2309             Roo.log("show canceled");
2310             return;
2311         }
2312         this.parentMenu = parentMenu;
2313         if(!this.el){
2314             this.render();
2315         }
2316         
2317         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2318     },
2319      /**
2320      * Displays this menu at a specific xy position
2321      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2322      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2323      */
2324     showAt : function(xy, parentMenu, /* private: */_e){
2325         this.parentMenu = parentMenu;
2326         if(!this.el){
2327             this.render();
2328         }
2329         if(_e !== false){
2330             this.fireEvent("beforeshow", this);
2331             //xy = this.el.adjustForConstraints(xy);
2332         }
2333         
2334         //this.el.show();
2335         this.hideMenuItems();
2336         this.hidden = false;
2337         this.triggerEl.addClass('open');
2338         this.el.addClass('show');
2339         
2340         // reassign x when hitting right
2341         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2342             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2343         }
2344         
2345         // reassign y when hitting bottom
2346         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2347             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2348         }
2349         
2350         // but the list may align on trigger left or trigger top... should it be a properity?
2351         
2352         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2353             this.el.setXY(xy);
2354         }
2355         
2356         this.focus();
2357         this.fireEvent("show", this);
2358     },
2359     
2360     focus : function(){
2361         return;
2362         if(!this.hidden){
2363             this.doFocus.defer(50, this);
2364         }
2365     },
2366
2367     doFocus : function(){
2368         if(!this.hidden){
2369             this.focusEl.focus();
2370         }
2371     },
2372
2373     /**
2374      * Hides this menu and optionally all parent menus
2375      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2376      */
2377     hide : function(deep)
2378     {
2379         if (false === this.fireEvent("beforehide", this)) {
2380             Roo.log("hide canceled");
2381             return;
2382         }
2383         this.hideMenuItems();
2384         if(this.el && this.isVisible()){
2385            
2386             if(this.activeItem){
2387                 this.activeItem.deactivate();
2388                 this.activeItem = null;
2389             }
2390             this.triggerEl.removeClass('open');;
2391             this.el.removeClass('show');
2392             this.hidden = true;
2393             this.fireEvent("hide", this);
2394         }
2395         if(deep === true && this.parentMenu){
2396             this.parentMenu.hide(true);
2397         }
2398     },
2399     
2400     onTriggerClick : function(e)
2401     {
2402         Roo.log('trigger click');
2403         
2404         var target = e.getTarget();
2405         
2406         Roo.log(target.nodeName.toLowerCase());
2407         
2408         if(target.nodeName.toLowerCase() === 'i'){
2409             e.preventDefault();
2410         }
2411         
2412     },
2413     
2414     onTriggerPress  : function(e)
2415     {
2416         Roo.log('trigger press');
2417         //Roo.log(e.getTarget());
2418        // Roo.log(this.triggerEl.dom);
2419        
2420         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2421         var pel = Roo.get(e.getTarget());
2422         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2423             Roo.log('is treeview or dropdown?');
2424             return;
2425         }
2426         
2427         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2428             return;
2429         }
2430         
2431         if (this.isVisible()) {
2432             Roo.log('hide');
2433             this.hide();
2434         } else {
2435             Roo.log('show');
2436             this.show(this.triggerEl, '?', false);
2437         }
2438         
2439         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2440             e.stopEvent();
2441         }
2442         
2443     },
2444        
2445     
2446     hideMenuItems : function()
2447     {
2448         Roo.log("hide Menu Items");
2449         if (!this.el) { 
2450             return;
2451         }
2452         
2453         this.el.select('.open',true).each(function(aa) {
2454             
2455             aa.removeClass('open');
2456          
2457         });
2458     },
2459     addxtypeChild : function (tree, cntr) {
2460         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2461           
2462         this.menuitems.add(comp);
2463         return comp;
2464
2465     },
2466     getEl : function()
2467     {
2468         Roo.log(this.el);
2469         return this.el;
2470     },
2471     
2472     clear : function()
2473     {
2474         this.getEl().dom.innerHTML = '';
2475         this.menuitems.clear();
2476     }
2477 });
2478
2479  
2480  /*
2481  * - LGPL
2482  *
2483  * menu item
2484  * 
2485  */
2486
2487
2488 /**
2489  * @class Roo.bootstrap.MenuItem
2490  * @extends Roo.bootstrap.Component
2491  * Bootstrap MenuItem class
2492  * @cfg {String} html the menu label
2493  * @cfg {String} href the link
2494  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2495  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2496  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2497  * @cfg {String} fa favicon to show on left of menu item.
2498  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2499  * 
2500  * 
2501  * @constructor
2502  * Create a new MenuItem
2503  * @param {Object} config The config object
2504  */
2505
2506
2507 Roo.bootstrap.MenuItem = function(config){
2508     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2509     this.addEvents({
2510         // raw events
2511         /**
2512          * @event click
2513          * The raw click event for the entire grid.
2514          * @param {Roo.bootstrap.MenuItem} this
2515          * @param {Roo.EventObject} e
2516          */
2517         "click" : true
2518     });
2519 };
2520
2521 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2522     
2523     href : false,
2524     html : false,
2525     preventDefault: false,
2526     isContainer : false,
2527     active : false,
2528     fa: false,
2529     
2530     getAutoCreate : function(){
2531         
2532         if(this.isContainer){
2533             return {
2534                 tag: 'li',
2535                 cls: 'dropdown-menu-item '
2536             };
2537         }
2538         var ctag = {
2539             tag: 'span',
2540             html: 'Link'
2541         };
2542         
2543         var anc = {
2544             tag : 'a',
2545             cls : 'dropdown-item',
2546             href : '#',
2547             cn : [  ]
2548         };
2549         
2550         if (this.fa !== false) {
2551             anc.cn.push({
2552                 tag : 'i',
2553                 cls : 'fa fa-' + this.fa
2554             });
2555         }
2556         
2557         anc.cn.push(ctag);
2558         
2559         
2560         var cfg= {
2561             tag: 'li',
2562             cls: 'dropdown-menu-item',
2563             cn: [ anc ]
2564         };
2565         if (this.parent().type == 'treeview') {
2566             cfg.cls = 'treeview-menu';
2567         }
2568         if (this.active) {
2569             cfg.cls += ' active';
2570         }
2571         
2572         
2573         
2574         anc.href = this.href || cfg.cn[0].href ;
2575         ctag.html = this.html || cfg.cn[0].html ;
2576         return cfg;
2577     },
2578     
2579     initEvents: function()
2580     {
2581         if (this.parent().type == 'treeview') {
2582             this.el.select('a').on('click', this.onClick, this);
2583         }
2584         
2585         if (this.menu) {
2586             this.menu.parentType = this.xtype;
2587             this.menu.triggerEl = this.el;
2588             this.menu = this.addxtype(Roo.apply({}, this.menu));
2589         }
2590         
2591     },
2592     onClick : function(e)
2593     {
2594         Roo.log('item on click ');
2595         
2596         if(this.preventDefault){
2597             e.preventDefault();
2598         }
2599         //this.parent().hideMenuItems();
2600         
2601         this.fireEvent('click', this, e);
2602     },
2603     getEl : function()
2604     {
2605         return this.el;
2606     } 
2607 });
2608
2609  
2610
2611  /*
2612  * - LGPL
2613  *
2614  * menu separator
2615  * 
2616  */
2617
2618
2619 /**
2620  * @class Roo.bootstrap.MenuSeparator
2621  * @extends Roo.bootstrap.Component
2622  * Bootstrap MenuSeparator class
2623  * 
2624  * @constructor
2625  * Create a new MenuItem
2626  * @param {Object} config The config object
2627  */
2628
2629
2630 Roo.bootstrap.MenuSeparator = function(config){
2631     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2632 };
2633
2634 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2635     
2636     getAutoCreate : function(){
2637         var cfg = {
2638             cls: 'divider',
2639             tag : 'li'
2640         };
2641         
2642         return cfg;
2643     }
2644    
2645 });
2646
2647  
2648
2649  
2650 /*
2651 * Licence: LGPL
2652 */
2653
2654 /**
2655  * @class Roo.bootstrap.Modal
2656  * @extends Roo.bootstrap.Component
2657  * Bootstrap Modal class
2658  * @cfg {String} title Title of dialog
2659  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2660  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2661  * @cfg {Boolean} specificTitle default false
2662  * @cfg {Array} buttons Array of buttons or standard button set..
2663  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2664  * @cfg {Boolean} animate default true
2665  * @cfg {Boolean} allow_close default true
2666  * @cfg {Boolean} fitwindow default false
2667  * @cfg {String} size (sm|lg) default empty
2668  * @cfg {Number} max_width set the max width of modal
2669  *
2670  *
2671  * @constructor
2672  * Create a new Modal Dialog
2673  * @param {Object} config The config object
2674  */
2675
2676 Roo.bootstrap.Modal = function(config){
2677     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2678     this.addEvents({
2679         // raw events
2680         /**
2681          * @event btnclick
2682          * The raw btnclick event for the button
2683          * @param {Roo.EventObject} e
2684          */
2685         "btnclick" : true,
2686         /**
2687          * @event resize
2688          * Fire when dialog resize
2689          * @param {Roo.bootstrap.Modal} this
2690          * @param {Roo.EventObject} e
2691          */
2692         "resize" : true
2693     });
2694     this.buttons = this.buttons || [];
2695
2696     if (this.tmpl) {
2697         this.tmpl = Roo.factory(this.tmpl);
2698     }
2699
2700 };
2701
2702 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2703
2704     title : 'test dialog',
2705
2706     buttons : false,
2707
2708     // set on load...
2709
2710     html: false,
2711
2712     tmp: false,
2713
2714     specificTitle: false,
2715
2716     buttonPosition: 'right',
2717
2718     allow_close : true,
2719
2720     animate : true,
2721
2722     fitwindow: false,
2723     
2724      // private
2725     dialogEl: false,
2726     bodyEl:  false,
2727     footerEl:  false,
2728     titleEl:  false,
2729     closeEl:  false,
2730
2731     size: '',
2732     
2733     max_width: 0,
2734     
2735     max_height: 0,
2736     
2737     fit_content: false,
2738
2739     onRender : function(ct, position)
2740     {
2741         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2742
2743         if(!this.el){
2744             var cfg = Roo.apply({},  this.getAutoCreate());
2745             cfg.id = Roo.id();
2746             //if(!cfg.name){
2747             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2748             //}
2749             //if (!cfg.name.length) {
2750             //    delete cfg.name;
2751            // }
2752             if (this.cls) {
2753                 cfg.cls += ' ' + this.cls;
2754             }
2755             if (this.style) {
2756                 cfg.style = this.style;
2757             }
2758             this.el = Roo.get(document.body).createChild(cfg, position);
2759         }
2760         //var type = this.el.dom.type;
2761
2762
2763         if(this.tabIndex !== undefined){
2764             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2765         }
2766
2767         this.dialogEl = this.el.select('.modal-dialog',true).first();
2768         this.bodyEl = this.el.select('.modal-body',true).first();
2769         this.closeEl = this.el.select('.modal-header .close', true).first();
2770         this.headerEl = this.el.select('.modal-header',true).first();
2771         this.titleEl = this.el.select('.modal-title',true).first();
2772         this.footerEl = this.el.select('.modal-footer',true).first();
2773
2774         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2775         
2776         //this.el.addClass("x-dlg-modal");
2777
2778         if (this.buttons.length) {
2779             Roo.each(this.buttons, function(bb) {
2780                 var b = Roo.apply({}, bb);
2781                 b.xns = b.xns || Roo.bootstrap;
2782                 b.xtype = b.xtype || 'Button';
2783                 if (typeof(b.listeners) == 'undefined') {
2784                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2785                 }
2786
2787                 var btn = Roo.factory(b);
2788
2789                 btn.render(this.getButtonContainer());
2790
2791             },this);
2792         }
2793         // render the children.
2794         var nitems = [];
2795
2796         if(typeof(this.items) != 'undefined'){
2797             var items = this.items;
2798             delete this.items;
2799
2800             for(var i =0;i < items.length;i++) {
2801                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2802             }
2803         }
2804
2805         this.items = nitems;
2806
2807         // where are these used - they used to be body/close/footer
2808
2809
2810         this.initEvents();
2811         //this.el.addClass([this.fieldClass, this.cls]);
2812
2813     },
2814
2815     getAutoCreate : function()
2816     {
2817         var bdy = {
2818                 cls : 'modal-body',
2819                 html : this.html || ''
2820         };
2821
2822         var title = {
2823             tag: 'h4',
2824             cls : 'modal-title',
2825             html : this.title
2826         };
2827
2828         if(this.specificTitle){
2829             title = this.title;
2830
2831         }
2832
2833         var header = [];
2834         if (this.allow_close && Roo.bootstrap.version == 3) {
2835             header.push({
2836                 tag: 'button',
2837                 cls : 'close',
2838                 html : '&times'
2839             });
2840         }
2841
2842         header.push(title);
2843
2844         if (this.allow_close && Roo.bootstrap.version == 4) {
2845             header.push({
2846                 tag: 'button',
2847                 cls : 'close',
2848                 html : '&times'
2849             });
2850         }
2851         
2852         var size = '';
2853
2854         if(this.size.length){
2855             size = 'modal-' + this.size;
2856         }
2857         
2858         var footer = Roo.bootstrap.version == 3 ?
2859             {
2860                 cls : 'modal-footer',
2861                 cn : [
2862                     {
2863                         tag: 'div',
2864                         cls: 'btn-' + this.buttonPosition
2865                     }
2866                 ]
2867
2868             } :
2869             {  // BS4 uses mr-auto on left buttons....
2870                 cls : 'modal-footer'
2871             };
2872
2873             
2874
2875         
2876         
2877         var modal = {
2878             cls: "modal",
2879              cn : [
2880                 {
2881                     cls: "modal-dialog " + size,
2882                     cn : [
2883                         {
2884                             cls : "modal-content",
2885                             cn : [
2886                                 {
2887                                     cls : 'modal-header',
2888                                     cn : header
2889                                 },
2890                                 bdy,
2891                                 footer
2892                             ]
2893
2894                         }
2895                     ]
2896
2897                 }
2898             ]
2899         };
2900
2901         if(this.animate){
2902             modal.cls += ' fade';
2903         }
2904
2905         return modal;
2906
2907     },
2908     getChildContainer : function() {
2909
2910          return this.bodyEl;
2911
2912     },
2913     getButtonContainer : function() {
2914         
2915          return Roo.bootstrap.version == 4 ?
2916             this.el.select('.modal-footer',true).first()
2917             : this.el.select('.modal-footer div',true).first();
2918
2919     },
2920     initEvents : function()
2921     {
2922         if (this.allow_close) {
2923             this.closeEl.on('click', this.hide, this);
2924         }
2925         Roo.EventManager.onWindowResize(this.resize, this, true);
2926
2927
2928     },
2929   
2930
2931     resize : function()
2932     {
2933         this.maskEl.setSize(
2934             Roo.lib.Dom.getViewWidth(true),
2935             Roo.lib.Dom.getViewHeight(true)
2936         );
2937         
2938         if (this.fitwindow) {
2939             
2940            
2941             this.setSize(
2942                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2943                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2944             );
2945             return;
2946         }
2947         
2948         if(this.max_width !== 0) {
2949             
2950             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2951             
2952             if(this.height) {
2953                 this.setSize(w, this.height);
2954                 return;
2955             }
2956             
2957             if(this.max_height) {
2958                 this.setSize(w,Math.min(
2959                     this.max_height,
2960                     Roo.lib.Dom.getViewportHeight(true) - 60
2961                 ));
2962                 
2963                 return;
2964             }
2965             
2966             if(!this.fit_content) {
2967                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2968                 return;
2969             }
2970             
2971             this.setSize(w, Math.min(
2972                 60 +
2973                 this.headerEl.getHeight() + 
2974                 this.footerEl.getHeight() + 
2975                 this.getChildHeight(this.bodyEl.dom.childNodes),
2976                 Roo.lib.Dom.getViewportHeight(true) - 60)
2977             );
2978         }
2979         
2980     },
2981
2982     setSize : function(w,h)
2983     {
2984         if (!w && !h) {
2985             return;
2986         }
2987         
2988         this.resizeTo(w,h);
2989     },
2990
2991     show : function() {
2992
2993         if (!this.rendered) {
2994             this.render();
2995         }
2996
2997         //this.el.setStyle('display', 'block');
2998         this.el.removeClass('hideing');
2999         this.el.dom.style.display='block';
3000         
3001         Roo.get(document.body).addClass('modal-open');
3002  
3003         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3004             
3005             (function(){
3006                 this.el.addClass('show');
3007                 this.el.addClass('in');
3008             }).defer(50, this);
3009         }else{
3010             this.el.addClass('show');
3011             this.el.addClass('in');
3012         }
3013
3014         // not sure how we can show data in here..
3015         //if (this.tmpl) {
3016         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3017         //}
3018
3019         Roo.get(document.body).addClass("x-body-masked");
3020         
3021         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3022         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3023         this.maskEl.dom.style.display = 'block';
3024         this.maskEl.addClass('show');
3025         
3026         
3027         this.resize();
3028         
3029         this.fireEvent('show', this);
3030
3031         // set zindex here - otherwise it appears to be ignored...
3032         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3033
3034         (function () {
3035             this.items.forEach( function(e) {
3036                 e.layout ? e.layout() : false;
3037
3038             });
3039         }).defer(100,this);
3040
3041     },
3042     hide : function()
3043     {
3044         if(this.fireEvent("beforehide", this) !== false){
3045             
3046             this.maskEl.removeClass('show');
3047             
3048             this.maskEl.dom.style.display = '';
3049             Roo.get(document.body).removeClass("x-body-masked");
3050             this.el.removeClass('in');
3051             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3052
3053             if(this.animate){ // why
3054                 this.el.addClass('hideing');
3055                 this.el.removeClass('show');
3056                 (function(){
3057                     if (!this.el.hasClass('hideing')) {
3058                         return; // it's been shown again...
3059                     }
3060                     
3061                     this.el.dom.style.display='';
3062
3063                     Roo.get(document.body).removeClass('modal-open');
3064                     this.el.removeClass('hideing');
3065                 }).defer(150,this);
3066                 
3067             }else{
3068                 this.el.removeClass('show');
3069                 this.el.dom.style.display='';
3070                 Roo.get(document.body).removeClass('modal-open');
3071
3072             }
3073             this.fireEvent('hide', this);
3074         }
3075     },
3076     isVisible : function()
3077     {
3078         
3079         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3080         
3081     },
3082
3083     addButton : function(str, cb)
3084     {
3085
3086
3087         var b = Roo.apply({}, { html : str } );
3088         b.xns = b.xns || Roo.bootstrap;
3089         b.xtype = b.xtype || 'Button';
3090         if (typeof(b.listeners) == 'undefined') {
3091             b.listeners = { click : cb.createDelegate(this)  };
3092         }
3093
3094         var btn = Roo.factory(b);
3095
3096         btn.render(this.getButtonContainer());
3097
3098         return btn;
3099
3100     },
3101
3102     setDefaultButton : function(btn)
3103     {
3104         //this.el.select('.modal-footer').()
3105     },
3106
3107     resizeTo: function(w,h)
3108     {
3109         this.dialogEl.setWidth(w);
3110         
3111         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3112
3113         this.bodyEl.setHeight(h - diff);
3114         
3115         this.fireEvent('resize', this);
3116     },
3117     
3118     setContentSize  : function(w, h)
3119     {
3120
3121     },
3122     onButtonClick: function(btn,e)
3123     {
3124         //Roo.log([a,b,c]);
3125         this.fireEvent('btnclick', btn.name, e);
3126     },
3127      /**
3128      * Set the title of the Dialog
3129      * @param {String} str new Title
3130      */
3131     setTitle: function(str) {
3132         this.titleEl.dom.innerHTML = str;
3133     },
3134     /**
3135      * Set the body of the Dialog
3136      * @param {String} str new Title
3137      */
3138     setBody: function(str) {
3139         this.bodyEl.dom.innerHTML = str;
3140     },
3141     /**
3142      * Set the body of the Dialog using the template
3143      * @param {Obj} data - apply this data to the template and replace the body contents.
3144      */
3145     applyBody: function(obj)
3146     {
3147         if (!this.tmpl) {
3148             Roo.log("Error - using apply Body without a template");
3149             //code
3150         }
3151         this.tmpl.overwrite(this.bodyEl, obj);
3152     },
3153     
3154     getChildHeight : function(child_nodes)
3155     {
3156         if(
3157             !child_nodes ||
3158             child_nodes.length == 0
3159         ) {
3160             return;
3161         }
3162         
3163         var child_height = 0;
3164         
3165         for(var i = 0; i < child_nodes.length; i++) {
3166             
3167             /*
3168             * for modal with tabs...
3169             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3170                 
3171                 var layout_childs = child_nodes[i].childNodes;
3172                 
3173                 for(var j = 0; j < layout_childs.length; j++) {
3174                     
3175                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3176                         
3177                         var layout_body_childs = layout_childs[j].childNodes;
3178                         
3179                         for(var k = 0; k < layout_body_childs.length; k++) {
3180                             
3181                             if(layout_body_childs[k].classList.contains('navbar')) {
3182                                 child_height += layout_body_childs[k].offsetHeight;
3183                                 continue;
3184                             }
3185                             
3186                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3187                                 
3188                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3189                                 
3190                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3191                                     
3192                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3193                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3194                                         continue;
3195                                     }
3196                                     
3197                                 }
3198                                 
3199                             }
3200                             
3201                         }
3202                     }
3203                 }
3204                 continue;
3205             }
3206             */
3207             
3208             child_height += child_nodes[i].offsetHeight;
3209             // Roo.log(child_nodes[i].offsetHeight);
3210         }
3211         
3212         return child_height;
3213     }
3214
3215 });
3216
3217
3218 Roo.apply(Roo.bootstrap.Modal,  {
3219     /**
3220          * Button config that displays a single OK button
3221          * @type Object
3222          */
3223         OK :  [{
3224             name : 'ok',
3225             weight : 'primary',
3226             html : 'OK'
3227         }],
3228         /**
3229          * Button config that displays Yes and No buttons
3230          * @type Object
3231          */
3232         YESNO : [
3233             {
3234                 name  : 'no',
3235                 html : 'No'
3236             },
3237             {
3238                 name  :'yes',
3239                 weight : 'primary',
3240                 html : 'Yes'
3241             }
3242         ],
3243
3244         /**
3245          * Button config that displays OK and Cancel buttons
3246          * @type Object
3247          */
3248         OKCANCEL : [
3249             {
3250                name : 'cancel',
3251                 html : 'Cancel'
3252             },
3253             {
3254                 name : 'ok',
3255                 weight : 'primary',
3256                 html : 'OK'
3257             }
3258         ],
3259         /**
3260          * Button config that displays Yes, No and Cancel buttons
3261          * @type Object
3262          */
3263         YESNOCANCEL : [
3264             {
3265                 name : 'yes',
3266                 weight : 'primary',
3267                 html : 'Yes'
3268             },
3269             {
3270                 name : 'no',
3271                 html : 'No'
3272             },
3273             {
3274                 name : 'cancel',
3275                 html : 'Cancel'
3276             }
3277         ],
3278         
3279         zIndex : 10001
3280 });
3281 /*
3282  * - LGPL
3283  *
3284  * messagebox - can be used as a replace
3285  * 
3286  */
3287 /**
3288  * @class Roo.MessageBox
3289  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3290  * Example usage:
3291  *<pre><code>
3292 // Basic alert:
3293 Roo.Msg.alert('Status', 'Changes saved successfully.');
3294
3295 // Prompt for user data:
3296 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3297     if (btn == 'ok'){
3298         // process text value...
3299     }
3300 });
3301
3302 // Show a dialog using config options:
3303 Roo.Msg.show({
3304    title:'Save Changes?',
3305    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3306    buttons: Roo.Msg.YESNOCANCEL,
3307    fn: processResult,
3308    animEl: 'elId'
3309 });
3310 </code></pre>
3311  * @singleton
3312  */
3313 Roo.bootstrap.MessageBox = function(){
3314     var dlg, opt, mask, waitTimer;
3315     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3316     var buttons, activeTextEl, bwidth;
3317
3318     
3319     // private
3320     var handleButton = function(button){
3321         dlg.hide();
3322         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3323     };
3324
3325     // private
3326     var handleHide = function(){
3327         if(opt && opt.cls){
3328             dlg.el.removeClass(opt.cls);
3329         }
3330         //if(waitTimer){
3331         //    Roo.TaskMgr.stop(waitTimer);
3332         //    waitTimer = null;
3333         //}
3334     };
3335
3336     // private
3337     var updateButtons = function(b){
3338         var width = 0;
3339         if(!b){
3340             buttons["ok"].hide();
3341             buttons["cancel"].hide();
3342             buttons["yes"].hide();
3343             buttons["no"].hide();
3344             dlg.footerEl.hide();
3345             
3346             return width;
3347         }
3348         dlg.footerEl.show();
3349         for(var k in buttons){
3350             if(typeof buttons[k] != "function"){
3351                 if(b[k]){
3352                     buttons[k].show();
3353                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3354                     width += buttons[k].el.getWidth()+15;
3355                 }else{
3356                     buttons[k].hide();
3357                 }
3358             }
3359         }
3360         return width;
3361     };
3362
3363     // private
3364     var handleEsc = function(d, k, e){
3365         if(opt && opt.closable !== false){
3366             dlg.hide();
3367         }
3368         if(e){
3369             e.stopEvent();
3370         }
3371     };
3372
3373     return {
3374         /**
3375          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3376          * @return {Roo.BasicDialog} The BasicDialog element
3377          */
3378         getDialog : function(){
3379            if(!dlg){
3380                 dlg = new Roo.bootstrap.Modal( {
3381                     //draggable: true,
3382                     //resizable:false,
3383                     //constraintoviewport:false,
3384                     //fixedcenter:true,
3385                     //collapsible : false,
3386                     //shim:true,
3387                     //modal: true,
3388                 //    width: 'auto',
3389                   //  height:100,
3390                     //buttonAlign:"center",
3391                     closeClick : function(){
3392                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3393                             handleButton("no");
3394                         }else{
3395                             handleButton("cancel");
3396                         }
3397                     }
3398                 });
3399                 dlg.render();
3400                 dlg.on("hide", handleHide);
3401                 mask = dlg.mask;
3402                 //dlg.addKeyListener(27, handleEsc);
3403                 buttons = {};
3404                 this.buttons = buttons;
3405                 var bt = this.buttonText;
3406                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3407                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3408                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3409                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3410                 //Roo.log(buttons);
3411                 bodyEl = dlg.bodyEl.createChild({
3412
3413                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3414                         '<textarea class="roo-mb-textarea"></textarea>' +
3415                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3416                 });
3417                 msgEl = bodyEl.dom.firstChild;
3418                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3419                 textboxEl.enableDisplayMode();
3420                 textboxEl.addKeyListener([10,13], function(){
3421                     if(dlg.isVisible() && opt && opt.buttons){
3422                         if(opt.buttons.ok){
3423                             handleButton("ok");
3424                         }else if(opt.buttons.yes){
3425                             handleButton("yes");
3426                         }
3427                     }
3428                 });
3429                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3430                 textareaEl.enableDisplayMode();
3431                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3432                 progressEl.enableDisplayMode();
3433                 
3434                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3435                 var pf = progressEl.dom.firstChild;
3436                 if (pf) {
3437                     pp = Roo.get(pf.firstChild);
3438                     pp.setHeight(pf.offsetHeight);
3439                 }
3440                 
3441             }
3442             return dlg;
3443         },
3444
3445         /**
3446          * Updates the message box body text
3447          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3448          * the XHTML-compliant non-breaking space character '&amp;#160;')
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         updateText : function(text)
3452         {
3453             if(!dlg.isVisible() && !opt.width){
3454                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3455                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3456             }
3457             msgEl.innerHTML = text || '&#160;';
3458       
3459             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3460             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3461             var w = Math.max(
3462                     Math.min(opt.width || cw , this.maxWidth), 
3463                     Math.max(opt.minWidth || this.minWidth, bwidth)
3464             );
3465             if(opt.prompt){
3466                 activeTextEl.setWidth(w);
3467             }
3468             if(dlg.isVisible()){
3469                 dlg.fixedcenter = false;
3470             }
3471             // to big, make it scroll. = But as usual stupid IE does not support
3472             // !important..
3473             
3474             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3475                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3476                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3477             } else {
3478                 bodyEl.dom.style.height = '';
3479                 bodyEl.dom.style.overflowY = '';
3480             }
3481             if (cw > w) {
3482                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3483             } else {
3484                 bodyEl.dom.style.overflowX = '';
3485             }
3486             
3487             dlg.setContentSize(w, bodyEl.getHeight());
3488             if(dlg.isVisible()){
3489                 dlg.fixedcenter = true;
3490             }
3491             return this;
3492         },
3493
3494         /**
3495          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3496          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3497          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3498          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3499          * @return {Roo.MessageBox} This message box
3500          */
3501         updateProgress : function(value, text){
3502             if(text){
3503                 this.updateText(text);
3504             }
3505             
3506             if (pp) { // weird bug on my firefox - for some reason this is not defined
3507                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3508                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3509             }
3510             return this;
3511         },        
3512
3513         /**
3514          * Returns true if the message box is currently displayed
3515          * @return {Boolean} True if the message box is visible, else false
3516          */
3517         isVisible : function(){
3518             return dlg && dlg.isVisible();  
3519         },
3520
3521         /**
3522          * Hides the message box if it is displayed
3523          */
3524         hide : function(){
3525             if(this.isVisible()){
3526                 dlg.hide();
3527             }  
3528         },
3529
3530         /**
3531          * Displays a new message box, or reinitializes an existing message box, based on the config options
3532          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3533          * The following config object properties are supported:
3534          * <pre>
3535 Property    Type             Description
3536 ----------  ---------------  ------------------------------------------------------------------------------------
3537 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3538                                    closes (defaults to undefined)
3539 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3540                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3541 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3542                                    progress and wait dialogs will ignore this property and always hide the
3543                                    close button as they can only be closed programmatically.
3544 cls               String           A custom CSS class to apply to the message box element
3545 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3546                                    displayed (defaults to 75)
3547 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3548                                    function will be btn (the name of the button that was clicked, if applicable,
3549                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3550                                    Progress and wait dialogs will ignore this option since they do not respond to
3551                                    user actions and can only be closed programmatically, so any required function
3552                                    should be called by the same code after it closes the dialog.
3553 icon              String           A CSS class that provides a background image to be used as an icon for
3554                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3555 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3556 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3557 modal             Boolean          False to allow user interaction with the page while the message box is
3558                                    displayed (defaults to true)
3559 msg               String           A string that will replace the existing message box body text (defaults
3560                                    to the XHTML-compliant non-breaking space character '&#160;')
3561 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3562 progress          Boolean          True to display a progress bar (defaults to false)
3563 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3564 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3565 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3566 title             String           The title text
3567 value             String           The string value to set into the active textbox element if displayed
3568 wait              Boolean          True to display a progress bar (defaults to false)
3569 width             Number           The width of the dialog in pixels
3570 </pre>
3571          *
3572          * Example usage:
3573          * <pre><code>
3574 Roo.Msg.show({
3575    title: 'Address',
3576    msg: 'Please enter your address:',
3577    width: 300,
3578    buttons: Roo.MessageBox.OKCANCEL,
3579    multiline: true,
3580    fn: saveAddress,
3581    animEl: 'addAddressBtn'
3582 });
3583 </code></pre>
3584          * @param {Object} config Configuration options
3585          * @return {Roo.MessageBox} This message box
3586          */
3587         show : function(options)
3588         {
3589             
3590             // this causes nightmares if you show one dialog after another
3591             // especially on callbacks..
3592              
3593             if(this.isVisible()){
3594                 
3595                 this.hide();
3596                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3597                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3598                 Roo.log("New Dialog Message:" +  options.msg )
3599                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3600                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3601                 
3602             }
3603             var d = this.getDialog();
3604             opt = options;
3605             d.setTitle(opt.title || "&#160;");
3606             d.closeEl.setDisplayed(opt.closable !== false);
3607             activeTextEl = textboxEl;
3608             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3609             if(opt.prompt){
3610                 if(opt.multiline){
3611                     textboxEl.hide();
3612                     textareaEl.show();
3613                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3614                         opt.multiline : this.defaultTextHeight);
3615                     activeTextEl = textareaEl;
3616                 }else{
3617                     textboxEl.show();
3618                     textareaEl.hide();
3619                 }
3620             }else{
3621                 textboxEl.hide();
3622                 textareaEl.hide();
3623             }
3624             progressEl.setDisplayed(opt.progress === true);
3625             if (opt.progress) {
3626                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3627             }
3628             this.updateProgress(0);
3629             activeTextEl.dom.value = opt.value || "";
3630             if(opt.prompt){
3631                 dlg.setDefaultButton(activeTextEl);
3632             }else{
3633                 var bs = opt.buttons;
3634                 var db = null;
3635                 if(bs && bs.ok){
3636                     db = buttons["ok"];
3637                 }else if(bs && bs.yes){
3638                     db = buttons["yes"];
3639                 }
3640                 dlg.setDefaultButton(db);
3641             }
3642             bwidth = updateButtons(opt.buttons);
3643             this.updateText(opt.msg);
3644             if(opt.cls){
3645                 d.el.addClass(opt.cls);
3646             }
3647             d.proxyDrag = opt.proxyDrag === true;
3648             d.modal = opt.modal !== false;
3649             d.mask = opt.modal !== false ? mask : false;
3650             if(!d.isVisible()){
3651                 // force it to the end of the z-index stack so it gets a cursor in FF
3652                 document.body.appendChild(dlg.el.dom);
3653                 d.animateTarget = null;
3654                 d.show(options.animEl);
3655             }
3656             return this;
3657         },
3658
3659         /**
3660          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3661          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3662          * and closing the message box when the process is complete.
3663          * @param {String} title The title bar text
3664          * @param {String} msg The message box body text
3665          * @return {Roo.MessageBox} This message box
3666          */
3667         progress : function(title, msg){
3668             this.show({
3669                 title : title,
3670                 msg : msg,
3671                 buttons: false,
3672                 progress:true,
3673                 closable:false,
3674                 minWidth: this.minProgressWidth,
3675                 modal : true
3676             });
3677             return this;
3678         },
3679
3680         /**
3681          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3682          * If a callback function is passed it will be called after the user clicks the button, and the
3683          * id of the button that was clicked will be passed as the only parameter to the callback
3684          * (could also be the top-right close button).
3685          * @param {String} title The title bar text
3686          * @param {String} msg The message box body text
3687          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3688          * @param {Object} scope (optional) The scope of the callback function
3689          * @return {Roo.MessageBox} This message box
3690          */
3691         alert : function(title, msg, fn, scope)
3692         {
3693             this.show({
3694                 title : title,
3695                 msg : msg,
3696                 buttons: this.OK,
3697                 fn: fn,
3698                 closable : false,
3699                 scope : scope,
3700                 modal : true
3701             });
3702             return this;
3703         },
3704
3705         /**
3706          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3707          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3708          * You are responsible for closing the message box when the process is complete.
3709          * @param {String} msg The message box body text
3710          * @param {String} title (optional) The title bar text
3711          * @return {Roo.MessageBox} This message box
3712          */
3713         wait : function(msg, title){
3714             this.show({
3715                 title : title,
3716                 msg : msg,
3717                 buttons: false,
3718                 closable:false,
3719                 progress:true,
3720                 modal:true,
3721                 width:300,
3722                 wait:true
3723             });
3724             waitTimer = Roo.TaskMgr.start({
3725                 run: function(i){
3726                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3727                 },
3728                 interval: 1000
3729             });
3730             return this;
3731         },
3732
3733         /**
3734          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3735          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3736          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3737          * @param {String} title The title bar text
3738          * @param {String} msg The message box body text
3739          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3740          * @param {Object} scope (optional) The scope of the callback function
3741          * @return {Roo.MessageBox} This message box
3742          */
3743         confirm : function(title, msg, fn, scope){
3744             this.show({
3745                 title : title,
3746                 msg : msg,
3747                 buttons: this.YESNO,
3748                 fn: fn,
3749                 scope : scope,
3750                 modal : true
3751             });
3752             return this;
3753         },
3754
3755         /**
3756          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3757          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3758          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3759          * (could also be the top-right close button) and the text that was entered will be passed as the two
3760          * parameters to the callback.
3761          * @param {String} title The title bar text
3762          * @param {String} msg The message box body text
3763          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3764          * @param {Object} scope (optional) The scope of the callback function
3765          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3766          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3767          * @return {Roo.MessageBox} This message box
3768          */
3769         prompt : function(title, msg, fn, scope, multiline){
3770             this.show({
3771                 title : title,
3772                 msg : msg,
3773                 buttons: this.OKCANCEL,
3774                 fn: fn,
3775                 minWidth:250,
3776                 scope : scope,
3777                 prompt:true,
3778                 multiline: multiline,
3779                 modal : true
3780             });
3781             return this;
3782         },
3783
3784         /**
3785          * Button config that displays a single OK button
3786          * @type Object
3787          */
3788         OK : {ok:true},
3789         /**
3790          * Button config that displays Yes and No buttons
3791          * @type Object
3792          */
3793         YESNO : {yes:true, no:true},
3794         /**
3795          * Button config that displays OK and Cancel buttons
3796          * @type Object
3797          */
3798         OKCANCEL : {ok:true, cancel:true},
3799         /**
3800          * Button config that displays Yes, No and Cancel buttons
3801          * @type Object
3802          */
3803         YESNOCANCEL : {yes:true, no:true, cancel:true},
3804
3805         /**
3806          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3807          * @type Number
3808          */
3809         defaultTextHeight : 75,
3810         /**
3811          * The maximum width in pixels of the message box (defaults to 600)
3812          * @type Number
3813          */
3814         maxWidth : 600,
3815         /**
3816          * The minimum width in pixels of the message box (defaults to 100)
3817          * @type Number
3818          */
3819         minWidth : 100,
3820         /**
3821          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3822          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3823          * @type Number
3824          */
3825         minProgressWidth : 250,
3826         /**
3827          * An object containing the default button text strings that can be overriden for localized language support.
3828          * Supported properties are: ok, cancel, yes and no.
3829          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3830          * @type Object
3831          */
3832         buttonText : {
3833             ok : "OK",
3834             cancel : "Cancel",
3835             yes : "Yes",
3836             no : "No"
3837         }
3838     };
3839 }();
3840
3841 /**
3842  * Shorthand for {@link Roo.MessageBox}
3843  */
3844 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3845 Roo.Msg = Roo.Msg || Roo.MessageBox;
3846 /*
3847  * - LGPL
3848  *
3849  * navbar
3850  * 
3851  */
3852
3853 /**
3854  * @class Roo.bootstrap.Navbar
3855  * @extends Roo.bootstrap.Component
3856  * Bootstrap Navbar class
3857
3858  * @constructor
3859  * Create a new Navbar
3860  * @param {Object} config The config object
3861  */
3862
3863
3864 Roo.bootstrap.Navbar = function(config){
3865     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3866     this.addEvents({
3867         // raw events
3868         /**
3869          * @event beforetoggle
3870          * Fire before toggle the menu
3871          * @param {Roo.EventObject} e
3872          */
3873         "beforetoggle" : true
3874     });
3875 };
3876
3877 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3878     
3879     
3880    
3881     // private
3882     navItems : false,
3883     loadMask : false,
3884     
3885     
3886     getAutoCreate : function(){
3887         
3888         
3889         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3890         
3891     },
3892     
3893     initEvents :function ()
3894     {
3895         //Roo.log(this.el.select('.navbar-toggle',true));
3896         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3897         
3898         var mark = {
3899             tag: "div",
3900             cls:"x-dlg-mask"
3901         };
3902         
3903         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3904         
3905         var size = this.el.getSize();
3906         this.maskEl.setSize(size.width, size.height);
3907         this.maskEl.enableDisplayMode("block");
3908         this.maskEl.hide();
3909         
3910         if(this.loadMask){
3911             this.maskEl.show();
3912         }
3913     },
3914     
3915     
3916     getChildContainer : function()
3917     {
3918         if (this.el && this.el.select('.collapse').getCount()) {
3919             return this.el.select('.collapse',true).first();
3920         }
3921         
3922         return this.el;
3923     },
3924     
3925     mask : function()
3926     {
3927         this.maskEl.show();
3928     },
3929     
3930     unmask : function()
3931     {
3932         this.maskEl.hide();
3933     },
3934     onToggle : function()
3935     {
3936         
3937         if(this.fireEvent('beforetoggle', this) === false){
3938             return;
3939         }
3940         var ce = this.el.select('.navbar-collapse',true).first();
3941       
3942         if (!ce.hasClass('show')) {
3943            this.expand();
3944         } else {
3945             this.collapse();
3946         }
3947         
3948         
3949     
3950     },
3951     /**
3952      * Expand the navbar pulldown 
3953      */
3954     expand : function ()
3955     {
3956        
3957         var ce = this.el.select('.navbar-collapse',true).first();
3958         if (ce.hasClass('collapsing')) {
3959             return;
3960         }
3961         ce.dom.style.height = '';
3962                // show it...
3963         ce.addClass('in'); // old...
3964         ce.removeClass('collapse');
3965         ce.addClass('show');
3966         var h = ce.getHeight();
3967         Roo.log(h);
3968         ce.removeClass('show');
3969         // at this point we should be able to see it..
3970         ce.addClass('collapsing');
3971         
3972         ce.setHeight(0); // resize it ...
3973         ce.on('transitionend', function() {
3974             //Roo.log('done transition');
3975             ce.removeClass('collapsing');
3976             ce.addClass('show');
3977             ce.removeClass('collapse');
3978
3979             ce.dom.style.height = '';
3980         }, this, { single: true} );
3981         ce.setHeight(h);
3982         ce.dom.scrollTop = 0;
3983     },
3984     /**
3985      * Collapse the navbar pulldown 
3986      */
3987     collapse : function()
3988     {
3989          var ce = this.el.select('.navbar-collapse',true).first();
3990        
3991         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3992             // it's collapsed or collapsing..
3993             return;
3994         }
3995         ce.removeClass('in'); // old...
3996         ce.setHeight(ce.getHeight());
3997         ce.removeClass('show');
3998         ce.addClass('collapsing');
3999         
4000         ce.on('transitionend', function() {
4001             ce.dom.style.height = '';
4002             ce.removeClass('collapsing');
4003             ce.addClass('collapse');
4004         }, this, { single: true} );
4005         ce.setHeight(0);
4006     }
4007     
4008     
4009     
4010 });
4011
4012
4013
4014  
4015
4016  /*
4017  * - LGPL
4018  *
4019  * navbar
4020  * 
4021  */
4022
4023 /**
4024  * @class Roo.bootstrap.NavSimplebar
4025  * @extends Roo.bootstrap.Navbar
4026  * Bootstrap Sidebar class
4027  *
4028  * @cfg {Boolean} inverse is inverted color
4029  * 
4030  * @cfg {String} type (nav | pills | tabs)
4031  * @cfg {Boolean} arrangement stacked | justified
4032  * @cfg {String} align (left | right) alignment
4033  * 
4034  * @cfg {Boolean} main (true|false) main nav bar? default false
4035  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4036  * 
4037  * @cfg {String} tag (header|footer|nav|div) default is nav 
4038
4039  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4040  * 
4041  * 
4042  * @constructor
4043  * Create a new Sidebar
4044  * @param {Object} config The config object
4045  */
4046
4047
4048 Roo.bootstrap.NavSimplebar = function(config){
4049     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4050 };
4051
4052 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4053     
4054     inverse: false,
4055     
4056     type: false,
4057     arrangement: '',
4058     align : false,
4059     
4060     weight : 'light',
4061     
4062     main : false,
4063     
4064     
4065     tag : false,
4066     
4067     
4068     getAutoCreate : function(){
4069         
4070         
4071         var cfg = {
4072             tag : this.tag || 'div',
4073             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4074         };
4075         if (['light','white'].indexOf(this.weight) > -1) {
4076             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4077         }
4078         cfg.cls += ' bg-' + this.weight;
4079         
4080         if (this.inverse) {
4081             cfg.cls += ' navbar-inverse';
4082             
4083         }
4084         
4085         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4086         
4087         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4088             return cfg;
4089         }
4090         
4091         
4092     
4093         
4094         cfg.cn = [
4095             {
4096                 cls: 'nav nav-' + this.xtype,
4097                 tag : 'ul'
4098             }
4099         ];
4100         
4101          
4102         this.type = this.type || 'nav';
4103         if (['tabs','pills'].indexOf(this.type) != -1) {
4104             cfg.cn[0].cls += ' nav-' + this.type
4105         
4106         
4107         } else {
4108             if (this.type!=='nav') {
4109                 Roo.log('nav type must be nav/tabs/pills')
4110             }
4111             cfg.cn[0].cls += ' navbar-nav'
4112         }
4113         
4114         
4115         
4116         
4117         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4118             cfg.cn[0].cls += ' nav-' + this.arrangement;
4119         }
4120         
4121         
4122         if (this.align === 'right') {
4123             cfg.cn[0].cls += ' navbar-right';
4124         }
4125         
4126         
4127         
4128         
4129         return cfg;
4130     
4131         
4132     }
4133     
4134     
4135     
4136 });
4137
4138
4139
4140  
4141
4142  
4143        /*
4144  * - LGPL
4145  *
4146  * navbar
4147  * navbar-fixed-top
4148  * navbar-expand-md  fixed-top 
4149  */
4150
4151 /**
4152  * @class Roo.bootstrap.NavHeaderbar
4153  * @extends Roo.bootstrap.NavSimplebar
4154  * Bootstrap Sidebar class
4155  *
4156  * @cfg {String} brand what is brand
4157  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4158  * @cfg {String} brand_href href of the brand
4159  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4160  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4161  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4162  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4163  * 
4164  * @constructor
4165  * Create a new Sidebar
4166  * @param {Object} config The config object
4167  */
4168
4169
4170 Roo.bootstrap.NavHeaderbar = function(config){
4171     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4172       
4173 };
4174
4175 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4176     
4177     position: '',
4178     brand: '',
4179     brand_href: false,
4180     srButton : true,
4181     autohide : false,
4182     desktopCenter : false,
4183    
4184     
4185     getAutoCreate : function(){
4186         
4187         var   cfg = {
4188             tag: this.nav || 'nav',
4189             cls: 'navbar navbar-expand-md',
4190             role: 'navigation',
4191             cn: []
4192         };
4193         
4194         var cn = cfg.cn;
4195         if (this.desktopCenter) {
4196             cn.push({cls : 'container', cn : []});
4197             cn = cn[0].cn;
4198         }
4199         
4200         if(this.srButton){
4201             var btn = {
4202                 tag: 'button',
4203                 type: 'button',
4204                 cls: 'navbar-toggle navbar-toggler',
4205                 'data-toggle': 'collapse',
4206                 cn: [
4207                     {
4208                         tag: 'span',
4209                         cls: 'sr-only',
4210                         html: 'Toggle navigation'
4211                     },
4212                     {
4213                         tag: 'span',
4214                         cls: 'icon-bar navbar-toggler-icon'
4215                     },
4216                     {
4217                         tag: 'span',
4218                         cls: 'icon-bar'
4219                     },
4220                     {
4221                         tag: 'span',
4222                         cls: 'icon-bar'
4223                     }
4224                 ]
4225             };
4226             
4227             cn.push( Roo.bootstrap.version == 4 ? btn : {
4228                 tag: 'div',
4229                 cls: 'navbar-header',
4230                 cn: [
4231                     btn
4232                 ]
4233             });
4234         }
4235         
4236         cn.push({
4237             tag: 'div',
4238             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4239             cn : []
4240         });
4241         
4242         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4243         
4244         if (['light','white'].indexOf(this.weight) > -1) {
4245             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4246         }
4247         cfg.cls += ' bg-' + this.weight;
4248         
4249         
4250         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4251             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4252             
4253             // tag can override this..
4254             
4255             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4256         }
4257         
4258         if (this.brand !== '') {
4259             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4260             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4261                 tag: 'a',
4262                 href: this.brand_href ? this.brand_href : '#',
4263                 cls: 'navbar-brand',
4264                 cn: [
4265                 this.brand
4266                 ]
4267             });
4268         }
4269         
4270         if(this.main){
4271             cfg.cls += ' main-nav';
4272         }
4273         
4274         
4275         return cfg;
4276
4277         
4278     },
4279     getHeaderChildContainer : function()
4280     {
4281         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4282             return this.el.select('.navbar-header',true).first();
4283         }
4284         
4285         return this.getChildContainer();
4286     },
4287     
4288     
4289     initEvents : function()
4290     {
4291         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4292         
4293         if (this.autohide) {
4294             
4295             var prevScroll = 0;
4296             var ft = this.el;
4297             
4298             Roo.get(document).on('scroll',function(e) {
4299                 var ns = Roo.get(document).getScroll().top;
4300                 var os = prevScroll;
4301                 prevScroll = ns;
4302                 
4303                 if(ns > os){
4304                     ft.removeClass('slideDown');
4305                     ft.addClass('slideUp');
4306                     return;
4307                 }
4308                 ft.removeClass('slideUp');
4309                 ft.addClass('slideDown');
4310                  
4311               
4312           },this);
4313         }
4314     }    
4315     
4316 });
4317
4318
4319
4320  
4321
4322  /*
4323  * - LGPL
4324  *
4325  * navbar
4326  * 
4327  */
4328
4329 /**
4330  * @class Roo.bootstrap.NavSidebar
4331  * @extends Roo.bootstrap.Navbar
4332  * Bootstrap Sidebar class
4333  * 
4334  * @constructor
4335  * Create a new Sidebar
4336  * @param {Object} config The config object
4337  */
4338
4339
4340 Roo.bootstrap.NavSidebar = function(config){
4341     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4342 };
4343
4344 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4345     
4346     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4347     
4348     getAutoCreate : function(){
4349         
4350         
4351         return  {
4352             tag: 'div',
4353             cls: 'sidebar sidebar-nav'
4354         };
4355     
4356         
4357     }
4358     
4359     
4360     
4361 });
4362
4363
4364
4365  
4366
4367  /*
4368  * - LGPL
4369  *
4370  * nav group
4371  * 
4372  */
4373
4374 /**
4375  * @class Roo.bootstrap.NavGroup
4376  * @extends Roo.bootstrap.Component
4377  * Bootstrap NavGroup class
4378  * @cfg {String} align (left|right)
4379  * @cfg {Boolean} inverse
4380  * @cfg {String} type (nav|pills|tab) default nav
4381  * @cfg {String} navId - reference Id for navbar.
4382
4383  * 
4384  * @constructor
4385  * Create a new nav group
4386  * @param {Object} config The config object
4387  */
4388
4389 Roo.bootstrap.NavGroup = function(config){
4390     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4391     this.navItems = [];
4392    
4393     Roo.bootstrap.NavGroup.register(this);
4394      this.addEvents({
4395         /**
4396              * @event changed
4397              * Fires when the active item changes
4398              * @param {Roo.bootstrap.NavGroup} this
4399              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4400              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4401          */
4402         'changed': true
4403      });
4404     
4405 };
4406
4407 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4408     
4409     align: '',
4410     inverse: false,
4411     form: false,
4412     type: 'nav',
4413     navId : '',
4414     // private
4415     
4416     navItems : false, 
4417     
4418     getAutoCreate : function()
4419     {
4420         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4421         
4422         cfg = {
4423             tag : 'ul',
4424             cls: 'nav' 
4425         };
4426         if (Roo.bootstrap.version == 4) {
4427             if (['tabs','pills'].indexOf(this.type) != -1) {
4428                 cfg.cls += ' nav-' + this.type; 
4429             } else {
4430                 // trying to remove so header bar can right align top?
4431                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4432                     // do not use on header bar... 
4433                     cfg.cls += ' navbar-nav';
4434                 }
4435             }
4436             
4437         } else {
4438             if (['tabs','pills'].indexOf(this.type) != -1) {
4439                 cfg.cls += ' nav-' + this.type
4440             } else {
4441                 if (this.type !== 'nav') {
4442                     Roo.log('nav type must be nav/tabs/pills')
4443                 }
4444                 cfg.cls += ' navbar-nav'
4445             }
4446         }
4447         
4448         if (this.parent() && this.parent().sidebar) {
4449             cfg = {
4450                 tag: 'ul',
4451                 cls: 'dashboard-menu sidebar-menu'
4452             };
4453             
4454             return cfg;
4455         }
4456         
4457         if (this.form === true) {
4458             cfg = {
4459                 tag: 'form',
4460                 cls: 'navbar-form form-inline'
4461             };
4462             //nav navbar-right ml-md-auto
4463             if (this.align === 'right') {
4464                 cfg.cls += ' navbar-right ml-md-auto';
4465             } else {
4466                 cfg.cls += ' navbar-left';
4467             }
4468         }
4469         
4470         if (this.align === 'right') {
4471             cfg.cls += ' navbar-right ml-md-auto';
4472         } else {
4473             cfg.cls += ' mr-auto';
4474         }
4475         
4476         if (this.inverse) {
4477             cfg.cls += ' navbar-inverse';
4478             
4479         }
4480         
4481         
4482         return cfg;
4483     },
4484     /**
4485     * sets the active Navigation item
4486     * @param {Roo.bootstrap.NavItem} the new current navitem
4487     */
4488     setActiveItem : function(item)
4489     {
4490         var prev = false;
4491         Roo.each(this.navItems, function(v){
4492             if (v == item) {
4493                 return ;
4494             }
4495             if (v.isActive()) {
4496                 v.setActive(false, true);
4497                 prev = v;
4498                 
4499             }
4500             
4501         });
4502
4503         item.setActive(true, true);
4504         this.fireEvent('changed', this, item, prev);
4505         
4506         
4507     },
4508     /**
4509     * gets the active Navigation item
4510     * @return {Roo.bootstrap.NavItem} the current navitem
4511     */
4512     getActive : function()
4513     {
4514         
4515         var prev = false;
4516         Roo.each(this.navItems, function(v){
4517             
4518             if (v.isActive()) {
4519                 prev = v;
4520                 
4521             }
4522             
4523         });
4524         return prev;
4525     },
4526     
4527     indexOfNav : function()
4528     {
4529         
4530         var prev = false;
4531         Roo.each(this.navItems, function(v,i){
4532             
4533             if (v.isActive()) {
4534                 prev = i;
4535                 
4536             }
4537             
4538         });
4539         return prev;
4540     },
4541     /**
4542     * adds a Navigation item
4543     * @param {Roo.bootstrap.NavItem} the navitem to add
4544     */
4545     addItem : function(cfg)
4546     {
4547         if (this.form && Roo.bootstrap.version == 4) {
4548             cfg.tag = 'div';
4549         }
4550         var cn = new Roo.bootstrap.NavItem(cfg);
4551         this.register(cn);
4552         cn.parentId = this.id;
4553         cn.onRender(this.el, null);
4554         return cn;
4555     },
4556     /**
4557     * register a Navigation item
4558     * @param {Roo.bootstrap.NavItem} the navitem to add
4559     */
4560     register : function(item)
4561     {
4562         this.navItems.push( item);
4563         item.navId = this.navId;
4564     
4565     },
4566     
4567     /**
4568     * clear all the Navigation item
4569     */
4570    
4571     clearAll : function()
4572     {
4573         this.navItems = [];
4574         this.el.dom.innerHTML = '';
4575     },
4576     
4577     getNavItem: function(tabId)
4578     {
4579         var ret = false;
4580         Roo.each(this.navItems, function(e) {
4581             if (e.tabId == tabId) {
4582                ret =  e;
4583                return false;
4584             }
4585             return true;
4586             
4587         });
4588         return ret;
4589     },
4590     
4591     setActiveNext : function()
4592     {
4593         var i = this.indexOfNav(this.getActive());
4594         if (i > this.navItems.length) {
4595             return;
4596         }
4597         this.setActiveItem(this.navItems[i+1]);
4598     },
4599     setActivePrev : function()
4600     {
4601         var i = this.indexOfNav(this.getActive());
4602         if (i  < 1) {
4603             return;
4604         }
4605         this.setActiveItem(this.navItems[i-1]);
4606     },
4607     clearWasActive : function(except) {
4608         Roo.each(this.navItems, function(e) {
4609             if (e.tabId != except.tabId && e.was_active) {
4610                e.was_active = false;
4611                return false;
4612             }
4613             return true;
4614             
4615         });
4616     },
4617     getWasActive : function ()
4618     {
4619         var r = false;
4620         Roo.each(this.navItems, function(e) {
4621             if (e.was_active) {
4622                r = e;
4623                return false;
4624             }
4625             return true;
4626             
4627         });
4628         return r;
4629     }
4630     
4631     
4632 });
4633
4634  
4635 Roo.apply(Roo.bootstrap.NavGroup, {
4636     
4637     groups: {},
4638      /**
4639     * register a Navigation Group
4640     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4641     */
4642     register : function(navgrp)
4643     {
4644         this.groups[navgrp.navId] = navgrp;
4645         
4646     },
4647     /**
4648     * fetch a Navigation Group based on the navigation ID
4649     * @param {string} the navgroup to add
4650     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4651     */
4652     get: function(navId) {
4653         if (typeof(this.groups[navId]) == 'undefined') {
4654             return false;
4655             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4656         }
4657         return this.groups[navId] ;
4658     }
4659     
4660     
4661     
4662 });
4663
4664  /*
4665  * - LGPL
4666  *
4667  * row
4668  * 
4669  */
4670
4671 /**
4672  * @class Roo.bootstrap.NavItem
4673  * @extends Roo.bootstrap.Component
4674  * Bootstrap Navbar.NavItem class
4675  * @cfg {String} href  link to
4676  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4677
4678  * @cfg {String} html content of button
4679  * @cfg {String} badge text inside badge
4680  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4681  * @cfg {String} glyphicon DEPRICATED - use fa
4682  * @cfg {String} icon DEPRICATED - use fa
4683  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4684  * @cfg {Boolean} active Is item active
4685  * @cfg {Boolean} disabled Is item disabled
4686  
4687  * @cfg {Boolean} preventDefault (true | false) default false
4688  * @cfg {String} tabId the tab that this item activates.
4689  * @cfg {String} tagtype (a|span) render as a href or span?
4690  * @cfg {Boolean} animateRef (true|false) link to element default false  
4691   
4692  * @constructor
4693  * Create a new Navbar Item
4694  * @param {Object} config The config object
4695  */
4696 Roo.bootstrap.NavItem = function(config){
4697     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4698     this.addEvents({
4699         // raw events
4700         /**
4701          * @event click
4702          * The raw click event for the entire grid.
4703          * @param {Roo.EventObject} e
4704          */
4705         "click" : true,
4706          /**
4707             * @event changed
4708             * Fires when the active item active state changes
4709             * @param {Roo.bootstrap.NavItem} this
4710             * @param {boolean} state the new state
4711              
4712          */
4713         'changed': true,
4714         /**
4715             * @event scrollto
4716             * Fires when scroll to element
4717             * @param {Roo.bootstrap.NavItem} this
4718             * @param {Object} options
4719             * @param {Roo.EventObject} e
4720              
4721          */
4722         'scrollto': true
4723     });
4724    
4725 };
4726
4727 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4728     
4729     href: false,
4730     html: '',
4731     badge: '',
4732     icon: false,
4733     fa : false,
4734     glyphicon: false,
4735     active: false,
4736     preventDefault : false,
4737     tabId : false,
4738     tagtype : 'a',
4739     tag: 'li',
4740     disabled : false,
4741     animateRef : false,
4742     was_active : false,
4743     button_weight : '',
4744     button_outline : false,
4745     
4746     navLink: false,
4747     
4748     getAutoCreate : function(){
4749          
4750         var cfg = {
4751             tag: this.tag,
4752             cls: 'nav-item'
4753         };
4754         
4755         if (this.active) {
4756             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4757         }
4758         if (this.disabled) {
4759             cfg.cls += ' disabled';
4760         }
4761         
4762         // BS4 only?
4763         if (this.button_weight.length) {
4764             cfg.tag = this.href ? 'a' : 'button';
4765             cfg.html = this.html || '';
4766             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4767             if (this.href) {
4768                 cfg.href = this.href;
4769             }
4770             if (this.fa) {
4771                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4772             }
4773             
4774             // menu .. should add dropdown-menu class - so no need for carat..
4775             
4776             if (this.badge !== '') {
4777                  
4778                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4779             }
4780             return cfg;
4781         }
4782         
4783         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4784             cfg.cn = [
4785                 {
4786                     tag: this.tagtype,
4787                     href : this.href || "#",
4788                     html: this.html || ''
4789                 }
4790             ];
4791             if (this.tagtype == 'a') {
4792                 cfg.cn[0].cls = 'nav-link';
4793             }
4794             if (this.icon) {
4795                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4796             }
4797             if (this.fa) {
4798                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4799             }
4800             if(this.glyphicon) {
4801                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4802             }
4803             
4804             if (this.menu) {
4805                 
4806                 cfg.cn[0].html += " <span class='caret'></span>";
4807              
4808             }
4809             
4810             if (this.badge !== '') {
4811                  
4812                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4813             }
4814         }
4815         
4816         
4817         
4818         return cfg;
4819     },
4820     onRender : function(ct, position)
4821     {
4822        // Roo.log("Call onRender: " + this.xtype);
4823         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4824             this.tag = 'div';
4825         }
4826         
4827         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4828         this.navLink = this.el.select('.nav-link',true).first();
4829         return ret;
4830     },
4831       
4832     
4833     initEvents: function() 
4834     {
4835         if (typeof (this.menu) != 'undefined') {
4836             this.menu.parentType = this.xtype;
4837             this.menu.triggerEl = this.el;
4838             this.menu = this.addxtype(Roo.apply({}, this.menu));
4839         }
4840         
4841         this.el.select('a',true).on('click', this.onClick, this);
4842         
4843         if(this.tagtype == 'span'){
4844             this.el.select('span',true).on('click', this.onClick, this);
4845         }
4846        
4847         // at this point parent should be available..
4848         this.parent().register(this);
4849     },
4850     
4851     onClick : function(e)
4852     {
4853         if (e.getTarget('.dropdown-menu-item')) {
4854             // did you click on a menu itemm.... - then don't trigger onclick..
4855             return;
4856         }
4857         
4858         if(
4859                 this.preventDefault || 
4860                 this.href == '#' 
4861         ){
4862             Roo.log("NavItem - prevent Default?");
4863             e.preventDefault();
4864         }
4865         
4866         if (this.disabled) {
4867             return;
4868         }
4869         
4870         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4871         if (tg && tg.transition) {
4872             Roo.log("waiting for the transitionend");
4873             return;
4874         }
4875         
4876         
4877         
4878         //Roo.log("fire event clicked");
4879         if(this.fireEvent('click', this, e) === false){
4880             return;
4881         };
4882         
4883         if(this.tagtype == 'span'){
4884             return;
4885         }
4886         
4887         //Roo.log(this.href);
4888         var ael = this.el.select('a',true).first();
4889         //Roo.log(ael);
4890         
4891         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4892             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4893             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4894                 return; // ignore... - it's a 'hash' to another page.
4895             }
4896             Roo.log("NavItem - prevent Default?");
4897             e.preventDefault();
4898             this.scrollToElement(e);
4899         }
4900         
4901         
4902         var p =  this.parent();
4903    
4904         if (['tabs','pills'].indexOf(p.type)!==-1) {
4905             if (typeof(p.setActiveItem) !== 'undefined') {
4906                 p.setActiveItem(this);
4907             }
4908         }
4909         
4910         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4911         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4912             // remove the collapsed menu expand...
4913             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
4914         }
4915     },
4916     
4917     isActive: function () {
4918         return this.active
4919     },
4920     setActive : function(state, fire, is_was_active)
4921     {
4922         if (this.active && !state && this.navId) {
4923             this.was_active = true;
4924             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4925             if (nv) {
4926                 nv.clearWasActive(this);
4927             }
4928             
4929         }
4930         this.active = state;
4931         
4932         if (!state ) {
4933             this.el.removeClass('active');
4934             this.navLink ? this.navLink.removeClass('active') : false;
4935         } else if (!this.el.hasClass('active')) {
4936             
4937             this.el.addClass('active');
4938             if (Roo.bootstrap.version == 4 && this.navLink ) {
4939                 this.navLink.addClass('active');
4940             }
4941             
4942         }
4943         if (fire) {
4944             this.fireEvent('changed', this, state);
4945         }
4946         
4947         // show a panel if it's registered and related..
4948         
4949         if (!this.navId || !this.tabId || !state || is_was_active) {
4950             return;
4951         }
4952         
4953         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4954         if (!tg) {
4955             return;
4956         }
4957         var pan = tg.getPanelByName(this.tabId);
4958         if (!pan) {
4959             return;
4960         }
4961         // if we can not flip to new panel - go back to old nav highlight..
4962         if (false == tg.showPanel(pan)) {
4963             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4964             if (nv) {
4965                 var onav = nv.getWasActive();
4966                 if (onav) {
4967                     onav.setActive(true, false, true);
4968                 }
4969             }
4970             
4971         }
4972         
4973         
4974         
4975     },
4976      // this should not be here...
4977     setDisabled : function(state)
4978     {
4979         this.disabled = state;
4980         if (!state ) {
4981             this.el.removeClass('disabled');
4982         } else if (!this.el.hasClass('disabled')) {
4983             this.el.addClass('disabled');
4984         }
4985         
4986     },
4987     
4988     /**
4989      * Fetch the element to display the tooltip on.
4990      * @return {Roo.Element} defaults to this.el
4991      */
4992     tooltipEl : function()
4993     {
4994         return this.el.select('' + this.tagtype + '', true).first();
4995     },
4996     
4997     scrollToElement : function(e)
4998     {
4999         var c = document.body;
5000         
5001         /*
5002          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5003          */
5004         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5005             c = document.documentElement;
5006         }
5007         
5008         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5009         
5010         if(!target){
5011             return;
5012         }
5013
5014         var o = target.calcOffsetsTo(c);
5015         
5016         var options = {
5017             target : target,
5018             value : o[1]
5019         };
5020         
5021         this.fireEvent('scrollto', this, options, e);
5022         
5023         Roo.get(c).scrollTo('top', options.value, true);
5024         
5025         return;
5026     }
5027 });
5028  
5029
5030  /*
5031  * - LGPL
5032  *
5033  * sidebar item
5034  *
5035  *  li
5036  *    <span> icon </span>
5037  *    <span> text </span>
5038  *    <span>badge </span>
5039  */
5040
5041 /**
5042  * @class Roo.bootstrap.NavSidebarItem
5043  * @extends Roo.bootstrap.NavItem
5044  * Bootstrap Navbar.NavSidebarItem class
5045  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5046  * {Boolean} open is the menu open
5047  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5048  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5049  * {String} buttonSize (sm|md|lg)the extra classes for the button
5050  * {Boolean} showArrow show arrow next to the text (default true)
5051  * @constructor
5052  * Create a new Navbar Button
5053  * @param {Object} config The config object
5054  */
5055 Roo.bootstrap.NavSidebarItem = function(config){
5056     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5057     this.addEvents({
5058         // raw events
5059         /**
5060          * @event click
5061          * The raw click event for the entire grid.
5062          * @param {Roo.EventObject} e
5063          */
5064         "click" : true,
5065          /**
5066             * @event changed
5067             * Fires when the active item active state changes
5068             * @param {Roo.bootstrap.NavSidebarItem} this
5069             * @param {boolean} state the new state
5070              
5071          */
5072         'changed': true
5073     });
5074    
5075 };
5076
5077 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5078     
5079     badgeWeight : 'default',
5080     
5081     open: false,
5082     
5083     buttonView : false,
5084     
5085     buttonWeight : 'default',
5086     
5087     buttonSize : 'md',
5088     
5089     showArrow : true,
5090     
5091     getAutoCreate : function(){
5092         
5093         
5094         var a = {
5095                 tag: 'a',
5096                 href : this.href || '#',
5097                 cls: '',
5098                 html : '',
5099                 cn : []
5100         };
5101         
5102         if(this.buttonView){
5103             a = {
5104                 tag: 'button',
5105                 href : this.href || '#',
5106                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5107                 html : this.html,
5108                 cn : []
5109             };
5110         }
5111         
5112         var cfg = {
5113             tag: 'li',
5114             cls: '',
5115             cn: [ a ]
5116         };
5117         
5118         if (this.active) {
5119             cfg.cls += ' active';
5120         }
5121         
5122         if (this.disabled) {
5123             cfg.cls += ' disabled';
5124         }
5125         if (this.open) {
5126             cfg.cls += ' open x-open';
5127         }
5128         // left icon..
5129         if (this.glyphicon || this.icon) {
5130             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5131             a.cn.push({ tag : 'i', cls : c }) ;
5132         }
5133         
5134         if(!this.buttonView){
5135             var span = {
5136                 tag: 'span',
5137                 html : this.html || ''
5138             };
5139
5140             a.cn.push(span);
5141             
5142         }
5143         
5144         if (this.badge !== '') {
5145             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5146         }
5147         
5148         if (this.menu) {
5149             
5150             if(this.showArrow){
5151                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5152             }
5153             
5154             a.cls += ' dropdown-toggle treeview' ;
5155         }
5156         
5157         return cfg;
5158     },
5159     
5160     initEvents : function()
5161     { 
5162         if (typeof (this.menu) != 'undefined') {
5163             this.menu.parentType = this.xtype;
5164             this.menu.triggerEl = this.el;
5165             this.menu = this.addxtype(Roo.apply({}, this.menu));
5166         }
5167         
5168         this.el.on('click', this.onClick, this);
5169         
5170         if(this.badge !== ''){
5171             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5172         }
5173         
5174     },
5175     
5176     onClick : function(e)
5177     {
5178         if(this.disabled){
5179             e.preventDefault();
5180             return;
5181         }
5182         
5183         if(this.preventDefault){
5184             e.preventDefault();
5185         }
5186         
5187         this.fireEvent('click', this, e);
5188     },
5189     
5190     disable : function()
5191     {
5192         this.setDisabled(true);
5193     },
5194     
5195     enable : function()
5196     {
5197         this.setDisabled(false);
5198     },
5199     
5200     setDisabled : function(state)
5201     {
5202         if(this.disabled == state){
5203             return;
5204         }
5205         
5206         this.disabled = state;
5207         
5208         if (state) {
5209             this.el.addClass('disabled');
5210             return;
5211         }
5212         
5213         this.el.removeClass('disabled');
5214         
5215         return;
5216     },
5217     
5218     setActive : function(state)
5219     {
5220         if(this.active == state){
5221             return;
5222         }
5223         
5224         this.active = state;
5225         
5226         if (state) {
5227             this.el.addClass('active');
5228             return;
5229         }
5230         
5231         this.el.removeClass('active');
5232         
5233         return;
5234     },
5235     
5236     isActive: function () 
5237     {
5238         return this.active;
5239     },
5240     
5241     setBadge : function(str)
5242     {
5243         if(!this.badgeEl){
5244             return;
5245         }
5246         
5247         this.badgeEl.dom.innerHTML = str;
5248     }
5249     
5250    
5251      
5252  
5253 });
5254  
5255
5256  /*
5257  * - LGPL
5258  *
5259  * row
5260  * 
5261  */
5262
5263 /**
5264  * @class Roo.bootstrap.Row
5265  * @extends Roo.bootstrap.Component
5266  * Bootstrap Row class (contains columns...)
5267  * 
5268  * @constructor
5269  * Create a new Row
5270  * @param {Object} config The config object
5271  */
5272
5273 Roo.bootstrap.Row = function(config){
5274     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5275 };
5276
5277 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5278     
5279     getAutoCreate : function(){
5280        return {
5281             cls: 'row clearfix'
5282        };
5283     }
5284     
5285     
5286 });
5287
5288  
5289
5290  /*
5291  * - LGPL
5292  *
5293  * element
5294  * 
5295  */
5296
5297 /**
5298  * @class Roo.bootstrap.Element
5299  * @extends Roo.bootstrap.Component
5300  * Bootstrap Element class
5301  * @cfg {String} html contents of the element
5302  * @cfg {String} tag tag of the element
5303  * @cfg {String} cls class of the element
5304  * @cfg {Boolean} preventDefault (true|false) default false
5305  * @cfg {Boolean} clickable (true|false) default false
5306  * 
5307  * @constructor
5308  * Create a new Element
5309  * @param {Object} config The config object
5310  */
5311
5312 Roo.bootstrap.Element = function(config){
5313     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5314     
5315     this.addEvents({
5316         // raw events
5317         /**
5318          * @event click
5319          * When a element is chick
5320          * @param {Roo.bootstrap.Element} this
5321          * @param {Roo.EventObject} e
5322          */
5323         "click" : true
5324     });
5325 };
5326
5327 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5328     
5329     tag: 'div',
5330     cls: '',
5331     html: '',
5332     preventDefault: false, 
5333     clickable: false,
5334     
5335     getAutoCreate : function(){
5336         
5337         var cfg = {
5338             tag: this.tag,
5339             // cls: this.cls, double assign in parent class Component.js :: onRender
5340             html: this.html
5341         };
5342         
5343         return cfg;
5344     },
5345     
5346     initEvents: function() 
5347     {
5348         Roo.bootstrap.Element.superclass.initEvents.call(this);
5349         
5350         if(this.clickable){
5351             this.el.on('click', this.onClick, this);
5352         }
5353         
5354     },
5355     
5356     onClick : function(e)
5357     {
5358         if(this.preventDefault){
5359             e.preventDefault();
5360         }
5361         
5362         this.fireEvent('click', this, e);
5363     },
5364     
5365     getValue : function()
5366     {
5367         return this.el.dom.innerHTML;
5368     },
5369     
5370     setValue : function(value)
5371     {
5372         this.el.dom.innerHTML = value;
5373     }
5374    
5375 });
5376
5377  
5378
5379  /*
5380  * - LGPL
5381  *
5382  * pagination
5383  * 
5384  */
5385
5386 /**
5387  * @class Roo.bootstrap.Pagination
5388  * @extends Roo.bootstrap.Component
5389  * Bootstrap Pagination class
5390  * @cfg {String} size xs | sm | md | lg
5391  * @cfg {Boolean} inverse false | true
5392  * 
5393  * @constructor
5394  * Create a new Pagination
5395  * @param {Object} config The config object
5396  */
5397
5398 Roo.bootstrap.Pagination = function(config){
5399     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5400 };
5401
5402 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5403     
5404     cls: false,
5405     size: false,
5406     inverse: false,
5407     
5408     getAutoCreate : function(){
5409         var cfg = {
5410             tag: 'ul',
5411                 cls: 'pagination'
5412         };
5413         if (this.inverse) {
5414             cfg.cls += ' inverse';
5415         }
5416         if (this.html) {
5417             cfg.html=this.html;
5418         }
5419         if (this.cls) {
5420             cfg.cls += " " + this.cls;
5421         }
5422         return cfg;
5423     }
5424    
5425 });
5426
5427  
5428
5429  /*
5430  * - LGPL
5431  *
5432  * Pagination item
5433  * 
5434  */
5435
5436
5437 /**
5438  * @class Roo.bootstrap.PaginationItem
5439  * @extends Roo.bootstrap.Component
5440  * Bootstrap PaginationItem class
5441  * @cfg {String} html text
5442  * @cfg {String} href the link
5443  * @cfg {Boolean} preventDefault (true | false) default true
5444  * @cfg {Boolean} active (true | false) default false
5445  * @cfg {Boolean} disabled default false
5446  * 
5447  * 
5448  * @constructor
5449  * Create a new PaginationItem
5450  * @param {Object} config The config object
5451  */
5452
5453
5454 Roo.bootstrap.PaginationItem = function(config){
5455     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5456     this.addEvents({
5457         // raw events
5458         /**
5459          * @event click
5460          * The raw click event for the entire grid.
5461          * @param {Roo.EventObject} e
5462          */
5463         "click" : true
5464     });
5465 };
5466
5467 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5468     
5469     href : false,
5470     html : false,
5471     preventDefault: true,
5472     active : false,
5473     cls : false,
5474     disabled: false,
5475     
5476     getAutoCreate : function(){
5477         var cfg= {
5478             tag: 'li',
5479             cn: [
5480                 {
5481                     tag : 'a',
5482                     href : this.href ? this.href : '#',
5483                     html : this.html ? this.html : ''
5484                 }
5485             ]
5486         };
5487         
5488         if(this.cls){
5489             cfg.cls = this.cls;
5490         }
5491         
5492         if(this.disabled){
5493             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5494         }
5495         
5496         if(this.active){
5497             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5498         }
5499         
5500         return cfg;
5501     },
5502     
5503     initEvents: function() {
5504         
5505         this.el.on('click', this.onClick, this);
5506         
5507     },
5508     onClick : function(e)
5509     {
5510         Roo.log('PaginationItem on click ');
5511         if(this.preventDefault){
5512             e.preventDefault();
5513         }
5514         
5515         if(this.disabled){
5516             return;
5517         }
5518         
5519         this.fireEvent('click', this, e);
5520     }
5521    
5522 });
5523
5524  
5525
5526  /*
5527  * - LGPL
5528  *
5529  * slider
5530  * 
5531  */
5532
5533
5534 /**
5535  * @class Roo.bootstrap.Slider
5536  * @extends Roo.bootstrap.Component
5537  * Bootstrap Slider class
5538  *    
5539  * @constructor
5540  * Create a new Slider
5541  * @param {Object} config The config object
5542  */
5543
5544 Roo.bootstrap.Slider = function(config){
5545     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5546 };
5547
5548 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5549     
5550     getAutoCreate : function(){
5551         
5552         var cfg = {
5553             tag: 'div',
5554             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5555             cn: [
5556                 {
5557                     tag: 'a',
5558                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5559                 }
5560             ]
5561         };
5562         
5563         return cfg;
5564     }
5565    
5566 });
5567
5568  /*
5569  * Based on:
5570  * Ext JS Library 1.1.1
5571  * Copyright(c) 2006-2007, Ext JS, LLC.
5572  *
5573  * Originally Released Under LGPL - original licence link has changed is not relivant.
5574  *
5575  * Fork - LGPL
5576  * <script type="text/javascript">
5577  */
5578  
5579
5580 /**
5581  * @class Roo.grid.ColumnModel
5582  * @extends Roo.util.Observable
5583  * This is the default implementation of a ColumnModel used by the Grid. It defines
5584  * the columns in the grid.
5585  * <br>Usage:<br>
5586  <pre><code>
5587  var colModel = new Roo.grid.ColumnModel([
5588         {header: "Ticker", width: 60, sortable: true, locked: true},
5589         {header: "Company Name", width: 150, sortable: true},
5590         {header: "Market Cap.", width: 100, sortable: true},
5591         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5592         {header: "Employees", width: 100, sortable: true, resizable: false}
5593  ]);
5594  </code></pre>
5595  * <p>
5596  
5597  * The config options listed for this class are options which may appear in each
5598  * individual column definition.
5599  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5600  * @constructor
5601  * @param {Object} config An Array of column config objects. See this class's
5602  * config objects for details.
5603 */
5604 Roo.grid.ColumnModel = function(config){
5605         /**
5606      * The config passed into the constructor
5607      */
5608     this.config = config;
5609     this.lookup = {};
5610
5611     // if no id, create one
5612     // if the column does not have a dataIndex mapping,
5613     // map it to the order it is in the config
5614     for(var i = 0, len = config.length; i < len; i++){
5615         var c = config[i];
5616         if(typeof c.dataIndex == "undefined"){
5617             c.dataIndex = i;
5618         }
5619         if(typeof c.renderer == "string"){
5620             c.renderer = Roo.util.Format[c.renderer];
5621         }
5622         if(typeof c.id == "undefined"){
5623             c.id = Roo.id();
5624         }
5625         if(c.editor && c.editor.xtype){
5626             c.editor  = Roo.factory(c.editor, Roo.grid);
5627         }
5628         if(c.editor && c.editor.isFormField){
5629             c.editor = new Roo.grid.GridEditor(c.editor);
5630         }
5631         this.lookup[c.id] = c;
5632     }
5633
5634     /**
5635      * The width of columns which have no width specified (defaults to 100)
5636      * @type Number
5637      */
5638     this.defaultWidth = 100;
5639
5640     /**
5641      * Default sortable of columns which have no sortable specified (defaults to false)
5642      * @type Boolean
5643      */
5644     this.defaultSortable = false;
5645
5646     this.addEvents({
5647         /**
5648              * @event widthchange
5649              * Fires when the width of a column changes.
5650              * @param {ColumnModel} this
5651              * @param {Number} columnIndex The column index
5652              * @param {Number} newWidth The new width
5653              */
5654             "widthchange": true,
5655         /**
5656              * @event headerchange
5657              * Fires when the text of a header changes.
5658              * @param {ColumnModel} this
5659              * @param {Number} columnIndex The column index
5660              * @param {Number} newText The new header text
5661              */
5662             "headerchange": true,
5663         /**
5664              * @event hiddenchange
5665              * Fires when a column is hidden or "unhidden".
5666              * @param {ColumnModel} this
5667              * @param {Number} columnIndex The column index
5668              * @param {Boolean} hidden true if hidden, false otherwise
5669              */
5670             "hiddenchange": true,
5671             /**
5672          * @event columnmoved
5673          * Fires when a column is moved.
5674          * @param {ColumnModel} this
5675          * @param {Number} oldIndex
5676          * @param {Number} newIndex
5677          */
5678         "columnmoved" : true,
5679         /**
5680          * @event columlockchange
5681          * Fires when a column's locked state is changed
5682          * @param {ColumnModel} this
5683          * @param {Number} colIndex
5684          * @param {Boolean} locked true if locked
5685          */
5686         "columnlockchange" : true
5687     });
5688     Roo.grid.ColumnModel.superclass.constructor.call(this);
5689 };
5690 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5691     /**
5692      * @cfg {String} header The header text to display in the Grid view.
5693      */
5694     /**
5695      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5696      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5697      * specified, the column's index is used as an index into the Record's data Array.
5698      */
5699     /**
5700      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5701      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5702      */
5703     /**
5704      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5705      * Defaults to the value of the {@link #defaultSortable} property.
5706      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5707      */
5708     /**
5709      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5710      */
5711     /**
5712      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5713      */
5714     /**
5715      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5716      */
5717     /**
5718      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5719      */
5720     /**
5721      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5722      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5723      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5724      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5725      */
5726        /**
5727      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5728      */
5729     /**
5730      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5731      */
5732     /**
5733      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5734      */
5735     /**
5736      * @cfg {String} cursor (Optional)
5737      */
5738     /**
5739      * @cfg {String} tooltip (Optional)
5740      */
5741     /**
5742      * @cfg {Number} xs (Optional)
5743      */
5744     /**
5745      * @cfg {Number} sm (Optional)
5746      */
5747     /**
5748      * @cfg {Number} md (Optional)
5749      */
5750     /**
5751      * @cfg {Number} lg (Optional)
5752      */
5753     /**
5754      * Returns the id of the column at the specified index.
5755      * @param {Number} index The column index
5756      * @return {String} the id
5757      */
5758     getColumnId : function(index){
5759         return this.config[index].id;
5760     },
5761
5762     /**
5763      * Returns the column for a specified id.
5764      * @param {String} id The column id
5765      * @return {Object} the column
5766      */
5767     getColumnById : function(id){
5768         return this.lookup[id];
5769     },
5770
5771     
5772     /**
5773      * Returns the column for a specified dataIndex.
5774      * @param {String} dataIndex The column dataIndex
5775      * @return {Object|Boolean} the column or false if not found
5776      */
5777     getColumnByDataIndex: function(dataIndex){
5778         var index = this.findColumnIndex(dataIndex);
5779         return index > -1 ? this.config[index] : false;
5780     },
5781     
5782     /**
5783      * Returns the index for a specified column id.
5784      * @param {String} id The column id
5785      * @return {Number} the index, or -1 if not found
5786      */
5787     getIndexById : function(id){
5788         for(var i = 0, len = this.config.length; i < len; i++){
5789             if(this.config[i].id == id){
5790                 return i;
5791             }
5792         }
5793         return -1;
5794     },
5795     
5796     /**
5797      * Returns the index for a specified column dataIndex.
5798      * @param {String} dataIndex The column dataIndex
5799      * @return {Number} the index, or -1 if not found
5800      */
5801     
5802     findColumnIndex : function(dataIndex){
5803         for(var i = 0, len = this.config.length; i < len; i++){
5804             if(this.config[i].dataIndex == dataIndex){
5805                 return i;
5806             }
5807         }
5808         return -1;
5809     },
5810     
5811     
5812     moveColumn : function(oldIndex, newIndex){
5813         var c = this.config[oldIndex];
5814         this.config.splice(oldIndex, 1);
5815         this.config.splice(newIndex, 0, c);
5816         this.dataMap = null;
5817         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5818     },
5819
5820     isLocked : function(colIndex){
5821         return this.config[colIndex].locked === true;
5822     },
5823
5824     setLocked : function(colIndex, value, suppressEvent){
5825         if(this.isLocked(colIndex) == value){
5826             return;
5827         }
5828         this.config[colIndex].locked = value;
5829         if(!suppressEvent){
5830             this.fireEvent("columnlockchange", this, colIndex, value);
5831         }
5832     },
5833
5834     getTotalLockedWidth : function(){
5835         var totalWidth = 0;
5836         for(var i = 0; i < this.config.length; i++){
5837             if(this.isLocked(i) && !this.isHidden(i)){
5838                 this.totalWidth += this.getColumnWidth(i);
5839             }
5840         }
5841         return totalWidth;
5842     },
5843
5844     getLockedCount : function(){
5845         for(var i = 0, len = this.config.length; i < len; i++){
5846             if(!this.isLocked(i)){
5847                 return i;
5848             }
5849         }
5850         
5851         return this.config.length;
5852     },
5853
5854     /**
5855      * Returns the number of columns.
5856      * @return {Number}
5857      */
5858     getColumnCount : function(visibleOnly){
5859         if(visibleOnly === true){
5860             var c = 0;
5861             for(var i = 0, len = this.config.length; i < len; i++){
5862                 if(!this.isHidden(i)){
5863                     c++;
5864                 }
5865             }
5866             return c;
5867         }
5868         return this.config.length;
5869     },
5870
5871     /**
5872      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5873      * @param {Function} fn
5874      * @param {Object} scope (optional)
5875      * @return {Array} result
5876      */
5877     getColumnsBy : function(fn, scope){
5878         var r = [];
5879         for(var i = 0, len = this.config.length; i < len; i++){
5880             var c = this.config[i];
5881             if(fn.call(scope||this, c, i) === true){
5882                 r[r.length] = c;
5883             }
5884         }
5885         return r;
5886     },
5887
5888     /**
5889      * Returns true if the specified column is sortable.
5890      * @param {Number} col The column index
5891      * @return {Boolean}
5892      */
5893     isSortable : function(col){
5894         if(typeof this.config[col].sortable == "undefined"){
5895             return this.defaultSortable;
5896         }
5897         return this.config[col].sortable;
5898     },
5899
5900     /**
5901      * Returns the rendering (formatting) function defined for the column.
5902      * @param {Number} col The column index.
5903      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5904      */
5905     getRenderer : function(col){
5906         if(!this.config[col].renderer){
5907             return Roo.grid.ColumnModel.defaultRenderer;
5908         }
5909         return this.config[col].renderer;
5910     },
5911
5912     /**
5913      * Sets the rendering (formatting) function for a column.
5914      * @param {Number} col The column index
5915      * @param {Function} fn The function to use to process the cell's raw data
5916      * to return HTML markup for the grid view. The render function is called with
5917      * the following parameters:<ul>
5918      * <li>Data value.</li>
5919      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5920      * <li>css A CSS style string to apply to the table cell.</li>
5921      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5922      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5923      * <li>Row index</li>
5924      * <li>Column index</li>
5925      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5926      */
5927     setRenderer : function(col, fn){
5928         this.config[col].renderer = fn;
5929     },
5930
5931     /**
5932      * Returns the width for the specified column.
5933      * @param {Number} col The column index
5934      * @return {Number}
5935      */
5936     getColumnWidth : function(col){
5937         return this.config[col].width * 1 || this.defaultWidth;
5938     },
5939
5940     /**
5941      * Sets the width for a column.
5942      * @param {Number} col The column index
5943      * @param {Number} width The new width
5944      */
5945     setColumnWidth : function(col, width, suppressEvent){
5946         this.config[col].width = width;
5947         this.totalWidth = null;
5948         if(!suppressEvent){
5949              this.fireEvent("widthchange", this, col, width);
5950         }
5951     },
5952
5953     /**
5954      * Returns the total width of all columns.
5955      * @param {Boolean} includeHidden True to include hidden column widths
5956      * @return {Number}
5957      */
5958     getTotalWidth : function(includeHidden){
5959         if(!this.totalWidth){
5960             this.totalWidth = 0;
5961             for(var i = 0, len = this.config.length; i < len; i++){
5962                 if(includeHidden || !this.isHidden(i)){
5963                     this.totalWidth += this.getColumnWidth(i);
5964                 }
5965             }
5966         }
5967         return this.totalWidth;
5968     },
5969
5970     /**
5971      * Returns the header for the specified column.
5972      * @param {Number} col The column index
5973      * @return {String}
5974      */
5975     getColumnHeader : function(col){
5976         return this.config[col].header;
5977     },
5978
5979     /**
5980      * Sets the header for a column.
5981      * @param {Number} col The column index
5982      * @param {String} header The new header
5983      */
5984     setColumnHeader : function(col, header){
5985         this.config[col].header = header;
5986         this.fireEvent("headerchange", this, col, header);
5987     },
5988
5989     /**
5990      * Returns the tooltip for the specified column.
5991      * @param {Number} col The column index
5992      * @return {String}
5993      */
5994     getColumnTooltip : function(col){
5995             return this.config[col].tooltip;
5996     },
5997     /**
5998      * Sets the tooltip for a column.
5999      * @param {Number} col The column index
6000      * @param {String} tooltip The new tooltip
6001      */
6002     setColumnTooltip : function(col, tooltip){
6003             this.config[col].tooltip = tooltip;
6004     },
6005
6006     /**
6007      * Returns the dataIndex for the specified column.
6008      * @param {Number} col The column index
6009      * @return {Number}
6010      */
6011     getDataIndex : function(col){
6012         return this.config[col].dataIndex;
6013     },
6014
6015     /**
6016      * Sets the dataIndex for a column.
6017      * @param {Number} col The column index
6018      * @param {Number} dataIndex The new dataIndex
6019      */
6020     setDataIndex : function(col, dataIndex){
6021         this.config[col].dataIndex = dataIndex;
6022     },
6023
6024     
6025     
6026     /**
6027      * Returns true if the cell is editable.
6028      * @param {Number} colIndex The column index
6029      * @param {Number} rowIndex The row index - this is nto actually used..?
6030      * @return {Boolean}
6031      */
6032     isCellEditable : function(colIndex, rowIndex){
6033         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6034     },
6035
6036     /**
6037      * Returns the editor defined for the cell/column.
6038      * return false or null to disable editing.
6039      * @param {Number} colIndex The column index
6040      * @param {Number} rowIndex The row index
6041      * @return {Object}
6042      */
6043     getCellEditor : function(colIndex, rowIndex){
6044         return this.config[colIndex].editor;
6045     },
6046
6047     /**
6048      * Sets if a column is editable.
6049      * @param {Number} col The column index
6050      * @param {Boolean} editable True if the column is editable
6051      */
6052     setEditable : function(col, editable){
6053         this.config[col].editable = editable;
6054     },
6055
6056
6057     /**
6058      * Returns true if the column is hidden.
6059      * @param {Number} colIndex The column index
6060      * @return {Boolean}
6061      */
6062     isHidden : function(colIndex){
6063         return this.config[colIndex].hidden;
6064     },
6065
6066
6067     /**
6068      * Returns true if the column width cannot be changed
6069      */
6070     isFixed : function(colIndex){
6071         return this.config[colIndex].fixed;
6072     },
6073
6074     /**
6075      * Returns true if the column can be resized
6076      * @return {Boolean}
6077      */
6078     isResizable : function(colIndex){
6079         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6080     },
6081     /**
6082      * Sets if a column is hidden.
6083      * @param {Number} colIndex The column index
6084      * @param {Boolean} hidden True if the column is hidden
6085      */
6086     setHidden : function(colIndex, hidden){
6087         this.config[colIndex].hidden = hidden;
6088         this.totalWidth = null;
6089         this.fireEvent("hiddenchange", this, colIndex, hidden);
6090     },
6091
6092     /**
6093      * Sets the editor for a column.
6094      * @param {Number} col The column index
6095      * @param {Object} editor The editor object
6096      */
6097     setEditor : function(col, editor){
6098         this.config[col].editor = editor;
6099     }
6100 });
6101
6102 Roo.grid.ColumnModel.defaultRenderer = function(value)
6103 {
6104     if(typeof value == "object") {
6105         return value;
6106     }
6107         if(typeof value == "string" && value.length < 1){
6108             return "&#160;";
6109         }
6110     
6111         return String.format("{0}", value);
6112 };
6113
6114 // Alias for backwards compatibility
6115 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6116 /*
6117  * Based on:
6118  * Ext JS Library 1.1.1
6119  * Copyright(c) 2006-2007, Ext JS, LLC.
6120  *
6121  * Originally Released Under LGPL - original licence link has changed is not relivant.
6122  *
6123  * Fork - LGPL
6124  * <script type="text/javascript">
6125  */
6126  
6127 /**
6128  * @class Roo.LoadMask
6129  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6130  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6131  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6132  * element's UpdateManager load indicator and will be destroyed after the initial load.
6133  * @constructor
6134  * Create a new LoadMask
6135  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6136  * @param {Object} config The config object
6137  */
6138 Roo.LoadMask = function(el, config){
6139     this.el = Roo.get(el);
6140     Roo.apply(this, config);
6141     if(this.store){
6142         this.store.on('beforeload', this.onBeforeLoad, this);
6143         this.store.on('load', this.onLoad, this);
6144         this.store.on('loadexception', this.onLoadException, this);
6145         this.removeMask = false;
6146     }else{
6147         var um = this.el.getUpdateManager();
6148         um.showLoadIndicator = false; // disable the default indicator
6149         um.on('beforeupdate', this.onBeforeLoad, this);
6150         um.on('update', this.onLoad, this);
6151         um.on('failure', this.onLoad, this);
6152         this.removeMask = true;
6153     }
6154 };
6155
6156 Roo.LoadMask.prototype = {
6157     /**
6158      * @cfg {Boolean} removeMask
6159      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6160      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6161      */
6162     /**
6163      * @cfg {String} msg
6164      * The text to display in a centered loading message box (defaults to 'Loading...')
6165      */
6166     msg : 'Loading...',
6167     /**
6168      * @cfg {String} msgCls
6169      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6170      */
6171     msgCls : 'x-mask-loading',
6172
6173     /**
6174      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6175      * @type Boolean
6176      */
6177     disabled: false,
6178
6179     /**
6180      * Disables the mask to prevent it from being displayed
6181      */
6182     disable : function(){
6183        this.disabled = true;
6184     },
6185
6186     /**
6187      * Enables the mask so that it can be displayed
6188      */
6189     enable : function(){
6190         this.disabled = false;
6191     },
6192     
6193     onLoadException : function()
6194     {
6195         Roo.log(arguments);
6196         
6197         if (typeof(arguments[3]) != 'undefined') {
6198             Roo.MessageBox.alert("Error loading",arguments[3]);
6199         } 
6200         /*
6201         try {
6202             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6203                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6204             }   
6205         } catch(e) {
6206             
6207         }
6208         */
6209     
6210         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6211     },
6212     // private
6213     onLoad : function()
6214     {
6215         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6216     },
6217
6218     // private
6219     onBeforeLoad : function(){
6220         if(!this.disabled){
6221             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6222         }
6223     },
6224
6225     // private
6226     destroy : function(){
6227         if(this.store){
6228             this.store.un('beforeload', this.onBeforeLoad, this);
6229             this.store.un('load', this.onLoad, this);
6230             this.store.un('loadexception', this.onLoadException, this);
6231         }else{
6232             var um = this.el.getUpdateManager();
6233             um.un('beforeupdate', this.onBeforeLoad, this);
6234             um.un('update', this.onLoad, this);
6235             um.un('failure', this.onLoad, this);
6236         }
6237     }
6238 };/*
6239  * - LGPL
6240  *
6241  * table
6242  * 
6243  */
6244
6245 /**
6246  * @class Roo.bootstrap.Table
6247  * @extends Roo.bootstrap.Component
6248  * Bootstrap Table class
6249  * @cfg {String} cls table class
6250  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6251  * @cfg {String} bgcolor Specifies the background color for a table
6252  * @cfg {Number} border Specifies whether the table cells should have borders or not
6253  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6254  * @cfg {Number} cellspacing Specifies the space between cells
6255  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6256  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6257  * @cfg {String} sortable Specifies that the table should be sortable
6258  * @cfg {String} summary Specifies a summary of the content of a table
6259  * @cfg {Number} width Specifies the width of a table
6260  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6261  * 
6262  * @cfg {boolean} striped Should the rows be alternative striped
6263  * @cfg {boolean} bordered Add borders to the table
6264  * @cfg {boolean} hover Add hover highlighting
6265  * @cfg {boolean} condensed Format condensed
6266  * @cfg {boolean} responsive Format condensed
6267  * @cfg {Boolean} loadMask (true|false) default false
6268  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6269  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6270  * @cfg {Boolean} rowSelection (true|false) default false
6271  * @cfg {Boolean} cellSelection (true|false) default false
6272  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6273  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6274  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6275  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6276  
6277  * 
6278  * @constructor
6279  * Create a new Table
6280  * @param {Object} config The config object
6281  */
6282
6283 Roo.bootstrap.Table = function(config){
6284     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6285     
6286   
6287     
6288     // BC...
6289     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6290     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6291     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6292     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6293     
6294     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6295     if (this.sm) {
6296         this.sm.grid = this;
6297         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6298         this.sm = this.selModel;
6299         this.sm.xmodule = this.xmodule || false;
6300     }
6301     
6302     if (this.cm && typeof(this.cm.config) == 'undefined') {
6303         this.colModel = new Roo.grid.ColumnModel(this.cm);
6304         this.cm = this.colModel;
6305         this.cm.xmodule = this.xmodule || false;
6306     }
6307     if (this.store) {
6308         this.store= Roo.factory(this.store, Roo.data);
6309         this.ds = this.store;
6310         this.ds.xmodule = this.xmodule || false;
6311          
6312     }
6313     if (this.footer && this.store) {
6314         this.footer.dataSource = this.ds;
6315         this.footer = Roo.factory(this.footer);
6316     }
6317     
6318     /** @private */
6319     this.addEvents({
6320         /**
6321          * @event cellclick
6322          * Fires when a cell is clicked
6323          * @param {Roo.bootstrap.Table} this
6324          * @param {Roo.Element} el
6325          * @param {Number} rowIndex
6326          * @param {Number} columnIndex
6327          * @param {Roo.EventObject} e
6328          */
6329         "cellclick" : true,
6330         /**
6331          * @event celldblclick
6332          * Fires when a cell is double clicked
6333          * @param {Roo.bootstrap.Table} this
6334          * @param {Roo.Element} el
6335          * @param {Number} rowIndex
6336          * @param {Number} columnIndex
6337          * @param {Roo.EventObject} e
6338          */
6339         "celldblclick" : true,
6340         /**
6341          * @event rowclick
6342          * Fires when a row is clicked
6343          * @param {Roo.bootstrap.Table} this
6344          * @param {Roo.Element} el
6345          * @param {Number} rowIndex
6346          * @param {Roo.EventObject} e
6347          */
6348         "rowclick" : true,
6349         /**
6350          * @event rowdblclick
6351          * Fires when a row is double clicked
6352          * @param {Roo.bootstrap.Table} this
6353          * @param {Roo.Element} el
6354          * @param {Number} rowIndex
6355          * @param {Roo.EventObject} e
6356          */
6357         "rowdblclick" : true,
6358         /**
6359          * @event mouseover
6360          * Fires when a mouseover occur
6361          * @param {Roo.bootstrap.Table} this
6362          * @param {Roo.Element} el
6363          * @param {Number} rowIndex
6364          * @param {Number} columnIndex
6365          * @param {Roo.EventObject} e
6366          */
6367         "mouseover" : true,
6368         /**
6369          * @event mouseout
6370          * Fires when a mouseout occur
6371          * @param {Roo.bootstrap.Table} this
6372          * @param {Roo.Element} el
6373          * @param {Number} rowIndex
6374          * @param {Number} columnIndex
6375          * @param {Roo.EventObject} e
6376          */
6377         "mouseout" : true,
6378         /**
6379          * @event rowclass
6380          * Fires when a row is rendered, so you can change add a style to it.
6381          * @param {Roo.bootstrap.Table} this
6382          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6383          */
6384         'rowclass' : true,
6385           /**
6386          * @event rowsrendered
6387          * Fires when all the  rows have been rendered
6388          * @param {Roo.bootstrap.Table} this
6389          */
6390         'rowsrendered' : true,
6391         /**
6392          * @event contextmenu
6393          * The raw contextmenu event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "contextmenu" : true,
6397         /**
6398          * @event rowcontextmenu
6399          * Fires when a row is right clicked
6400          * @param {Roo.bootstrap.Table} this
6401          * @param {Number} rowIndex
6402          * @param {Roo.EventObject} e
6403          */
6404         "rowcontextmenu" : true,
6405         /**
6406          * @event cellcontextmenu
6407          * Fires when a cell is right clicked
6408          * @param {Roo.bootstrap.Table} this
6409          * @param {Number} rowIndex
6410          * @param {Number} cellIndex
6411          * @param {Roo.EventObject} e
6412          */
6413          "cellcontextmenu" : true,
6414          /**
6415          * @event headercontextmenu
6416          * Fires when a header is right clicked
6417          * @param {Roo.bootstrap.Table} this
6418          * @param {Number} columnIndex
6419          * @param {Roo.EventObject} e
6420          */
6421         "headercontextmenu" : true
6422     });
6423 };
6424
6425 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6426     
6427     cls: false,
6428     align: false,
6429     bgcolor: false,
6430     border: false,
6431     cellpadding: false,
6432     cellspacing: false,
6433     frame: false,
6434     rules: false,
6435     sortable: false,
6436     summary: false,
6437     width: false,
6438     striped : false,
6439     scrollBody : false,
6440     bordered: false,
6441     hover:  false,
6442     condensed : false,
6443     responsive : false,
6444     sm : false,
6445     cm : false,
6446     store : false,
6447     loadMask : false,
6448     footerShow : true,
6449     headerShow : true,
6450   
6451     rowSelection : false,
6452     cellSelection : false,
6453     layout : false,
6454     
6455     // Roo.Element - the tbody
6456     mainBody: false,
6457     // Roo.Element - thead element
6458     mainHead: false,
6459     
6460     container: false, // used by gridpanel...
6461     
6462     lazyLoad : false,
6463     
6464     CSS : Roo.util.CSS,
6465     
6466     auto_hide_footer : false,
6467     
6468     getAutoCreate : function()
6469     {
6470         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6471         
6472         cfg = {
6473             tag: 'table',
6474             cls : 'table',
6475             cn : []
6476         };
6477         if (this.scrollBody) {
6478             cfg.cls += ' table-body-fixed';
6479         }    
6480         if (this.striped) {
6481             cfg.cls += ' table-striped';
6482         }
6483         
6484         if (this.hover) {
6485             cfg.cls += ' table-hover';
6486         }
6487         if (this.bordered) {
6488             cfg.cls += ' table-bordered';
6489         }
6490         if (this.condensed) {
6491             cfg.cls += ' table-condensed';
6492         }
6493         if (this.responsive) {
6494             cfg.cls += ' table-responsive';
6495         }
6496         
6497         if (this.cls) {
6498             cfg.cls+=  ' ' +this.cls;
6499         }
6500         
6501         // this lot should be simplifed...
6502         var _t = this;
6503         var cp = [
6504             'align',
6505             'bgcolor',
6506             'border',
6507             'cellpadding',
6508             'cellspacing',
6509             'frame',
6510             'rules',
6511             'sortable',
6512             'summary',
6513             'width'
6514         ].forEach(function(k) {
6515             if (_t[k]) {
6516                 cfg[k] = _t[k];
6517             }
6518         });
6519         
6520         
6521         if (this.layout) {
6522             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6523         }
6524         
6525         if(this.store || this.cm){
6526             if(this.headerShow){
6527                 cfg.cn.push(this.renderHeader());
6528             }
6529             
6530             cfg.cn.push(this.renderBody());
6531             
6532             if(this.footerShow){
6533                 cfg.cn.push(this.renderFooter());
6534             }
6535             // where does this come from?
6536             //cfg.cls+=  ' TableGrid';
6537         }
6538         
6539         return { cn : [ cfg ] };
6540     },
6541     
6542     initEvents : function()
6543     {   
6544         if(!this.store || !this.cm){
6545             return;
6546         }
6547         if (this.selModel) {
6548             this.selModel.initEvents();
6549         }
6550         
6551         
6552         //Roo.log('initEvents with ds!!!!');
6553         
6554         this.mainBody = this.el.select('tbody', true).first();
6555         this.mainHead = this.el.select('thead', true).first();
6556         this.mainFoot = this.el.select('tfoot', true).first();
6557         
6558         
6559         
6560         var _this = this;
6561         
6562         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6563             e.on('click', _this.sort, _this);
6564         });
6565         
6566         this.mainBody.on("click", this.onClick, this);
6567         this.mainBody.on("dblclick", this.onDblClick, this);
6568         
6569         // why is this done????? = it breaks dialogs??
6570         //this.parent().el.setStyle('position', 'relative');
6571         
6572         
6573         if (this.footer) {
6574             this.footer.parentId = this.id;
6575             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6576             
6577             if(this.lazyLoad){
6578                 this.el.select('tfoot tr td').first().addClass('hide');
6579             }
6580         } 
6581         
6582         if(this.loadMask) {
6583             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6584         }
6585         
6586         this.store.on('load', this.onLoad, this);
6587         this.store.on('beforeload', this.onBeforeLoad, this);
6588         this.store.on('update', this.onUpdate, this);
6589         this.store.on('add', this.onAdd, this);
6590         this.store.on("clear", this.clear, this);
6591         
6592         this.el.on("contextmenu", this.onContextMenu, this);
6593         
6594         this.mainBody.on('scroll', this.onBodyScroll, this);
6595         
6596         this.cm.on("headerchange", this.onHeaderChange, this);
6597         
6598         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6599         
6600     },
6601     
6602     onContextMenu : function(e, t)
6603     {
6604         this.processEvent("contextmenu", e);
6605     },
6606     
6607     processEvent : function(name, e)
6608     {
6609         if (name != 'touchstart' ) {
6610             this.fireEvent(name, e);    
6611         }
6612         
6613         var t = e.getTarget();
6614         
6615         var cell = Roo.get(t);
6616         
6617         if(!cell){
6618             return;
6619         }
6620         
6621         if(cell.findParent('tfoot', false, true)){
6622             return;
6623         }
6624         
6625         if(cell.findParent('thead', false, true)){
6626             
6627             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6628                 cell = Roo.get(t).findParent('th', false, true);
6629                 if (!cell) {
6630                     Roo.log("failed to find th in thead?");
6631                     Roo.log(e.getTarget());
6632                     return;
6633                 }
6634             }
6635             
6636             var cellIndex = cell.dom.cellIndex;
6637             
6638             var ename = name == 'touchstart' ? 'click' : name;
6639             this.fireEvent("header" + ename, this, cellIndex, e);
6640             
6641             return;
6642         }
6643         
6644         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6645             cell = Roo.get(t).findParent('td', false, true);
6646             if (!cell) {
6647                 Roo.log("failed to find th in tbody?");
6648                 Roo.log(e.getTarget());
6649                 return;
6650             }
6651         }
6652         
6653         var row = cell.findParent('tr', false, true);
6654         var cellIndex = cell.dom.cellIndex;
6655         var rowIndex = row.dom.rowIndex - 1;
6656         
6657         if(row !== false){
6658             
6659             this.fireEvent("row" + name, this, rowIndex, e);
6660             
6661             if(cell !== false){
6662             
6663                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6664             }
6665         }
6666         
6667     },
6668     
6669     onMouseover : function(e, el)
6670     {
6671         var cell = Roo.get(el);
6672         
6673         if(!cell){
6674             return;
6675         }
6676         
6677         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6678             cell = cell.findParent('td', false, true);
6679         }
6680         
6681         var row = cell.findParent('tr', false, true);
6682         var cellIndex = cell.dom.cellIndex;
6683         var rowIndex = row.dom.rowIndex - 1; // start from 0
6684         
6685         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6686         
6687     },
6688     
6689     onMouseout : function(e, el)
6690     {
6691         var cell = Roo.get(el);
6692         
6693         if(!cell){
6694             return;
6695         }
6696         
6697         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6698             cell = cell.findParent('td', false, true);
6699         }
6700         
6701         var row = cell.findParent('tr', false, true);
6702         var cellIndex = cell.dom.cellIndex;
6703         var rowIndex = row.dom.rowIndex - 1; // start from 0
6704         
6705         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6706         
6707     },
6708     
6709     onClick : function(e, el)
6710     {
6711         var cell = Roo.get(el);
6712         
6713         if(!cell || (!this.cellSelection && !this.rowSelection)){
6714             return;
6715         }
6716         
6717         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6718             cell = cell.findParent('td', false, true);
6719         }
6720         
6721         if(!cell || typeof(cell) == 'undefined'){
6722             return;
6723         }
6724         
6725         var row = cell.findParent('tr', false, true);
6726         
6727         if(!row || typeof(row) == 'undefined'){
6728             return;
6729         }
6730         
6731         var cellIndex = cell.dom.cellIndex;
6732         var rowIndex = this.getRowIndex(row);
6733         
6734         // why??? - should these not be based on SelectionModel?
6735         if(this.cellSelection){
6736             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6737         }
6738         
6739         if(this.rowSelection){
6740             this.fireEvent('rowclick', this, row, rowIndex, e);
6741         }
6742         
6743         
6744     },
6745         
6746     onDblClick : function(e,el)
6747     {
6748         var cell = Roo.get(el);
6749         
6750         if(!cell || (!this.cellSelection && !this.rowSelection)){
6751             return;
6752         }
6753         
6754         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6755             cell = cell.findParent('td', false, true);
6756         }
6757         
6758         if(!cell || typeof(cell) == 'undefined'){
6759             return;
6760         }
6761         
6762         var row = cell.findParent('tr', false, true);
6763         
6764         if(!row || typeof(row) == 'undefined'){
6765             return;
6766         }
6767         
6768         var cellIndex = cell.dom.cellIndex;
6769         var rowIndex = this.getRowIndex(row);
6770         
6771         if(this.cellSelection){
6772             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6773         }
6774         
6775         if(this.rowSelection){
6776             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6777         }
6778     },
6779     
6780     sort : function(e,el)
6781     {
6782         var col = Roo.get(el);
6783         
6784         if(!col.hasClass('sortable')){
6785             return;
6786         }
6787         
6788         var sort = col.attr('sort');
6789         var dir = 'ASC';
6790         
6791         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6792             dir = 'DESC';
6793         }
6794         
6795         this.store.sortInfo = {field : sort, direction : dir};
6796         
6797         if (this.footer) {
6798             Roo.log("calling footer first");
6799             this.footer.onClick('first');
6800         } else {
6801         
6802             this.store.load({ params : { start : 0 } });
6803         }
6804     },
6805     
6806     renderHeader : function()
6807     {
6808         var header = {
6809             tag: 'thead',
6810             cn : []
6811         };
6812         
6813         var cm = this.cm;
6814         this.totalWidth = 0;
6815         
6816         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6817             
6818             var config = cm.config[i];
6819             
6820             var c = {
6821                 tag: 'th',
6822                 cls : 'x-hcol-' + i,
6823                 style : '',
6824                 html: cm.getColumnHeader(i)
6825             };
6826             
6827             var hh = '';
6828             
6829             if(typeof(config.sortable) != 'undefined' && config.sortable){
6830                 c.cls = 'sortable';
6831                 c.html = '<i class="glyphicon"></i>' + c.html;
6832             }
6833             
6834             // could use BS4 hidden-..-down 
6835             
6836             if(typeof(config.lgHeader) != 'undefined'){
6837                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6838             }
6839             
6840             if(typeof(config.mdHeader) != 'undefined'){
6841                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6842             }
6843             
6844             if(typeof(config.smHeader) != 'undefined'){
6845                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6846             }
6847             
6848             if(typeof(config.xsHeader) != 'undefined'){
6849                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6850             }
6851             
6852             if(hh.length){
6853                 c.html = hh;
6854             }
6855             
6856             if(typeof(config.tooltip) != 'undefined'){
6857                 c.tooltip = config.tooltip;
6858             }
6859             
6860             if(typeof(config.colspan) != 'undefined'){
6861                 c.colspan = config.colspan;
6862             }
6863             
6864             if(typeof(config.hidden) != 'undefined' && config.hidden){
6865                 c.style += ' display:none;';
6866             }
6867             
6868             if(typeof(config.dataIndex) != 'undefined'){
6869                 c.sort = config.dataIndex;
6870             }
6871             
6872            
6873             
6874             if(typeof(config.align) != 'undefined' && config.align.length){
6875                 c.style += ' text-align:' + config.align + ';';
6876             }
6877             
6878             if(typeof(config.width) != 'undefined'){
6879                 c.style += ' width:' + config.width + 'px;';
6880                 this.totalWidth += config.width;
6881             } else {
6882                 this.totalWidth += 100; // assume minimum of 100 per column?
6883             }
6884             
6885             if(typeof(config.cls) != 'undefined'){
6886                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6887             }
6888             
6889             ['xs','sm','md','lg'].map(function(size){
6890                 
6891                 if(typeof(config[size]) == 'undefined'){
6892                     return;
6893                 }
6894                  
6895                 if (!config[size]) { // 0 = hidden
6896                     // BS 4 '0' is treated as hide that column and below.
6897                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6898                     return;
6899                 }
6900                 
6901                 c.cls += ' col-' + size + '-' + config[size] + (
6902                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6903                 );
6904                 
6905                 
6906             });
6907             
6908             header.cn.push(c)
6909         }
6910         
6911         return header;
6912     },
6913     
6914     renderBody : function()
6915     {
6916         var body = {
6917             tag: 'tbody',
6918             cn : [
6919                 {
6920                     tag: 'tr',
6921                     cn : [
6922                         {
6923                             tag : 'td',
6924                             colspan :  this.cm.getColumnCount()
6925                         }
6926                     ]
6927                 }
6928             ]
6929         };
6930         
6931         return body;
6932     },
6933     
6934     renderFooter : function()
6935     {
6936         var footer = {
6937             tag: 'tfoot',
6938             cn : [
6939                 {
6940                     tag: 'tr',
6941                     cn : [
6942                         {
6943                             tag : 'td',
6944                             colspan :  this.cm.getColumnCount()
6945                         }
6946                     ]
6947                 }
6948             ]
6949         };
6950         
6951         return footer;
6952     },
6953     
6954     
6955     
6956     onLoad : function()
6957     {
6958 //        Roo.log('ds onload');
6959         this.clear();
6960         
6961         var _this = this;
6962         var cm = this.cm;
6963         var ds = this.store;
6964         
6965         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6966             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6967             if (_this.store.sortInfo) {
6968                     
6969                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6970                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6971                 }
6972                 
6973                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6974                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6975                 }
6976             }
6977         });
6978         
6979         var tbody =  this.mainBody;
6980               
6981         if(ds.getCount() > 0){
6982             ds.data.each(function(d,rowIndex){
6983                 var row =  this.renderRow(cm, ds, rowIndex);
6984                 
6985                 tbody.createChild(row);
6986                 
6987                 var _this = this;
6988                 
6989                 if(row.cellObjects.length){
6990                     Roo.each(row.cellObjects, function(r){
6991                         _this.renderCellObject(r);
6992                     })
6993                 }
6994                 
6995             }, this);
6996         }
6997         
6998         var tfoot = this.el.select('tfoot', true).first();
6999         
7000         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7001             
7002             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7003             
7004             var total = this.ds.getTotalCount();
7005             
7006             if(this.footer.pageSize < total){
7007                 this.mainFoot.show();
7008             }
7009         }
7010         
7011         Roo.each(this.el.select('tbody td', true).elements, function(e){
7012             e.on('mouseover', _this.onMouseover, _this);
7013         });
7014         
7015         Roo.each(this.el.select('tbody td', true).elements, function(e){
7016             e.on('mouseout', _this.onMouseout, _this);
7017         });
7018         this.fireEvent('rowsrendered', this);
7019         
7020         this.autoSize();
7021     },
7022     
7023     
7024     onUpdate : function(ds,record)
7025     {
7026         this.refreshRow(record);
7027         this.autoSize();
7028     },
7029     
7030     onRemove : function(ds, record, index, isUpdate){
7031         if(isUpdate !== true){
7032             this.fireEvent("beforerowremoved", this, index, record);
7033         }
7034         var bt = this.mainBody.dom;
7035         
7036         var rows = this.el.select('tbody > tr', true).elements;
7037         
7038         if(typeof(rows[index]) != 'undefined'){
7039             bt.removeChild(rows[index].dom);
7040         }
7041         
7042 //        if(bt.rows[index]){
7043 //            bt.removeChild(bt.rows[index]);
7044 //        }
7045         
7046         if(isUpdate !== true){
7047             //this.stripeRows(index);
7048             //this.syncRowHeights(index, index);
7049             //this.layout();
7050             this.fireEvent("rowremoved", this, index, record);
7051         }
7052     },
7053     
7054     onAdd : function(ds, records, rowIndex)
7055     {
7056         //Roo.log('on Add called');
7057         // - note this does not handle multiple adding very well..
7058         var bt = this.mainBody.dom;
7059         for (var i =0 ; i < records.length;i++) {
7060             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7061             //Roo.log(records[i]);
7062             //Roo.log(this.store.getAt(rowIndex+i));
7063             this.insertRow(this.store, rowIndex + i, false);
7064             return;
7065         }
7066         
7067     },
7068     
7069     
7070     refreshRow : function(record){
7071         var ds = this.store, index;
7072         if(typeof record == 'number'){
7073             index = record;
7074             record = ds.getAt(index);
7075         }else{
7076             index = ds.indexOf(record);
7077         }
7078         this.insertRow(ds, index, true);
7079         this.autoSize();
7080         this.onRemove(ds, record, index+1, true);
7081         this.autoSize();
7082         //this.syncRowHeights(index, index);
7083         //this.layout();
7084         this.fireEvent("rowupdated", this, index, record);
7085     },
7086     
7087     insertRow : function(dm, rowIndex, isUpdate){
7088         
7089         if(!isUpdate){
7090             this.fireEvent("beforerowsinserted", this, rowIndex);
7091         }
7092             //var s = this.getScrollState();
7093         var row = this.renderRow(this.cm, this.store, rowIndex);
7094         // insert before rowIndex..
7095         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7096         
7097         var _this = this;
7098                 
7099         if(row.cellObjects.length){
7100             Roo.each(row.cellObjects, function(r){
7101                 _this.renderCellObject(r);
7102             })
7103         }
7104             
7105         if(!isUpdate){
7106             this.fireEvent("rowsinserted", this, rowIndex);
7107             //this.syncRowHeights(firstRow, lastRow);
7108             //this.stripeRows(firstRow);
7109             //this.layout();
7110         }
7111         
7112     },
7113     
7114     
7115     getRowDom : function(rowIndex)
7116     {
7117         var rows = this.el.select('tbody > tr', true).elements;
7118         
7119         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7120         
7121     },
7122     // returns the object tree for a tr..
7123   
7124     
7125     renderRow : function(cm, ds, rowIndex) 
7126     {
7127         var d = ds.getAt(rowIndex);
7128         
7129         var row = {
7130             tag : 'tr',
7131             cls : 'x-row-' + rowIndex,
7132             cn : []
7133         };
7134             
7135         var cellObjects = [];
7136         
7137         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7138             var config = cm.config[i];
7139             
7140             var renderer = cm.getRenderer(i);
7141             var value = '';
7142             var id = false;
7143             
7144             if(typeof(renderer) !== 'undefined'){
7145                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7146             }
7147             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7148             // and are rendered into the cells after the row is rendered - using the id for the element.
7149             
7150             if(typeof(value) === 'object'){
7151                 id = Roo.id();
7152                 cellObjects.push({
7153                     container : id,
7154                     cfg : value 
7155                 })
7156             }
7157             
7158             var rowcfg = {
7159                 record: d,
7160                 rowIndex : rowIndex,
7161                 colIndex : i,
7162                 rowClass : ''
7163             };
7164
7165             this.fireEvent('rowclass', this, rowcfg);
7166             
7167             var td = {
7168                 tag: 'td',
7169                 cls : rowcfg.rowClass + ' x-col-' + i,
7170                 style: '',
7171                 html: (typeof(value) === 'object') ? '' : value
7172             };
7173             
7174             if (id) {
7175                 td.id = id;
7176             }
7177             
7178             if(typeof(config.colspan) != 'undefined'){
7179                 td.colspan = config.colspan;
7180             }
7181             
7182             if(typeof(config.hidden) != 'undefined' && config.hidden){
7183                 td.style += ' display:none;';
7184             }
7185             
7186             if(typeof(config.align) != 'undefined' && config.align.length){
7187                 td.style += ' text-align:' + config.align + ';';
7188             }
7189             if(typeof(config.valign) != 'undefined' && config.valign.length){
7190                 td.style += ' vertical-align:' + config.valign + ';';
7191             }
7192             
7193             if(typeof(config.width) != 'undefined'){
7194                 td.style += ' width:' +  config.width + 'px;';
7195             }
7196             
7197             if(typeof(config.cursor) != 'undefined'){
7198                 td.style += ' cursor:' +  config.cursor + ';';
7199             }
7200             
7201             if(typeof(config.cls) != 'undefined'){
7202                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7203             }
7204             
7205             ['xs','sm','md','lg'].map(function(size){
7206                 
7207                 if(typeof(config[size]) == 'undefined'){
7208                     return;
7209                 }
7210                 
7211                 
7212                   
7213                 if (!config[size]) { // 0 = hidden
7214                     // BS 4 '0' is treated as hide that column and below.
7215                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7216                     return;
7217                 }
7218                 
7219                 td.cls += ' col-' + size + '-' + config[size] + (
7220                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7221                 );
7222                  
7223
7224             });
7225             
7226             row.cn.push(td);
7227            
7228         }
7229         
7230         row.cellObjects = cellObjects;
7231         
7232         return row;
7233           
7234     },
7235     
7236     
7237     
7238     onBeforeLoad : function()
7239     {
7240         
7241     },
7242      /**
7243      * Remove all rows
7244      */
7245     clear : function()
7246     {
7247         this.el.select('tbody', true).first().dom.innerHTML = '';
7248     },
7249     /**
7250      * Show or hide a row.
7251      * @param {Number} rowIndex to show or hide
7252      * @param {Boolean} state hide
7253      */
7254     setRowVisibility : function(rowIndex, state)
7255     {
7256         var bt = this.mainBody.dom;
7257         
7258         var rows = this.el.select('tbody > tr', true).elements;
7259         
7260         if(typeof(rows[rowIndex]) == 'undefined'){
7261             return;
7262         }
7263         rows[rowIndex].dom.style.display = state ? '' : 'none';
7264     },
7265     
7266     
7267     getSelectionModel : function(){
7268         if(!this.selModel){
7269             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7270         }
7271         return this.selModel;
7272     },
7273     /*
7274      * Render the Roo.bootstrap object from renderder
7275      */
7276     renderCellObject : function(r)
7277     {
7278         var _this = this;
7279         
7280         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7281         
7282         var t = r.cfg.render(r.container);
7283         
7284         if(r.cfg.cn){
7285             Roo.each(r.cfg.cn, function(c){
7286                 var child = {
7287                     container: t.getChildContainer(),
7288                     cfg: c
7289                 };
7290                 _this.renderCellObject(child);
7291             })
7292         }
7293     },
7294     
7295     getRowIndex : function(row)
7296     {
7297         var rowIndex = -1;
7298         
7299         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7300             if(el != row){
7301                 return;
7302             }
7303             
7304             rowIndex = index;
7305         });
7306         
7307         return rowIndex;
7308     },
7309      /**
7310      * Returns the grid's underlying element = used by panel.Grid
7311      * @return {Element} The element
7312      */
7313     getGridEl : function(){
7314         return this.el;
7315     },
7316      /**
7317      * Forces a resize - used by panel.Grid
7318      * @return {Element} The element
7319      */
7320     autoSize : function()
7321     {
7322         //var ctr = Roo.get(this.container.dom.parentElement);
7323         var ctr = Roo.get(this.el.dom);
7324         
7325         var thd = this.getGridEl().select('thead',true).first();
7326         var tbd = this.getGridEl().select('tbody', true).first();
7327         var tfd = this.getGridEl().select('tfoot', true).first();
7328         
7329         var cw = ctr.getWidth();
7330         
7331         if (tbd) {
7332             
7333             tbd.setSize(ctr.getWidth(),
7334                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7335             );
7336             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7337             cw -= barsize;
7338         }
7339         cw = Math.max(cw, this.totalWidth);
7340         this.getGridEl().select('tr',true).setWidth(cw);
7341         // resize 'expandable coloumn?
7342         
7343         return; // we doe not have a view in this design..
7344         
7345     },
7346     onBodyScroll: function()
7347     {
7348         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7349         if(this.mainHead){
7350             this.mainHead.setStyle({
7351                 'position' : 'relative',
7352                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7353             });
7354         }
7355         
7356         if(this.lazyLoad){
7357             
7358             var scrollHeight = this.mainBody.dom.scrollHeight;
7359             
7360             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7361             
7362             var height = this.mainBody.getHeight();
7363             
7364             if(scrollHeight - height == scrollTop) {
7365                 
7366                 var total = this.ds.getTotalCount();
7367                 
7368                 if(this.footer.cursor + this.footer.pageSize < total){
7369                     
7370                     this.footer.ds.load({
7371                         params : {
7372                             start : this.footer.cursor + this.footer.pageSize,
7373                             limit : this.footer.pageSize
7374                         },
7375                         add : true
7376                     });
7377                 }
7378             }
7379             
7380         }
7381     },
7382     
7383     onHeaderChange : function()
7384     {
7385         var header = this.renderHeader();
7386         var table = this.el.select('table', true).first();
7387         
7388         this.mainHead.remove();
7389         this.mainHead = table.createChild(header, this.mainBody, false);
7390     },
7391     
7392     onHiddenChange : function(colModel, colIndex, hidden)
7393     {
7394         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7395         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7396         
7397         this.CSS.updateRule(thSelector, "display", "");
7398         this.CSS.updateRule(tdSelector, "display", "");
7399         
7400         if(hidden){
7401             this.CSS.updateRule(thSelector, "display", "none");
7402             this.CSS.updateRule(tdSelector, "display", "none");
7403         }
7404         
7405         this.onHeaderChange();
7406         this.onLoad();
7407     },
7408     
7409     setColumnWidth: function(col_index, width)
7410     {
7411         // width = "md-2 xs-2..."
7412         if(!this.colModel.config[col_index]) {
7413             return;
7414         }
7415         
7416         var w = width.split(" ");
7417         
7418         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7419         
7420         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7421         
7422         
7423         for(var j = 0; j < w.length; j++) {
7424             
7425             if(!w[j]) {
7426                 continue;
7427             }
7428             
7429             var size_cls = w[j].split("-");
7430             
7431             if(!Number.isInteger(size_cls[1] * 1)) {
7432                 continue;
7433             }
7434             
7435             if(!this.colModel.config[col_index][size_cls[0]]) {
7436                 continue;
7437             }
7438             
7439             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7440                 continue;
7441             }
7442             
7443             h_row[0].classList.replace(
7444                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7445                 "col-"+size_cls[0]+"-"+size_cls[1]
7446             );
7447             
7448             for(var i = 0; i < rows.length; i++) {
7449                 
7450                 var size_cls = w[j].split("-");
7451                 
7452                 if(!Number.isInteger(size_cls[1] * 1)) {
7453                     continue;
7454                 }
7455                 
7456                 if(!this.colModel.config[col_index][size_cls[0]]) {
7457                     continue;
7458                 }
7459                 
7460                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7461                     continue;
7462                 }
7463                 
7464                 rows[i].classList.replace(
7465                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7466                     "col-"+size_cls[0]+"-"+size_cls[1]
7467                 );
7468             }
7469             
7470             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7471         }
7472     }
7473 });
7474
7475  
7476
7477  /*
7478  * - LGPL
7479  *
7480  * table cell
7481  * 
7482  */
7483
7484 /**
7485  * @class Roo.bootstrap.TableCell
7486  * @extends Roo.bootstrap.Component
7487  * Bootstrap TableCell class
7488  * @cfg {String} html cell contain text
7489  * @cfg {String} cls cell class
7490  * @cfg {String} tag cell tag (td|th) default td
7491  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7492  * @cfg {String} align Aligns the content in a cell
7493  * @cfg {String} axis Categorizes cells
7494  * @cfg {String} bgcolor Specifies the background color of a cell
7495  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7496  * @cfg {Number} colspan Specifies the number of columns a cell should span
7497  * @cfg {String} headers Specifies one or more header cells a cell is related to
7498  * @cfg {Number} height Sets the height of a cell
7499  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7500  * @cfg {Number} rowspan Sets the number of rows a cell should span
7501  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7502  * @cfg {String} valign Vertical aligns the content in a cell
7503  * @cfg {Number} width Specifies the width of a cell
7504  * 
7505  * @constructor
7506  * Create a new TableCell
7507  * @param {Object} config The config object
7508  */
7509
7510 Roo.bootstrap.TableCell = function(config){
7511     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7512 };
7513
7514 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7515     
7516     html: false,
7517     cls: false,
7518     tag: false,
7519     abbr: false,
7520     align: false,
7521     axis: false,
7522     bgcolor: false,
7523     charoff: false,
7524     colspan: false,
7525     headers: false,
7526     height: false,
7527     nowrap: false,
7528     rowspan: false,
7529     scope: false,
7530     valign: false,
7531     width: false,
7532     
7533     
7534     getAutoCreate : function(){
7535         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7536         
7537         cfg = {
7538             tag: 'td'
7539         };
7540         
7541         if(this.tag){
7542             cfg.tag = this.tag;
7543         }
7544         
7545         if (this.html) {
7546             cfg.html=this.html
7547         }
7548         if (this.cls) {
7549             cfg.cls=this.cls
7550         }
7551         if (this.abbr) {
7552             cfg.abbr=this.abbr
7553         }
7554         if (this.align) {
7555             cfg.align=this.align
7556         }
7557         if (this.axis) {
7558             cfg.axis=this.axis
7559         }
7560         if (this.bgcolor) {
7561             cfg.bgcolor=this.bgcolor
7562         }
7563         if (this.charoff) {
7564             cfg.charoff=this.charoff
7565         }
7566         if (this.colspan) {
7567             cfg.colspan=this.colspan
7568         }
7569         if (this.headers) {
7570             cfg.headers=this.headers
7571         }
7572         if (this.height) {
7573             cfg.height=this.height
7574         }
7575         if (this.nowrap) {
7576             cfg.nowrap=this.nowrap
7577         }
7578         if (this.rowspan) {
7579             cfg.rowspan=this.rowspan
7580         }
7581         if (this.scope) {
7582             cfg.scope=this.scope
7583         }
7584         if (this.valign) {
7585             cfg.valign=this.valign
7586         }
7587         if (this.width) {
7588             cfg.width=this.width
7589         }
7590         
7591         
7592         return cfg;
7593     }
7594    
7595 });
7596
7597  
7598
7599  /*
7600  * - LGPL
7601  *
7602  * table row
7603  * 
7604  */
7605
7606 /**
7607  * @class Roo.bootstrap.TableRow
7608  * @extends Roo.bootstrap.Component
7609  * Bootstrap TableRow class
7610  * @cfg {String} cls row class
7611  * @cfg {String} align Aligns the content in a table row
7612  * @cfg {String} bgcolor Specifies a background color for a table row
7613  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7614  * @cfg {String} valign Vertical aligns the content in a table row
7615  * 
7616  * @constructor
7617  * Create a new TableRow
7618  * @param {Object} config The config object
7619  */
7620
7621 Roo.bootstrap.TableRow = function(config){
7622     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7623 };
7624
7625 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7626     
7627     cls: false,
7628     align: false,
7629     bgcolor: false,
7630     charoff: false,
7631     valign: false,
7632     
7633     getAutoCreate : function(){
7634         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7635         
7636         cfg = {
7637             tag: 'tr'
7638         };
7639             
7640         if(this.cls){
7641             cfg.cls = this.cls;
7642         }
7643         if(this.align){
7644             cfg.align = this.align;
7645         }
7646         if(this.bgcolor){
7647             cfg.bgcolor = this.bgcolor;
7648         }
7649         if(this.charoff){
7650             cfg.charoff = this.charoff;
7651         }
7652         if(this.valign){
7653             cfg.valign = this.valign;
7654         }
7655         
7656         return cfg;
7657     }
7658    
7659 });
7660
7661  
7662
7663  /*
7664  * - LGPL
7665  *
7666  * table body
7667  * 
7668  */
7669
7670 /**
7671  * @class Roo.bootstrap.TableBody
7672  * @extends Roo.bootstrap.Component
7673  * Bootstrap TableBody class
7674  * @cfg {String} cls element class
7675  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7676  * @cfg {String} align Aligns the content inside the element
7677  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7678  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7679  * 
7680  * @constructor
7681  * Create a new TableBody
7682  * @param {Object} config The config object
7683  */
7684
7685 Roo.bootstrap.TableBody = function(config){
7686     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7687 };
7688
7689 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7690     
7691     cls: false,
7692     tag: false,
7693     align: false,
7694     charoff: false,
7695     valign: false,
7696     
7697     getAutoCreate : function(){
7698         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7699         
7700         cfg = {
7701             tag: 'tbody'
7702         };
7703             
7704         if (this.cls) {
7705             cfg.cls=this.cls
7706         }
7707         if(this.tag){
7708             cfg.tag = this.tag;
7709         }
7710         
7711         if(this.align){
7712             cfg.align = this.align;
7713         }
7714         if(this.charoff){
7715             cfg.charoff = this.charoff;
7716         }
7717         if(this.valign){
7718             cfg.valign = this.valign;
7719         }
7720         
7721         return cfg;
7722     }
7723     
7724     
7725 //    initEvents : function()
7726 //    {
7727 //        
7728 //        if(!this.store){
7729 //            return;
7730 //        }
7731 //        
7732 //        this.store = Roo.factory(this.store, Roo.data);
7733 //        this.store.on('load', this.onLoad, this);
7734 //        
7735 //        this.store.load();
7736 //        
7737 //    },
7738 //    
7739 //    onLoad: function () 
7740 //    {   
7741 //        this.fireEvent('load', this);
7742 //    }
7743 //    
7744 //   
7745 });
7746
7747  
7748
7749  /*
7750  * Based on:
7751  * Ext JS Library 1.1.1
7752  * Copyright(c) 2006-2007, Ext JS, LLC.
7753  *
7754  * Originally Released Under LGPL - original licence link has changed is not relivant.
7755  *
7756  * Fork - LGPL
7757  * <script type="text/javascript">
7758  */
7759
7760 // as we use this in bootstrap.
7761 Roo.namespace('Roo.form');
7762  /**
7763  * @class Roo.form.Action
7764  * Internal Class used to handle form actions
7765  * @constructor
7766  * @param {Roo.form.BasicForm} el The form element or its id
7767  * @param {Object} config Configuration options
7768  */
7769
7770  
7771  
7772 // define the action interface
7773 Roo.form.Action = function(form, options){
7774     this.form = form;
7775     this.options = options || {};
7776 };
7777 /**
7778  * Client Validation Failed
7779  * @const 
7780  */
7781 Roo.form.Action.CLIENT_INVALID = 'client';
7782 /**
7783  * Server Validation Failed
7784  * @const 
7785  */
7786 Roo.form.Action.SERVER_INVALID = 'server';
7787  /**
7788  * Connect to Server Failed
7789  * @const 
7790  */
7791 Roo.form.Action.CONNECT_FAILURE = 'connect';
7792 /**
7793  * Reading Data from Server Failed
7794  * @const 
7795  */
7796 Roo.form.Action.LOAD_FAILURE = 'load';
7797
7798 Roo.form.Action.prototype = {
7799     type : 'default',
7800     failureType : undefined,
7801     response : undefined,
7802     result : undefined,
7803
7804     // interface method
7805     run : function(options){
7806
7807     },
7808
7809     // interface method
7810     success : function(response){
7811
7812     },
7813
7814     // interface method
7815     handleResponse : function(response){
7816
7817     },
7818
7819     // default connection failure
7820     failure : function(response){
7821         
7822         this.response = response;
7823         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7824         this.form.afterAction(this, false);
7825     },
7826
7827     processResponse : function(response){
7828         this.response = response;
7829         if(!response.responseText){
7830             return true;
7831         }
7832         this.result = this.handleResponse(response);
7833         return this.result;
7834     },
7835
7836     // utility functions used internally
7837     getUrl : function(appendParams){
7838         var url = this.options.url || this.form.url || this.form.el.dom.action;
7839         if(appendParams){
7840             var p = this.getParams();
7841             if(p){
7842                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7843             }
7844         }
7845         return url;
7846     },
7847
7848     getMethod : function(){
7849         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7850     },
7851
7852     getParams : function(){
7853         var bp = this.form.baseParams;
7854         var p = this.options.params;
7855         if(p){
7856             if(typeof p == "object"){
7857                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7858             }else if(typeof p == 'string' && bp){
7859                 p += '&' + Roo.urlEncode(bp);
7860             }
7861         }else if(bp){
7862             p = Roo.urlEncode(bp);
7863         }
7864         return p;
7865     },
7866
7867     createCallback : function(){
7868         return {
7869             success: this.success,
7870             failure: this.failure,
7871             scope: this,
7872             timeout: (this.form.timeout*1000),
7873             upload: this.form.fileUpload ? this.success : undefined
7874         };
7875     }
7876 };
7877
7878 Roo.form.Action.Submit = function(form, options){
7879     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7880 };
7881
7882 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7883     type : 'submit',
7884
7885     haveProgress : false,
7886     uploadComplete : false,
7887     
7888     // uploadProgress indicator.
7889     uploadProgress : function()
7890     {
7891         if (!this.form.progressUrl) {
7892             return;
7893         }
7894         
7895         if (!this.haveProgress) {
7896             Roo.MessageBox.progress("Uploading", "Uploading");
7897         }
7898         if (this.uploadComplete) {
7899            Roo.MessageBox.hide();
7900            return;
7901         }
7902         
7903         this.haveProgress = true;
7904    
7905         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7906         
7907         var c = new Roo.data.Connection();
7908         c.request({
7909             url : this.form.progressUrl,
7910             params: {
7911                 id : uid
7912             },
7913             method: 'GET',
7914             success : function(req){
7915                //console.log(data);
7916                 var rdata = false;
7917                 var edata;
7918                 try  {
7919                    rdata = Roo.decode(req.responseText)
7920                 } catch (e) {
7921                     Roo.log("Invalid data from server..");
7922                     Roo.log(edata);
7923                     return;
7924                 }
7925                 if (!rdata || !rdata.success) {
7926                     Roo.log(rdata);
7927                     Roo.MessageBox.alert(Roo.encode(rdata));
7928                     return;
7929                 }
7930                 var data = rdata.data;
7931                 
7932                 if (this.uploadComplete) {
7933                    Roo.MessageBox.hide();
7934                    return;
7935                 }
7936                    
7937                 if (data){
7938                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7939                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7940                     );
7941                 }
7942                 this.uploadProgress.defer(2000,this);
7943             },
7944        
7945             failure: function(data) {
7946                 Roo.log('progress url failed ');
7947                 Roo.log(data);
7948             },
7949             scope : this
7950         });
7951            
7952     },
7953     
7954     
7955     run : function()
7956     {
7957         // run get Values on the form, so it syncs any secondary forms.
7958         this.form.getValues();
7959         
7960         var o = this.options;
7961         var method = this.getMethod();
7962         var isPost = method == 'POST';
7963         if(o.clientValidation === false || this.form.isValid()){
7964             
7965             if (this.form.progressUrl) {
7966                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7967                     (new Date() * 1) + '' + Math.random());
7968                     
7969             } 
7970             
7971             
7972             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7973                 form:this.form.el.dom,
7974                 url:this.getUrl(!isPost),
7975                 method: method,
7976                 params:isPost ? this.getParams() : null,
7977                 isUpload: this.form.fileUpload,
7978                 formData : this.form.formData
7979             }));
7980             
7981             this.uploadProgress();
7982
7983         }else if (o.clientValidation !== false){ // client validation failed
7984             this.failureType = Roo.form.Action.CLIENT_INVALID;
7985             this.form.afterAction(this, false);
7986         }
7987     },
7988
7989     success : function(response)
7990     {
7991         this.uploadComplete= true;
7992         if (this.haveProgress) {
7993             Roo.MessageBox.hide();
7994         }
7995         
7996         
7997         var result = this.processResponse(response);
7998         if(result === true || result.success){
7999             this.form.afterAction(this, true);
8000             return;
8001         }
8002         if(result.errors){
8003             this.form.markInvalid(result.errors);
8004             this.failureType = Roo.form.Action.SERVER_INVALID;
8005         }
8006         this.form.afterAction(this, false);
8007     },
8008     failure : function(response)
8009     {
8010         this.uploadComplete= true;
8011         if (this.haveProgress) {
8012             Roo.MessageBox.hide();
8013         }
8014         
8015         this.response = response;
8016         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8017         this.form.afterAction(this, false);
8018     },
8019     
8020     handleResponse : function(response){
8021         if(this.form.errorReader){
8022             var rs = this.form.errorReader.read(response);
8023             var errors = [];
8024             if(rs.records){
8025                 for(var i = 0, len = rs.records.length; i < len; i++) {
8026                     var r = rs.records[i];
8027                     errors[i] = r.data;
8028                 }
8029             }
8030             if(errors.length < 1){
8031                 errors = null;
8032             }
8033             return {
8034                 success : rs.success,
8035                 errors : errors
8036             };
8037         }
8038         var ret = false;
8039         try {
8040             ret = Roo.decode(response.responseText);
8041         } catch (e) {
8042             ret = {
8043                 success: false,
8044                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8045                 errors : []
8046             };
8047         }
8048         return ret;
8049         
8050     }
8051 });
8052
8053
8054 Roo.form.Action.Load = function(form, options){
8055     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8056     this.reader = this.form.reader;
8057 };
8058
8059 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8060     type : 'load',
8061
8062     run : function(){
8063         
8064         Roo.Ajax.request(Roo.apply(
8065                 this.createCallback(), {
8066                     method:this.getMethod(),
8067                     url:this.getUrl(false),
8068                     params:this.getParams()
8069         }));
8070     },
8071
8072     success : function(response){
8073         
8074         var result = this.processResponse(response);
8075         if(result === true || !result.success || !result.data){
8076             this.failureType = Roo.form.Action.LOAD_FAILURE;
8077             this.form.afterAction(this, false);
8078             return;
8079         }
8080         this.form.clearInvalid();
8081         this.form.setValues(result.data);
8082         this.form.afterAction(this, true);
8083     },
8084
8085     handleResponse : function(response){
8086         if(this.form.reader){
8087             var rs = this.form.reader.read(response);
8088             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8089             return {
8090                 success : rs.success,
8091                 data : data
8092             };
8093         }
8094         return Roo.decode(response.responseText);
8095     }
8096 });
8097
8098 Roo.form.Action.ACTION_TYPES = {
8099     'load' : Roo.form.Action.Load,
8100     'submit' : Roo.form.Action.Submit
8101 };/*
8102  * - LGPL
8103  *
8104  * form
8105  *
8106  */
8107
8108 /**
8109  * @class Roo.bootstrap.Form
8110  * @extends Roo.bootstrap.Component
8111  * Bootstrap Form class
8112  * @cfg {String} method  GET | POST (default POST)
8113  * @cfg {String} labelAlign top | left (default top)
8114  * @cfg {String} align left  | right - for navbars
8115  * @cfg {Boolean} loadMask load mask when submit (default true)
8116
8117  *
8118  * @constructor
8119  * Create a new Form
8120  * @param {Object} config The config object
8121  */
8122
8123
8124 Roo.bootstrap.Form = function(config){
8125     
8126     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8127     
8128     Roo.bootstrap.Form.popover.apply();
8129     
8130     this.addEvents({
8131         /**
8132          * @event clientvalidation
8133          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8134          * @param {Form} this
8135          * @param {Boolean} valid true if the form has passed client-side validation
8136          */
8137         clientvalidation: true,
8138         /**
8139          * @event beforeaction
8140          * Fires before any action is performed. Return false to cancel the action.
8141          * @param {Form} this
8142          * @param {Action} action The action to be performed
8143          */
8144         beforeaction: true,
8145         /**
8146          * @event actionfailed
8147          * Fires when an action fails.
8148          * @param {Form} this
8149          * @param {Action} action The action that failed
8150          */
8151         actionfailed : true,
8152         /**
8153          * @event actioncomplete
8154          * Fires when an action is completed.
8155          * @param {Form} this
8156          * @param {Action} action The action that completed
8157          */
8158         actioncomplete : true
8159     });
8160 };
8161
8162 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8163
8164      /**
8165      * @cfg {String} method
8166      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8167      */
8168     method : 'POST',
8169     /**
8170      * @cfg {String} url
8171      * The URL to use for form actions if one isn't supplied in the action options.
8172      */
8173     /**
8174      * @cfg {Boolean} fileUpload
8175      * Set to true if this form is a file upload.
8176      */
8177
8178     /**
8179      * @cfg {Object} baseParams
8180      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8181      */
8182
8183     /**
8184      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8185      */
8186     timeout: 30,
8187     /**
8188      * @cfg {Sting} align (left|right) for navbar forms
8189      */
8190     align : 'left',
8191
8192     // private
8193     activeAction : null,
8194
8195     /**
8196      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8197      * element by passing it or its id or mask the form itself by passing in true.
8198      * @type Mixed
8199      */
8200     waitMsgTarget : false,
8201
8202     loadMask : true,
8203     
8204     /**
8205      * @cfg {Boolean} errorMask (true|false) default false
8206      */
8207     errorMask : false,
8208     
8209     /**
8210      * @cfg {Number} maskOffset Default 100
8211      */
8212     maskOffset : 100,
8213     
8214     /**
8215      * @cfg {Boolean} maskBody
8216      */
8217     maskBody : false,
8218
8219     getAutoCreate : function(){
8220
8221         var cfg = {
8222             tag: 'form',
8223             method : this.method || 'POST',
8224             id : this.id || Roo.id(),
8225             cls : ''
8226         };
8227         if (this.parent().xtype.match(/^Nav/)) {
8228             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8229
8230         }
8231
8232         if (this.labelAlign == 'left' ) {
8233             cfg.cls += ' form-horizontal';
8234         }
8235
8236
8237         return cfg;
8238     },
8239     initEvents : function()
8240     {
8241         this.el.on('submit', this.onSubmit, this);
8242         // this was added as random key presses on the form where triggering form submit.
8243         this.el.on('keypress', function(e) {
8244             if (e.getCharCode() != 13) {
8245                 return true;
8246             }
8247             // we might need to allow it for textareas.. and some other items.
8248             // check e.getTarget().
8249
8250             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8251                 return true;
8252             }
8253
8254             Roo.log("keypress blocked");
8255
8256             e.preventDefault();
8257             return false;
8258         });
8259         
8260     },
8261     // private
8262     onSubmit : function(e){
8263         e.stopEvent();
8264     },
8265
8266      /**
8267      * Returns true if client-side validation on the form is successful.
8268      * @return Boolean
8269      */
8270     isValid : function(){
8271         var items = this.getItems();
8272         var valid = true;
8273         var target = false;
8274         
8275         items.each(function(f){
8276             
8277             if(f.validate()){
8278                 return;
8279             }
8280             
8281             Roo.log('invalid field: ' + f.name);
8282             
8283             valid = false;
8284
8285             if(!target && f.el.isVisible(true)){
8286                 target = f;
8287             }
8288            
8289         });
8290         
8291         if(this.errorMask && !valid){
8292             Roo.bootstrap.Form.popover.mask(this, target);
8293         }
8294         
8295         return valid;
8296     },
8297     
8298     /**
8299      * Returns true if any fields in this form have changed since their original load.
8300      * @return Boolean
8301      */
8302     isDirty : function(){
8303         var dirty = false;
8304         var items = this.getItems();
8305         items.each(function(f){
8306            if(f.isDirty()){
8307                dirty = true;
8308                return false;
8309            }
8310            return true;
8311         });
8312         return dirty;
8313     },
8314      /**
8315      * Performs a predefined action (submit or load) or custom actions you define on this form.
8316      * @param {String} actionName The name of the action type
8317      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8318      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8319      * accept other config options):
8320      * <pre>
8321 Property          Type             Description
8322 ----------------  ---------------  ----------------------------------------------------------------------------------
8323 url               String           The url for the action (defaults to the form's url)
8324 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8325 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8326 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8327                                    validate the form on the client (defaults to false)
8328      * </pre>
8329      * @return {BasicForm} this
8330      */
8331     doAction : function(action, options){
8332         if(typeof action == 'string'){
8333             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8334         }
8335         if(this.fireEvent('beforeaction', this, action) !== false){
8336             this.beforeAction(action);
8337             action.run.defer(100, action);
8338         }
8339         return this;
8340     },
8341
8342     // private
8343     beforeAction : function(action){
8344         var o = action.options;
8345         
8346         if(this.loadMask){
8347             
8348             if(this.maskBody){
8349                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8350             } else {
8351                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8352             }
8353         }
8354         // not really supported yet.. ??
8355
8356         //if(this.waitMsgTarget === true){
8357         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8358         //}else if(this.waitMsgTarget){
8359         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8360         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8361         //}else {
8362         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8363        // }
8364
8365     },
8366
8367     // private
8368     afterAction : function(action, success){
8369         this.activeAction = null;
8370         var o = action.options;
8371
8372         if(this.loadMask){
8373             
8374             if(this.maskBody){
8375                 Roo.get(document.body).unmask();
8376             } else {
8377                 this.el.unmask();
8378             }
8379         }
8380         
8381         //if(this.waitMsgTarget === true){
8382 //            this.el.unmask();
8383         //}else if(this.waitMsgTarget){
8384         //    this.waitMsgTarget.unmask();
8385         //}else{
8386         //    Roo.MessageBox.updateProgress(1);
8387         //    Roo.MessageBox.hide();
8388        // }
8389         //
8390         if(success){
8391             if(o.reset){
8392                 this.reset();
8393             }
8394             Roo.callback(o.success, o.scope, [this, action]);
8395             this.fireEvent('actioncomplete', this, action);
8396
8397         }else{
8398
8399             // failure condition..
8400             // we have a scenario where updates need confirming.
8401             // eg. if a locking scenario exists..
8402             // we look for { errors : { needs_confirm : true }} in the response.
8403             if (
8404                 (typeof(action.result) != 'undefined')  &&
8405                 (typeof(action.result.errors) != 'undefined')  &&
8406                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8407            ){
8408                 var _t = this;
8409                 Roo.log("not supported yet");
8410                  /*
8411
8412                 Roo.MessageBox.confirm(
8413                     "Change requires confirmation",
8414                     action.result.errorMsg,
8415                     function(r) {
8416                         if (r != 'yes') {
8417                             return;
8418                         }
8419                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8420                     }
8421
8422                 );
8423                 */
8424
8425
8426                 return;
8427             }
8428
8429             Roo.callback(o.failure, o.scope, [this, action]);
8430             // show an error message if no failed handler is set..
8431             if (!this.hasListener('actionfailed')) {
8432                 Roo.log("need to add dialog support");
8433                 /*
8434                 Roo.MessageBox.alert("Error",
8435                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8436                         action.result.errorMsg :
8437                         "Saving Failed, please check your entries or try again"
8438                 );
8439                 */
8440             }
8441
8442             this.fireEvent('actionfailed', this, action);
8443         }
8444
8445     },
8446     /**
8447      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8448      * @param {String} id The value to search for
8449      * @return Field
8450      */
8451     findField : function(id){
8452         var items = this.getItems();
8453         var field = items.get(id);
8454         if(!field){
8455              items.each(function(f){
8456                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8457                     field = f;
8458                     return false;
8459                 }
8460                 return true;
8461             });
8462         }
8463         return field || null;
8464     },
8465      /**
8466      * Mark fields in this form invalid in bulk.
8467      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8468      * @return {BasicForm} this
8469      */
8470     markInvalid : function(errors){
8471         if(errors instanceof Array){
8472             for(var i = 0, len = errors.length; i < len; i++){
8473                 var fieldError = errors[i];
8474                 var f = this.findField(fieldError.id);
8475                 if(f){
8476                     f.markInvalid(fieldError.msg);
8477                 }
8478             }
8479         }else{
8480             var field, id;
8481             for(id in errors){
8482                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8483                     field.markInvalid(errors[id]);
8484                 }
8485             }
8486         }
8487         //Roo.each(this.childForms || [], function (f) {
8488         //    f.markInvalid(errors);
8489         //});
8490
8491         return this;
8492     },
8493
8494     /**
8495      * Set values for fields in this form in bulk.
8496      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8497      * @return {BasicForm} this
8498      */
8499     setValues : function(values){
8500         if(values instanceof Array){ // array of objects
8501             for(var i = 0, len = values.length; i < len; i++){
8502                 var v = values[i];
8503                 var f = this.findField(v.id);
8504                 if(f){
8505                     f.setValue(v.value);
8506                     if(this.trackResetOnLoad){
8507                         f.originalValue = f.getValue();
8508                     }
8509                 }
8510             }
8511         }else{ // object hash
8512             var field, id;
8513             for(id in values){
8514                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8515
8516                     if (field.setFromData &&
8517                         field.valueField &&
8518                         field.displayField &&
8519                         // combos' with local stores can
8520                         // be queried via setValue()
8521                         // to set their value..
8522                         (field.store && !field.store.isLocal)
8523                         ) {
8524                         // it's a combo
8525                         var sd = { };
8526                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8527                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8528                         field.setFromData(sd);
8529
8530                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8531                         
8532                         field.setFromData(values);
8533                         
8534                     } else {
8535                         field.setValue(values[id]);
8536                     }
8537
8538
8539                     if(this.trackResetOnLoad){
8540                         field.originalValue = field.getValue();
8541                     }
8542                 }
8543             }
8544         }
8545
8546         //Roo.each(this.childForms || [], function (f) {
8547         //    f.setValues(values);
8548         //});
8549
8550         return this;
8551     },
8552
8553     /**
8554      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8555      * they are returned as an array.
8556      * @param {Boolean} asString
8557      * @return {Object}
8558      */
8559     getValues : function(asString){
8560         //if (this.childForms) {
8561             // copy values from the child forms
8562         //    Roo.each(this.childForms, function (f) {
8563         //        this.setValues(f.getValues());
8564         //    }, this);
8565         //}
8566
8567
8568
8569         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8570         if(asString === true){
8571             return fs;
8572         }
8573         return Roo.urlDecode(fs);
8574     },
8575
8576     /**
8577      * Returns the fields in this form as an object with key/value pairs.
8578      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8579      * @return {Object}
8580      */
8581     getFieldValues : function(with_hidden)
8582     {
8583         var items = this.getItems();
8584         var ret = {};
8585         items.each(function(f){
8586             
8587             if (!f.getName()) {
8588                 return;
8589             }
8590             
8591             var v = f.getValue();
8592             
8593             if (f.inputType =='radio') {
8594                 if (typeof(ret[f.getName()]) == 'undefined') {
8595                     ret[f.getName()] = ''; // empty..
8596                 }
8597
8598                 if (!f.el.dom.checked) {
8599                     return;
8600
8601                 }
8602                 v = f.el.dom.value;
8603
8604             }
8605             
8606             if(f.xtype == 'MoneyField'){
8607                 ret[f.currencyName] = f.getCurrency();
8608             }
8609
8610             // not sure if this supported any more..
8611             if ((typeof(v) == 'object') && f.getRawValue) {
8612                 v = f.getRawValue() ; // dates..
8613             }
8614             // combo boxes where name != hiddenName...
8615             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8616                 ret[f.name] = f.getRawValue();
8617             }
8618             ret[f.getName()] = v;
8619         });
8620
8621         return ret;
8622     },
8623
8624     /**
8625      * Clears all invalid messages in this form.
8626      * @return {BasicForm} this
8627      */
8628     clearInvalid : function(){
8629         var items = this.getItems();
8630
8631         items.each(function(f){
8632            f.clearInvalid();
8633         });
8634
8635         return this;
8636     },
8637
8638     /**
8639      * Resets this form.
8640      * @return {BasicForm} this
8641      */
8642     reset : function(){
8643         var items = this.getItems();
8644         items.each(function(f){
8645             f.reset();
8646         });
8647
8648         Roo.each(this.childForms || [], function (f) {
8649             f.reset();
8650         });
8651
8652
8653         return this;
8654     },
8655     
8656     getItems : function()
8657     {
8658         var r=new Roo.util.MixedCollection(false, function(o){
8659             return o.id || (o.id = Roo.id());
8660         });
8661         var iter = function(el) {
8662             if (el.inputEl) {
8663                 r.add(el);
8664             }
8665             if (!el.items) {
8666                 return;
8667             }
8668             Roo.each(el.items,function(e) {
8669                 iter(e);
8670             });
8671         };
8672
8673         iter(this);
8674         return r;
8675     },
8676     
8677     hideFields : function(items)
8678     {
8679         Roo.each(items, function(i){
8680             
8681             var f = this.findField(i);
8682             
8683             if(!f){
8684                 return;
8685             }
8686             
8687             f.hide();
8688             
8689         }, this);
8690     },
8691     
8692     showFields : function(items)
8693     {
8694         Roo.each(items, function(i){
8695             
8696             var f = this.findField(i);
8697             
8698             if(!f){
8699                 return;
8700             }
8701             
8702             f.show();
8703             
8704         }, this);
8705     }
8706
8707 });
8708
8709 Roo.apply(Roo.bootstrap.Form, {
8710     
8711     popover : {
8712         
8713         padding : 5,
8714         
8715         isApplied : false,
8716         
8717         isMasked : false,
8718         
8719         form : false,
8720         
8721         target : false,
8722         
8723         toolTip : false,
8724         
8725         intervalID : false,
8726         
8727         maskEl : false,
8728         
8729         apply : function()
8730         {
8731             if(this.isApplied){
8732                 return;
8733             }
8734             
8735             this.maskEl = {
8736                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8737                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8738                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8739                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8740             };
8741             
8742             this.maskEl.top.enableDisplayMode("block");
8743             this.maskEl.left.enableDisplayMode("block");
8744             this.maskEl.bottom.enableDisplayMode("block");
8745             this.maskEl.right.enableDisplayMode("block");
8746             
8747             this.toolTip = new Roo.bootstrap.Tooltip({
8748                 cls : 'roo-form-error-popover',
8749                 alignment : {
8750                     'left' : ['r-l', [-2,0], 'right'],
8751                     'right' : ['l-r', [2,0], 'left'],
8752                     'bottom' : ['tl-bl', [0,2], 'top'],
8753                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8754                 }
8755             });
8756             
8757             this.toolTip.render(Roo.get(document.body));
8758
8759             this.toolTip.el.enableDisplayMode("block");
8760             
8761             Roo.get(document.body).on('click', function(){
8762                 this.unmask();
8763             }, this);
8764             
8765             Roo.get(document.body).on('touchstart', function(){
8766                 this.unmask();
8767             }, this);
8768             
8769             this.isApplied = true
8770         },
8771         
8772         mask : function(form, target)
8773         {
8774             this.form = form;
8775             
8776             this.target = target;
8777             
8778             if(!this.form.errorMask || !target.el){
8779                 return;
8780             }
8781             
8782             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8783             
8784             Roo.log(scrollable);
8785             
8786             var ot = this.target.el.calcOffsetsTo(scrollable);
8787             
8788             var scrollTo = ot[1] - this.form.maskOffset;
8789             
8790             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8791             
8792             scrollable.scrollTo('top', scrollTo);
8793             
8794             var box = this.target.el.getBox();
8795             Roo.log(box);
8796             var zIndex = Roo.bootstrap.Modal.zIndex++;
8797
8798             
8799             this.maskEl.top.setStyle('position', 'absolute');
8800             this.maskEl.top.setStyle('z-index', zIndex);
8801             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8802             this.maskEl.top.setLeft(0);
8803             this.maskEl.top.setTop(0);
8804             this.maskEl.top.show();
8805             
8806             this.maskEl.left.setStyle('position', 'absolute');
8807             this.maskEl.left.setStyle('z-index', zIndex);
8808             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8809             this.maskEl.left.setLeft(0);
8810             this.maskEl.left.setTop(box.y - this.padding);
8811             this.maskEl.left.show();
8812
8813             this.maskEl.bottom.setStyle('position', 'absolute');
8814             this.maskEl.bottom.setStyle('z-index', zIndex);
8815             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8816             this.maskEl.bottom.setLeft(0);
8817             this.maskEl.bottom.setTop(box.bottom + this.padding);
8818             this.maskEl.bottom.show();
8819
8820             this.maskEl.right.setStyle('position', 'absolute');
8821             this.maskEl.right.setStyle('z-index', zIndex);
8822             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8823             this.maskEl.right.setLeft(box.right + this.padding);
8824             this.maskEl.right.setTop(box.y - this.padding);
8825             this.maskEl.right.show();
8826
8827             this.toolTip.bindEl = this.target.el;
8828
8829             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8830
8831             var tip = this.target.blankText;
8832
8833             if(this.target.getValue() !== '' ) {
8834                 
8835                 if (this.target.invalidText.length) {
8836                     tip = this.target.invalidText;
8837                 } else if (this.target.regexText.length){
8838                     tip = this.target.regexText;
8839                 }
8840             }
8841
8842             this.toolTip.show(tip);
8843
8844             this.intervalID = window.setInterval(function() {
8845                 Roo.bootstrap.Form.popover.unmask();
8846             }, 10000);
8847
8848             window.onwheel = function(){ return false;};
8849             
8850             (function(){ this.isMasked = true; }).defer(500, this);
8851             
8852         },
8853         
8854         unmask : function()
8855         {
8856             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8857                 return;
8858             }
8859             
8860             this.maskEl.top.setStyle('position', 'absolute');
8861             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8862             this.maskEl.top.hide();
8863
8864             this.maskEl.left.setStyle('position', 'absolute');
8865             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8866             this.maskEl.left.hide();
8867
8868             this.maskEl.bottom.setStyle('position', 'absolute');
8869             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8870             this.maskEl.bottom.hide();
8871
8872             this.maskEl.right.setStyle('position', 'absolute');
8873             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8874             this.maskEl.right.hide();
8875             
8876             this.toolTip.hide();
8877             
8878             this.toolTip.el.hide();
8879             
8880             window.onwheel = function(){ return true;};
8881             
8882             if(this.intervalID){
8883                 window.clearInterval(this.intervalID);
8884                 this.intervalID = false;
8885             }
8886             
8887             this.isMasked = false;
8888             
8889         }
8890         
8891     }
8892     
8893 });
8894
8895 /*
8896  * Based on:
8897  * Ext JS Library 1.1.1
8898  * Copyright(c) 2006-2007, Ext JS, LLC.
8899  *
8900  * Originally Released Under LGPL - original licence link has changed is not relivant.
8901  *
8902  * Fork - LGPL
8903  * <script type="text/javascript">
8904  */
8905 /**
8906  * @class Roo.form.VTypes
8907  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8908  * @singleton
8909  */
8910 Roo.form.VTypes = function(){
8911     // closure these in so they are only created once.
8912     var alpha = /^[a-zA-Z_]+$/;
8913     var alphanum = /^[a-zA-Z0-9_]+$/;
8914     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8915     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8916
8917     // All these messages and functions are configurable
8918     return {
8919         /**
8920          * The function used to validate email addresses
8921          * @param {String} value The email address
8922          */
8923         'email' : function(v){
8924             return email.test(v);
8925         },
8926         /**
8927          * The error text to display when the email validation function returns false
8928          * @type String
8929          */
8930         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8931         /**
8932          * The keystroke filter mask to be applied on email input
8933          * @type RegExp
8934          */
8935         'emailMask' : /[a-z0-9_\.\-@]/i,
8936
8937         /**
8938          * The function used to validate URLs
8939          * @param {String} value The URL
8940          */
8941         'url' : function(v){
8942             return url.test(v);
8943         },
8944         /**
8945          * The error text to display when the url validation function returns false
8946          * @type String
8947          */
8948         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8949         
8950         /**
8951          * The function used to validate alpha values
8952          * @param {String} value The value
8953          */
8954         'alpha' : function(v){
8955             return alpha.test(v);
8956         },
8957         /**
8958          * The error text to display when the alpha validation function returns false
8959          * @type String
8960          */
8961         'alphaText' : 'This field should only contain letters and _',
8962         /**
8963          * The keystroke filter mask to be applied on alpha input
8964          * @type RegExp
8965          */
8966         'alphaMask' : /[a-z_]/i,
8967
8968         /**
8969          * The function used to validate alphanumeric values
8970          * @param {String} value The value
8971          */
8972         'alphanum' : function(v){
8973             return alphanum.test(v);
8974         },
8975         /**
8976          * The error text to display when the alphanumeric validation function returns false
8977          * @type String
8978          */
8979         'alphanumText' : 'This field should only contain letters, numbers and _',
8980         /**
8981          * The keystroke filter mask to be applied on alphanumeric input
8982          * @type RegExp
8983          */
8984         'alphanumMask' : /[a-z0-9_]/i
8985     };
8986 }();/*
8987  * - LGPL
8988  *
8989  * Input
8990  * 
8991  */
8992
8993 /**
8994  * @class Roo.bootstrap.Input
8995  * @extends Roo.bootstrap.Component
8996  * Bootstrap Input class
8997  * @cfg {Boolean} disabled is it disabled
8998  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8999  * @cfg {String} name name of the input
9000  * @cfg {string} fieldLabel - the label associated
9001  * @cfg {string} placeholder - placeholder to put in text.
9002  * @cfg {string}  before - input group add on before
9003  * @cfg {string} after - input group add on after
9004  * @cfg {string} size - (lg|sm) or leave empty..
9005  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9006  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9007  * @cfg {Number} md colspan out of 12 for computer-sized screens
9008  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9009  * @cfg {string} value default value of the input
9010  * @cfg {Number} labelWidth set the width of label 
9011  * @cfg {Number} labellg set the width of label (1-12)
9012  * @cfg {Number} labelmd set the width of label (1-12)
9013  * @cfg {Number} labelsm set the width of label (1-12)
9014  * @cfg {Number} labelxs set the width of label (1-12)
9015  * @cfg {String} labelAlign (top|left)
9016  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9017  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9018  * @cfg {String} indicatorpos (left|right) default left
9019  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9020  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9021
9022  * @cfg {String} align (left|center|right) Default left
9023  * @cfg {Boolean} forceFeedback (true|false) Default false
9024  * 
9025  * @constructor
9026  * Create a new Input
9027  * @param {Object} config The config object
9028  */
9029
9030 Roo.bootstrap.Input = function(config){
9031     
9032     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9033     
9034     this.addEvents({
9035         /**
9036          * @event focus
9037          * Fires when this field receives input focus.
9038          * @param {Roo.form.Field} this
9039          */
9040         focus : true,
9041         /**
9042          * @event blur
9043          * Fires when this field loses input focus.
9044          * @param {Roo.form.Field} this
9045          */
9046         blur : true,
9047         /**
9048          * @event specialkey
9049          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9050          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9051          * @param {Roo.form.Field} this
9052          * @param {Roo.EventObject} e The event object
9053          */
9054         specialkey : true,
9055         /**
9056          * @event change
9057          * Fires just before the field blurs if the field value has changed.
9058          * @param {Roo.form.Field} this
9059          * @param {Mixed} newValue The new value
9060          * @param {Mixed} oldValue The original value
9061          */
9062         change : true,
9063         /**
9064          * @event invalid
9065          * Fires after the field has been marked as invalid.
9066          * @param {Roo.form.Field} this
9067          * @param {String} msg The validation message
9068          */
9069         invalid : true,
9070         /**
9071          * @event valid
9072          * Fires after the field has been validated with no errors.
9073          * @param {Roo.form.Field} this
9074          */
9075         valid : true,
9076          /**
9077          * @event keyup
9078          * Fires after the key up
9079          * @param {Roo.form.Field} this
9080          * @param {Roo.EventObject}  e The event Object
9081          */
9082         keyup : true
9083     });
9084 };
9085
9086 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9087      /**
9088      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9089       automatic validation (defaults to "keyup").
9090      */
9091     validationEvent : "keyup",
9092      /**
9093      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9094      */
9095     validateOnBlur : true,
9096     /**
9097      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9098      */
9099     validationDelay : 250,
9100      /**
9101      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9102      */
9103     focusClass : "x-form-focus",  // not needed???
9104     
9105        
9106     /**
9107      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9108      */
9109     invalidClass : "has-warning",
9110     
9111     /**
9112      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9113      */
9114     validClass : "has-success",
9115     
9116     /**
9117      * @cfg {Boolean} hasFeedback (true|false) default true
9118      */
9119     hasFeedback : true,
9120     
9121     /**
9122      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9123      */
9124     invalidFeedbackClass : "glyphicon-warning-sign",
9125     
9126     /**
9127      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9128      */
9129     validFeedbackClass : "glyphicon-ok",
9130     
9131     /**
9132      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9133      */
9134     selectOnFocus : false,
9135     
9136      /**
9137      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9138      */
9139     maskRe : null,
9140        /**
9141      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9142      */
9143     vtype : null,
9144     
9145       /**
9146      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9147      */
9148     disableKeyFilter : false,
9149     
9150        /**
9151      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9152      */
9153     disabled : false,
9154      /**
9155      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9156      */
9157     allowBlank : true,
9158     /**
9159      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9160      */
9161     blankText : "Please complete this mandatory field",
9162     
9163      /**
9164      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9165      */
9166     minLength : 0,
9167     /**
9168      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9169      */
9170     maxLength : Number.MAX_VALUE,
9171     /**
9172      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9173      */
9174     minLengthText : "The minimum length for this field is {0}",
9175     /**
9176      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9177      */
9178     maxLengthText : "The maximum length for this field is {0}",
9179   
9180     
9181     /**
9182      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9183      * If available, this function will be called only after the basic validators all return true, and will be passed the
9184      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9185      */
9186     validator : null,
9187     /**
9188      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9189      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9190      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9191      */
9192     regex : null,
9193     /**
9194      * @cfg {String} regexText -- Depricated - use Invalid Text
9195      */
9196     regexText : "",
9197     
9198     /**
9199      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9200      */
9201     invalidText : "",
9202     
9203     
9204     
9205     autocomplete: false,
9206     
9207     
9208     fieldLabel : '',
9209     inputType : 'text',
9210     
9211     name : false,
9212     placeholder: false,
9213     before : false,
9214     after : false,
9215     size : false,
9216     hasFocus : false,
9217     preventMark: false,
9218     isFormField : true,
9219     value : '',
9220     labelWidth : 2,
9221     labelAlign : false,
9222     readOnly : false,
9223     align : false,
9224     formatedValue : false,
9225     forceFeedback : false,
9226     
9227     indicatorpos : 'left',
9228     
9229     labellg : 0,
9230     labelmd : 0,
9231     labelsm : 0,
9232     labelxs : 0,
9233     
9234     capture : '',
9235     accept : '',
9236     
9237     parentLabelAlign : function()
9238     {
9239         var parent = this;
9240         while (parent.parent()) {
9241             parent = parent.parent();
9242             if (typeof(parent.labelAlign) !='undefined') {
9243                 return parent.labelAlign;
9244             }
9245         }
9246         return 'left';
9247         
9248     },
9249     
9250     getAutoCreate : function()
9251     {
9252         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9253         
9254         var id = Roo.id();
9255         
9256         var cfg = {};
9257         
9258         if(this.inputType != 'hidden'){
9259             cfg.cls = 'form-group' //input-group
9260         }
9261         
9262         var input =  {
9263             tag: 'input',
9264             id : id,
9265             type : this.inputType,
9266             value : this.value,
9267             cls : 'form-control',
9268             placeholder : this.placeholder || '',
9269             autocomplete : this.autocomplete || 'new-password'
9270         };
9271         
9272         if(this.capture.length){
9273             input.capture = this.capture;
9274         }
9275         
9276         if(this.accept.length){
9277             input.accept = this.accept + "/*";
9278         }
9279         
9280         if(this.align){
9281             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9282         }
9283         
9284         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9285             input.maxLength = this.maxLength;
9286         }
9287         
9288         if (this.disabled) {
9289             input.disabled=true;
9290         }
9291         
9292         if (this.readOnly) {
9293             input.readonly=true;
9294         }
9295         
9296         if (this.name) {
9297             input.name = this.name;
9298         }
9299         
9300         if (this.size) {
9301             input.cls += ' input-' + this.size;
9302         }
9303         
9304         var settings=this;
9305         ['xs','sm','md','lg'].map(function(size){
9306             if (settings[size]) {
9307                 cfg.cls += ' col-' + size + '-' + settings[size];
9308             }
9309         });
9310         
9311         var inputblock = input;
9312         
9313         var feedback = {
9314             tag: 'span',
9315             cls: 'glyphicon form-control-feedback'
9316         };
9317             
9318         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9319             
9320             inputblock = {
9321                 cls : 'has-feedback',
9322                 cn :  [
9323                     input,
9324                     feedback
9325                 ] 
9326             };  
9327         }
9328         
9329         if (this.before || this.after) {
9330             
9331             inputblock = {
9332                 cls : 'input-group',
9333                 cn :  [] 
9334             };
9335             
9336             if (this.before && typeof(this.before) == 'string') {
9337                 
9338                 inputblock.cn.push({
9339                     tag :'span',
9340                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9341                     html : this.before
9342                 });
9343             }
9344             if (this.before && typeof(this.before) == 'object') {
9345                 this.before = Roo.factory(this.before);
9346                 
9347                 inputblock.cn.push({
9348                     tag :'span',
9349                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9350                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9351                 });
9352             }
9353             
9354             inputblock.cn.push(input);
9355             
9356             if (this.after && typeof(this.after) == 'string') {
9357                 inputblock.cn.push({
9358                     tag :'span',
9359                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9360                     html : this.after
9361                 });
9362             }
9363             if (this.after && typeof(this.after) == 'object') {
9364                 this.after = Roo.factory(this.after);
9365                 
9366                 inputblock.cn.push({
9367                     tag :'span',
9368                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9369                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9370                 });
9371             }
9372             
9373             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9374                 inputblock.cls += ' has-feedback';
9375                 inputblock.cn.push(feedback);
9376             }
9377         };
9378         var indicator = {
9379             tag : 'i',
9380             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9381             tooltip : 'This field is required'
9382         };
9383         if (Roo.bootstrap.version == 4) {
9384             indicator = {
9385                 tag : 'i',
9386                 style : 'display-none'
9387             };
9388         }
9389         if (align ==='left' && this.fieldLabel.length) {
9390             
9391             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9392             
9393             cfg.cn = [
9394                 indicator,
9395                 {
9396                     tag: 'label',
9397                     'for' :  id,
9398                     cls : 'control-label col-form-label',
9399                     html : this.fieldLabel
9400
9401                 },
9402                 {
9403                     cls : "", 
9404                     cn: [
9405                         inputblock
9406                     ]
9407                 }
9408             ];
9409             
9410             var labelCfg = cfg.cn[1];
9411             var contentCfg = cfg.cn[2];
9412             
9413             if(this.indicatorpos == 'right'){
9414                 cfg.cn = [
9415                     {
9416                         tag: 'label',
9417                         'for' :  id,
9418                         cls : 'control-label col-form-label',
9419                         cn : [
9420                             {
9421                                 tag : 'span',
9422                                 html : this.fieldLabel
9423                             },
9424                             indicator
9425                         ]
9426                     },
9427                     {
9428                         cls : "",
9429                         cn: [
9430                             inputblock
9431                         ]
9432                     }
9433
9434                 ];
9435                 
9436                 labelCfg = cfg.cn[0];
9437                 contentCfg = cfg.cn[1];
9438             
9439             }
9440             
9441             if(this.labelWidth > 12){
9442                 labelCfg.style = "width: " + this.labelWidth + 'px';
9443             }
9444             
9445             if(this.labelWidth < 13 && this.labelmd == 0){
9446                 this.labelmd = this.labelWidth;
9447             }
9448             
9449             if(this.labellg > 0){
9450                 labelCfg.cls += ' col-lg-' + this.labellg;
9451                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9452             }
9453             
9454             if(this.labelmd > 0){
9455                 labelCfg.cls += ' col-md-' + this.labelmd;
9456                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9457             }
9458             
9459             if(this.labelsm > 0){
9460                 labelCfg.cls += ' col-sm-' + this.labelsm;
9461                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9462             }
9463             
9464             if(this.labelxs > 0){
9465                 labelCfg.cls += ' col-xs-' + this.labelxs;
9466                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9467             }
9468             
9469             
9470         } else if ( this.fieldLabel.length) {
9471                 
9472             cfg.cn = [
9473                 {
9474                     tag : 'i',
9475                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9476                     tooltip : 'This field is required'
9477                 },
9478                 {
9479                     tag: 'label',
9480                    //cls : 'input-group-addon',
9481                     html : this.fieldLabel
9482
9483                 },
9484
9485                inputblock
9486
9487            ];
9488            
9489            if(this.indicatorpos == 'right'){
9490                 
9491                 cfg.cn = [
9492                     {
9493                         tag: 'label',
9494                        //cls : 'input-group-addon',
9495                         html : this.fieldLabel
9496
9497                     },
9498                     {
9499                         tag : 'i',
9500                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9501                         tooltip : 'This field is required'
9502                     },
9503
9504                    inputblock
9505
9506                ];
9507
9508             }
9509
9510         } else {
9511             
9512             cfg.cn = [
9513
9514                     inputblock
9515
9516             ];
9517                 
9518                 
9519         };
9520         
9521         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9522            cfg.cls += ' navbar-form';
9523         }
9524         
9525         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9526             // on BS4 we do this only if not form 
9527             cfg.cls += ' navbar-form';
9528             cfg.tag = 'li';
9529         }
9530         
9531         return cfg;
9532         
9533     },
9534     /**
9535      * return the real input element.
9536      */
9537     inputEl: function ()
9538     {
9539         return this.el.select('input.form-control',true).first();
9540     },
9541     
9542     tooltipEl : function()
9543     {
9544         return this.inputEl();
9545     },
9546     
9547     indicatorEl : function()
9548     {
9549         if (Roo.bootstrap.version == 4) {
9550             return false; // not enabled in v4 yet.
9551         }
9552         
9553         var indicator = this.el.select('i.roo-required-indicator',true).first();
9554         
9555         if(!indicator){
9556             return false;
9557         }
9558         
9559         return indicator;
9560         
9561     },
9562     
9563     setDisabled : function(v)
9564     {
9565         var i  = this.inputEl().dom;
9566         if (!v) {
9567             i.removeAttribute('disabled');
9568             return;
9569             
9570         }
9571         i.setAttribute('disabled','true');
9572     },
9573     initEvents : function()
9574     {
9575           
9576         this.inputEl().on("keydown" , this.fireKey,  this);
9577         this.inputEl().on("focus", this.onFocus,  this);
9578         this.inputEl().on("blur", this.onBlur,  this);
9579         
9580         this.inputEl().relayEvent('keyup', this);
9581         
9582         this.indicator = this.indicatorEl();
9583         
9584         if(this.indicator){
9585             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9586         }
9587  
9588         // reference to original value for reset
9589         this.originalValue = this.getValue();
9590         //Roo.form.TextField.superclass.initEvents.call(this);
9591         if(this.validationEvent == 'keyup'){
9592             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9593             this.inputEl().on('keyup', this.filterValidation, this);
9594         }
9595         else if(this.validationEvent !== false){
9596             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9597         }
9598         
9599         if(this.selectOnFocus){
9600             this.on("focus", this.preFocus, this);
9601             
9602         }
9603         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9604             this.inputEl().on("keypress", this.filterKeys, this);
9605         } else {
9606             this.inputEl().relayEvent('keypress', this);
9607         }
9608        /* if(this.grow){
9609             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9610             this.el.on("click", this.autoSize,  this);
9611         }
9612         */
9613         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9614             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9615         }
9616         
9617         if (typeof(this.before) == 'object') {
9618             this.before.render(this.el.select('.roo-input-before',true).first());
9619         }
9620         if (typeof(this.after) == 'object') {
9621             this.after.render(this.el.select('.roo-input-after',true).first());
9622         }
9623         
9624         this.inputEl().on('change', this.onChange, this);
9625         
9626     },
9627     filterValidation : function(e){
9628         if(!e.isNavKeyPress()){
9629             this.validationTask.delay(this.validationDelay);
9630         }
9631     },
9632      /**
9633      * Validates the field value
9634      * @return {Boolean} True if the value is valid, else false
9635      */
9636     validate : function(){
9637         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9638         if(this.disabled || this.validateValue(this.getRawValue())){
9639             this.markValid();
9640             return true;
9641         }
9642         
9643         this.markInvalid();
9644         return false;
9645     },
9646     
9647     
9648     /**
9649      * Validates a value according to the field's validation rules and marks the field as invalid
9650      * if the validation fails
9651      * @param {Mixed} value The value to validate
9652      * @return {Boolean} True if the value is valid, else false
9653      */
9654     validateValue : function(value)
9655     {
9656         if(this.getVisibilityEl().hasClass('hidden')){
9657             return true;
9658         }
9659         
9660         if(value.length < 1)  { // if it's blank
9661             if(this.allowBlank){
9662                 return true;
9663             }
9664             return false;
9665         }
9666         
9667         if(value.length < this.minLength){
9668             return false;
9669         }
9670         if(value.length > this.maxLength){
9671             return false;
9672         }
9673         if(this.vtype){
9674             var vt = Roo.form.VTypes;
9675             if(!vt[this.vtype](value, this)){
9676                 return false;
9677             }
9678         }
9679         if(typeof this.validator == "function"){
9680             var msg = this.validator(value);
9681             if(msg !== true){
9682                 return false;
9683             }
9684             if (typeof(msg) == 'string') {
9685                 this.invalidText = msg;
9686             }
9687         }
9688         
9689         if(this.regex && !this.regex.test(value)){
9690             return false;
9691         }
9692         
9693         return true;
9694     },
9695     
9696      // private
9697     fireKey : function(e){
9698         //Roo.log('field ' + e.getKey());
9699         if(e.isNavKeyPress()){
9700             this.fireEvent("specialkey", this, e);
9701         }
9702     },
9703     focus : function (selectText){
9704         if(this.rendered){
9705             this.inputEl().focus();
9706             if(selectText === true){
9707                 this.inputEl().dom.select();
9708             }
9709         }
9710         return this;
9711     } ,
9712     
9713     onFocus : function(){
9714         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9715            // this.el.addClass(this.focusClass);
9716         }
9717         if(!this.hasFocus){
9718             this.hasFocus = true;
9719             this.startValue = this.getValue();
9720             this.fireEvent("focus", this);
9721         }
9722     },
9723     
9724     beforeBlur : Roo.emptyFn,
9725
9726     
9727     // private
9728     onBlur : function(){
9729         this.beforeBlur();
9730         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9731             //this.el.removeClass(this.focusClass);
9732         }
9733         this.hasFocus = false;
9734         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9735             this.validate();
9736         }
9737         var v = this.getValue();
9738         if(String(v) !== String(this.startValue)){
9739             this.fireEvent('change', this, v, this.startValue);
9740         }
9741         this.fireEvent("blur", this);
9742     },
9743     
9744     onChange : function(e)
9745     {
9746         var v = this.getValue();
9747         if(String(v) !== String(this.startValue)){
9748             this.fireEvent('change', this, v, this.startValue);
9749         }
9750         
9751     },
9752     
9753     /**
9754      * Resets the current field value to the originally loaded value and clears any validation messages
9755      */
9756     reset : function(){
9757         this.setValue(this.originalValue);
9758         this.validate();
9759     },
9760      /**
9761      * Returns the name of the field
9762      * @return {Mixed} name The name field
9763      */
9764     getName: function(){
9765         return this.name;
9766     },
9767      /**
9768      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9769      * @return {Mixed} value The field value
9770      */
9771     getValue : function(){
9772         
9773         var v = this.inputEl().getValue();
9774         
9775         return v;
9776     },
9777     /**
9778      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9779      * @return {Mixed} value The field value
9780      */
9781     getRawValue : function(){
9782         var v = this.inputEl().getValue();
9783         
9784         return v;
9785     },
9786     
9787     /**
9788      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9789      * @param {Mixed} value The value to set
9790      */
9791     setRawValue : function(v){
9792         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9793     },
9794     
9795     selectText : function(start, end){
9796         var v = this.getRawValue();
9797         if(v.length > 0){
9798             start = start === undefined ? 0 : start;
9799             end = end === undefined ? v.length : end;
9800             var d = this.inputEl().dom;
9801             if(d.setSelectionRange){
9802                 d.setSelectionRange(start, end);
9803             }else if(d.createTextRange){
9804                 var range = d.createTextRange();
9805                 range.moveStart("character", start);
9806                 range.moveEnd("character", v.length-end);
9807                 range.select();
9808             }
9809         }
9810     },
9811     
9812     /**
9813      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9814      * @param {Mixed} value The value to set
9815      */
9816     setValue : function(v){
9817         this.value = v;
9818         if(this.rendered){
9819             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9820             this.validate();
9821         }
9822     },
9823     
9824     /*
9825     processValue : function(value){
9826         if(this.stripCharsRe){
9827             var newValue = value.replace(this.stripCharsRe, '');
9828             if(newValue !== value){
9829                 this.setRawValue(newValue);
9830                 return newValue;
9831             }
9832         }
9833         return value;
9834     },
9835   */
9836     preFocus : function(){
9837         
9838         if(this.selectOnFocus){
9839             this.inputEl().dom.select();
9840         }
9841     },
9842     filterKeys : function(e){
9843         var k = e.getKey();
9844         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9845             return;
9846         }
9847         var c = e.getCharCode(), cc = String.fromCharCode(c);
9848         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9849             return;
9850         }
9851         if(!this.maskRe.test(cc)){
9852             e.stopEvent();
9853         }
9854     },
9855      /**
9856      * Clear any invalid styles/messages for this field
9857      */
9858     clearInvalid : function(){
9859         
9860         if(!this.el || this.preventMark){ // not rendered
9861             return;
9862         }
9863         
9864         
9865         this.el.removeClass([this.invalidClass, 'is-invalid']);
9866         
9867         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9868             
9869             var feedback = this.el.select('.form-control-feedback', true).first();
9870             
9871             if(feedback){
9872                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9873             }
9874             
9875         }
9876         
9877         if(this.indicator){
9878             this.indicator.removeClass('visible');
9879             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9880         }
9881         
9882         this.fireEvent('valid', this);
9883     },
9884     
9885      /**
9886      * Mark this field as valid
9887      */
9888     markValid : function()
9889     {
9890         if(!this.el  || this.preventMark){ // not rendered...
9891             return;
9892         }
9893         
9894         this.el.removeClass([this.invalidClass, this.validClass]);
9895         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9896
9897         var feedback = this.el.select('.form-control-feedback', true).first();
9898             
9899         if(feedback){
9900             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9901         }
9902         
9903         if(this.indicator){
9904             this.indicator.removeClass('visible');
9905             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9906         }
9907         
9908         if(this.disabled){
9909             return;
9910         }
9911         
9912         if(this.allowBlank && !this.getRawValue().length){
9913             return;
9914         }
9915         if (Roo.bootstrap.version == 3) {
9916             this.el.addClass(this.validClass);
9917         } else {
9918             this.inputEl().addClass('is-valid');
9919         }
9920
9921         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9922             
9923             var feedback = this.el.select('.form-control-feedback', true).first();
9924             
9925             if(feedback){
9926                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9927                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9928             }
9929             
9930         }
9931         
9932         this.fireEvent('valid', this);
9933     },
9934     
9935      /**
9936      * Mark this field as invalid
9937      * @param {String} msg The validation message
9938      */
9939     markInvalid : function(msg)
9940     {
9941         if(!this.el  || this.preventMark){ // not rendered
9942             return;
9943         }
9944         
9945         this.el.removeClass([this.invalidClass, this.validClass]);
9946         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9947         
9948         var feedback = this.el.select('.form-control-feedback', true).first();
9949             
9950         if(feedback){
9951             this.el.select('.form-control-feedback', true).first().removeClass(
9952                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9953         }
9954
9955         if(this.disabled){
9956             return;
9957         }
9958         
9959         if(this.allowBlank && !this.getRawValue().length){
9960             return;
9961         }
9962         
9963         if(this.indicator){
9964             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9965             this.indicator.addClass('visible');
9966         }
9967         if (Roo.bootstrap.version == 3) {
9968             this.el.addClass(this.invalidClass);
9969         } else {
9970             this.inputEl().addClass('is-invalid');
9971         }
9972         
9973         
9974         
9975         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9976             
9977             var feedback = this.el.select('.form-control-feedback', true).first();
9978             
9979             if(feedback){
9980                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9981                 
9982                 if(this.getValue().length || this.forceFeedback){
9983                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9984                 }
9985                 
9986             }
9987             
9988         }
9989         
9990         this.fireEvent('invalid', this, msg);
9991     },
9992     // private
9993     SafariOnKeyDown : function(event)
9994     {
9995         // this is a workaround for a password hang bug on chrome/ webkit.
9996         if (this.inputEl().dom.type != 'password') {
9997             return;
9998         }
9999         
10000         var isSelectAll = false;
10001         
10002         if(this.inputEl().dom.selectionEnd > 0){
10003             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10004         }
10005         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10006             event.preventDefault();
10007             this.setValue('');
10008             return;
10009         }
10010         
10011         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10012             
10013             event.preventDefault();
10014             // this is very hacky as keydown always get's upper case.
10015             //
10016             var cc = String.fromCharCode(event.getCharCode());
10017             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10018             
10019         }
10020     },
10021     adjustWidth : function(tag, w){
10022         tag = tag.toLowerCase();
10023         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10024             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10025                 if(tag == 'input'){
10026                     return w + 2;
10027                 }
10028                 if(tag == 'textarea'){
10029                     return w-2;
10030                 }
10031             }else if(Roo.isOpera){
10032                 if(tag == 'input'){
10033                     return w + 2;
10034                 }
10035                 if(tag == 'textarea'){
10036                     return w-2;
10037                 }
10038             }
10039         }
10040         return w;
10041     },
10042     
10043     setFieldLabel : function(v)
10044     {
10045         if(!this.rendered){
10046             return;
10047         }
10048         
10049         if(this.indicatorEl()){
10050             var ar = this.el.select('label > span',true);
10051             
10052             if (ar.elements.length) {
10053                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10054                 this.fieldLabel = v;
10055                 return;
10056             }
10057             
10058             var br = this.el.select('label',true);
10059             
10060             if(br.elements.length) {
10061                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10062                 this.fieldLabel = v;
10063                 return;
10064             }
10065             
10066             Roo.log('Cannot Found any of label > span || label in input');
10067             return;
10068         }
10069         
10070         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10071         this.fieldLabel = v;
10072         
10073         
10074     }
10075 });
10076
10077  
10078 /*
10079  * - LGPL
10080  *
10081  * Input
10082  * 
10083  */
10084
10085 /**
10086  * @class Roo.bootstrap.TextArea
10087  * @extends Roo.bootstrap.Input
10088  * Bootstrap TextArea class
10089  * @cfg {Number} cols Specifies the visible width of a text area
10090  * @cfg {Number} rows Specifies the visible number of lines in a text area
10091  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10092  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10093  * @cfg {string} html text
10094  * 
10095  * @constructor
10096  * Create a new TextArea
10097  * @param {Object} config The config object
10098  */
10099
10100 Roo.bootstrap.TextArea = function(config){
10101     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10102    
10103 };
10104
10105 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10106      
10107     cols : false,
10108     rows : 5,
10109     readOnly : false,
10110     warp : 'soft',
10111     resize : false,
10112     value: false,
10113     html: false,
10114     
10115     getAutoCreate : function(){
10116         
10117         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10118         
10119         var id = Roo.id();
10120         
10121         var cfg = {};
10122         
10123         if(this.inputType != 'hidden'){
10124             cfg.cls = 'form-group' //input-group
10125         }
10126         
10127         var input =  {
10128             tag: 'textarea',
10129             id : id,
10130             warp : this.warp,
10131             rows : this.rows,
10132             value : this.value || '',
10133             html: this.html || '',
10134             cls : 'form-control',
10135             placeholder : this.placeholder || '' 
10136             
10137         };
10138         
10139         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10140             input.maxLength = this.maxLength;
10141         }
10142         
10143         if(this.resize){
10144             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10145         }
10146         
10147         if(this.cols){
10148             input.cols = this.cols;
10149         }
10150         
10151         if (this.readOnly) {
10152             input.readonly = true;
10153         }
10154         
10155         if (this.name) {
10156             input.name = this.name;
10157         }
10158         
10159         if (this.size) {
10160             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10161         }
10162         
10163         var settings=this;
10164         ['xs','sm','md','lg'].map(function(size){
10165             if (settings[size]) {
10166                 cfg.cls += ' col-' + size + '-' + settings[size];
10167             }
10168         });
10169         
10170         var inputblock = input;
10171         
10172         if(this.hasFeedback && !this.allowBlank){
10173             
10174             var feedback = {
10175                 tag: 'span',
10176                 cls: 'glyphicon form-control-feedback'
10177             };
10178
10179             inputblock = {
10180                 cls : 'has-feedback',
10181                 cn :  [
10182                     input,
10183                     feedback
10184                 ] 
10185             };  
10186         }
10187         
10188         
10189         if (this.before || this.after) {
10190             
10191             inputblock = {
10192                 cls : 'input-group',
10193                 cn :  [] 
10194             };
10195             if (this.before) {
10196                 inputblock.cn.push({
10197                     tag :'span',
10198                     cls : 'input-group-addon',
10199                     html : this.before
10200                 });
10201             }
10202             
10203             inputblock.cn.push(input);
10204             
10205             if(this.hasFeedback && !this.allowBlank){
10206                 inputblock.cls += ' has-feedback';
10207                 inputblock.cn.push(feedback);
10208             }
10209             
10210             if (this.after) {
10211                 inputblock.cn.push({
10212                     tag :'span',
10213                     cls : 'input-group-addon',
10214                     html : this.after
10215                 });
10216             }
10217             
10218         }
10219         
10220         if (align ==='left' && this.fieldLabel.length) {
10221             cfg.cn = [
10222                 {
10223                     tag: 'label',
10224                     'for' :  id,
10225                     cls : 'control-label',
10226                     html : this.fieldLabel
10227                 },
10228                 {
10229                     cls : "",
10230                     cn: [
10231                         inputblock
10232                     ]
10233                 }
10234
10235             ];
10236             
10237             if(this.labelWidth > 12){
10238                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10239             }
10240
10241             if(this.labelWidth < 13 && this.labelmd == 0){
10242                 this.labelmd = this.labelWidth;
10243             }
10244
10245             if(this.labellg > 0){
10246                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10247                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10248             }
10249
10250             if(this.labelmd > 0){
10251                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10252                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10253             }
10254
10255             if(this.labelsm > 0){
10256                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10257                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10258             }
10259
10260             if(this.labelxs > 0){
10261                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10262                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10263             }
10264             
10265         } else if ( this.fieldLabel.length) {
10266             cfg.cn = [
10267
10268                {
10269                    tag: 'label',
10270                    //cls : 'input-group-addon',
10271                    html : this.fieldLabel
10272
10273                },
10274
10275                inputblock
10276
10277            ];
10278
10279         } else {
10280
10281             cfg.cn = [
10282
10283                 inputblock
10284
10285             ];
10286                 
10287         }
10288         
10289         if (this.disabled) {
10290             input.disabled=true;
10291         }
10292         
10293         return cfg;
10294         
10295     },
10296     /**
10297      * return the real textarea element.
10298      */
10299     inputEl: function ()
10300     {
10301         return this.el.select('textarea.form-control',true).first();
10302     },
10303     
10304     /**
10305      * Clear any invalid styles/messages for this field
10306      */
10307     clearInvalid : function()
10308     {
10309         
10310         if(!this.el || this.preventMark){ // not rendered
10311             return;
10312         }
10313         
10314         var label = this.el.select('label', true).first();
10315         var icon = this.el.select('i.fa-star', true).first();
10316         
10317         if(label && icon){
10318             icon.remove();
10319         }
10320         this.el.removeClass( this.validClass);
10321         this.inputEl().removeClass('is-invalid');
10322          
10323         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10324             
10325             var feedback = this.el.select('.form-control-feedback', true).first();
10326             
10327             if(feedback){
10328                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10329             }
10330             
10331         }
10332         
10333         this.fireEvent('valid', this);
10334     },
10335     
10336      /**
10337      * Mark this field as valid
10338      */
10339     markValid : function()
10340     {
10341         if(!this.el  || this.preventMark){ // not rendered
10342             return;
10343         }
10344         
10345         this.el.removeClass([this.invalidClass, this.validClass]);
10346         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10347         
10348         var feedback = this.el.select('.form-control-feedback', true).first();
10349             
10350         if(feedback){
10351             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10352         }
10353
10354         if(this.disabled || this.allowBlank){
10355             return;
10356         }
10357         
10358         var label = this.el.select('label', true).first();
10359         var icon = this.el.select('i.fa-star', true).first();
10360         
10361         if(label && icon){
10362             icon.remove();
10363         }
10364         if (Roo.bootstrap.version == 3) {
10365             this.el.addClass(this.validClass);
10366         } else {
10367             this.inputEl().addClass('is-valid');
10368         }
10369         
10370         
10371         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10372             
10373             var feedback = this.el.select('.form-control-feedback', true).first();
10374             
10375             if(feedback){
10376                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10377                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10378             }
10379             
10380         }
10381         
10382         this.fireEvent('valid', this);
10383     },
10384     
10385      /**
10386      * Mark this field as invalid
10387      * @param {String} msg The validation message
10388      */
10389     markInvalid : function(msg)
10390     {
10391         if(!this.el  || this.preventMark){ // not rendered
10392             return;
10393         }
10394         
10395         this.el.removeClass([this.invalidClass, this.validClass]);
10396         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10397         
10398         var feedback = this.el.select('.form-control-feedback', true).first();
10399             
10400         if(feedback){
10401             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10402         }
10403
10404         if(this.disabled || this.allowBlank){
10405             return;
10406         }
10407         
10408         var label = this.el.select('label', true).first();
10409         var icon = this.el.select('i.fa-star', true).first();
10410         
10411         if(!this.getValue().length && label && !icon){
10412             this.el.createChild({
10413                 tag : 'i',
10414                 cls : 'text-danger fa fa-lg fa-star',
10415                 tooltip : 'This field is required',
10416                 style : 'margin-right:5px;'
10417             }, label, true);
10418         }
10419         
10420         if (Roo.bootstrap.version == 3) {
10421             this.el.addClass(this.invalidClass);
10422         } else {
10423             this.inputEl().addClass('is-invalid');
10424         }
10425         
10426         // fixme ... this may be depricated need to test..
10427         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10428             
10429             var feedback = this.el.select('.form-control-feedback', true).first();
10430             
10431             if(feedback){
10432                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10433                 
10434                 if(this.getValue().length || this.forceFeedback){
10435                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10436                 }
10437                 
10438             }
10439             
10440         }
10441         
10442         this.fireEvent('invalid', this, msg);
10443     }
10444 });
10445
10446  
10447 /*
10448  * - LGPL
10449  *
10450  * trigger field - base class for combo..
10451  * 
10452  */
10453  
10454 /**
10455  * @class Roo.bootstrap.TriggerField
10456  * @extends Roo.bootstrap.Input
10457  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10458  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10459  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10460  * for which you can provide a custom implementation.  For example:
10461  * <pre><code>
10462 var trigger = new Roo.bootstrap.TriggerField();
10463 trigger.onTriggerClick = myTriggerFn;
10464 trigger.applyTo('my-field');
10465 </code></pre>
10466  *
10467  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10468  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10469  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10470  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10471  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10472
10473  * @constructor
10474  * Create a new TriggerField.
10475  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10476  * to the base TextField)
10477  */
10478 Roo.bootstrap.TriggerField = function(config){
10479     this.mimicing = false;
10480     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10481 };
10482
10483 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10484     /**
10485      * @cfg {String} triggerClass A CSS class to apply to the trigger
10486      */
10487      /**
10488      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10489      */
10490     hideTrigger:false,
10491
10492     /**
10493      * @cfg {Boolean} removable (true|false) special filter default false
10494      */
10495     removable : false,
10496     
10497     /** @cfg {Boolean} grow @hide */
10498     /** @cfg {Number} growMin @hide */
10499     /** @cfg {Number} growMax @hide */
10500
10501     /**
10502      * @hide 
10503      * @method
10504      */
10505     autoSize: Roo.emptyFn,
10506     // private
10507     monitorTab : true,
10508     // private
10509     deferHeight : true,
10510
10511     
10512     actionMode : 'wrap',
10513     
10514     caret : false,
10515     
10516     
10517     getAutoCreate : function(){
10518        
10519         var align = this.labelAlign || this.parentLabelAlign();
10520         
10521         var id = Roo.id();
10522         
10523         var cfg = {
10524             cls: 'form-group' //input-group
10525         };
10526         
10527         
10528         var input =  {
10529             tag: 'input',
10530             id : id,
10531             type : this.inputType,
10532             cls : 'form-control',
10533             autocomplete: 'new-password',
10534             placeholder : this.placeholder || '' 
10535             
10536         };
10537         if (this.name) {
10538             input.name = this.name;
10539         }
10540         if (this.size) {
10541             input.cls += ' input-' + this.size;
10542         }
10543         
10544         if (this.disabled) {
10545             input.disabled=true;
10546         }
10547         
10548         var inputblock = input;
10549         
10550         if(this.hasFeedback && !this.allowBlank){
10551             
10552             var feedback = {
10553                 tag: 'span',
10554                 cls: 'glyphicon form-control-feedback'
10555             };
10556             
10557             if(this.removable && !this.editable && !this.tickable){
10558                 inputblock = {
10559                     cls : 'has-feedback',
10560                     cn :  [
10561                         inputblock,
10562                         {
10563                             tag: 'button',
10564                             html : 'x',
10565                             cls : 'roo-combo-removable-btn close'
10566                         },
10567                         feedback
10568                     ] 
10569                 };
10570             } else {
10571                 inputblock = {
10572                     cls : 'has-feedback',
10573                     cn :  [
10574                         inputblock,
10575                         feedback
10576                     ] 
10577                 };
10578             }
10579
10580         } else {
10581             if(this.removable && !this.editable && !this.tickable){
10582                 inputblock = {
10583                     cls : 'roo-removable',
10584                     cn :  [
10585                         inputblock,
10586                         {
10587                             tag: 'button',
10588                             html : 'x',
10589                             cls : 'roo-combo-removable-btn close'
10590                         }
10591                     ] 
10592                 };
10593             }
10594         }
10595         
10596         if (this.before || this.after) {
10597             
10598             inputblock = {
10599                 cls : 'input-group',
10600                 cn :  [] 
10601             };
10602             if (this.before) {
10603                 inputblock.cn.push({
10604                     tag :'span',
10605                     cls : 'input-group-addon input-group-prepend input-group-text',
10606                     html : this.before
10607                 });
10608             }
10609             
10610             inputblock.cn.push(input);
10611             
10612             if(this.hasFeedback && !this.allowBlank){
10613                 inputblock.cls += ' has-feedback';
10614                 inputblock.cn.push(feedback);
10615             }
10616             
10617             if (this.after) {
10618                 inputblock.cn.push({
10619                     tag :'span',
10620                     cls : 'input-group-addon input-group-append input-group-text',
10621                     html : this.after
10622                 });
10623             }
10624             
10625         };
10626         
10627       
10628         
10629         var ibwrap = inputblock;
10630         
10631         if(this.multiple){
10632             ibwrap = {
10633                 tag: 'ul',
10634                 cls: 'roo-select2-choices',
10635                 cn:[
10636                     {
10637                         tag: 'li',
10638                         cls: 'roo-select2-search-field',
10639                         cn: [
10640
10641                             inputblock
10642                         ]
10643                     }
10644                 ]
10645             };
10646                 
10647         }
10648         
10649         var combobox = {
10650             cls: 'roo-select2-container input-group',
10651             cn: [
10652                  {
10653                     tag: 'input',
10654                     type : 'hidden',
10655                     cls: 'form-hidden-field'
10656                 },
10657                 ibwrap
10658             ]
10659         };
10660         
10661         if(!this.multiple && this.showToggleBtn){
10662             
10663             var caret = {
10664                         tag: 'span',
10665                         cls: 'caret'
10666              };
10667             if (this.caret != false) {
10668                 caret = {
10669                      tag: 'i',
10670                      cls: 'fa fa-' + this.caret
10671                 };
10672                 
10673             }
10674             
10675             combobox.cn.push({
10676                 tag :'span',
10677                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10678                 cn : [
10679                     Roo.bootstrap.version == 3 ? caret : '',
10680                     {
10681                         tag: 'span',
10682                         cls: 'combobox-clear',
10683                         cn  : [
10684                             {
10685                                 tag : 'i',
10686                                 cls: 'icon-remove'
10687                             }
10688                         ]
10689                     }
10690                 ]
10691
10692             })
10693         }
10694         
10695         if(this.multiple){
10696             combobox.cls += ' roo-select2-container-multi';
10697         }
10698          var indicator = {
10699             tag : 'i',
10700             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10701             tooltip : 'This field is required'
10702         };
10703         if (Roo.bootstrap.version == 4) {
10704             indicator = {
10705                 tag : 'i',
10706                 style : 'display:none'
10707             };
10708         }
10709         
10710         
10711         if (align ==='left' && this.fieldLabel.length) {
10712             
10713             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10714
10715             cfg.cn = [
10716                 indicator,
10717                 {
10718                     tag: 'label',
10719                     'for' :  id,
10720                     cls : 'control-label',
10721                     html : this.fieldLabel
10722
10723                 },
10724                 {
10725                     cls : "", 
10726                     cn: [
10727                         combobox
10728                     ]
10729                 }
10730
10731             ];
10732             
10733             var labelCfg = cfg.cn[1];
10734             var contentCfg = cfg.cn[2];
10735             
10736             if(this.indicatorpos == 'right'){
10737                 cfg.cn = [
10738                     {
10739                         tag: 'label',
10740                         'for' :  id,
10741                         cls : 'control-label',
10742                         cn : [
10743                             {
10744                                 tag : 'span',
10745                                 html : this.fieldLabel
10746                             },
10747                             indicator
10748                         ]
10749                     },
10750                     {
10751                         cls : "", 
10752                         cn: [
10753                             combobox
10754                         ]
10755                     }
10756
10757                 ];
10758                 
10759                 labelCfg = cfg.cn[0];
10760                 contentCfg = cfg.cn[1];
10761             }
10762             
10763             if(this.labelWidth > 12){
10764                 labelCfg.style = "width: " + this.labelWidth + 'px';
10765             }
10766             
10767             if(this.labelWidth < 13 && this.labelmd == 0){
10768                 this.labelmd = this.labelWidth;
10769             }
10770             
10771             if(this.labellg > 0){
10772                 labelCfg.cls += ' col-lg-' + this.labellg;
10773                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10774             }
10775             
10776             if(this.labelmd > 0){
10777                 labelCfg.cls += ' col-md-' + this.labelmd;
10778                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10779             }
10780             
10781             if(this.labelsm > 0){
10782                 labelCfg.cls += ' col-sm-' + this.labelsm;
10783                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10784             }
10785             
10786             if(this.labelxs > 0){
10787                 labelCfg.cls += ' col-xs-' + this.labelxs;
10788                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10789             }
10790             
10791         } else if ( this.fieldLabel.length) {
10792 //                Roo.log(" label");
10793             cfg.cn = [
10794                 indicator,
10795                {
10796                    tag: 'label',
10797                    //cls : 'input-group-addon',
10798                    html : this.fieldLabel
10799
10800                },
10801
10802                combobox
10803
10804             ];
10805             
10806             if(this.indicatorpos == 'right'){
10807                 
10808                 cfg.cn = [
10809                     {
10810                        tag: 'label',
10811                        cn : [
10812                            {
10813                                tag : 'span',
10814                                html : this.fieldLabel
10815                            },
10816                            indicator
10817                        ]
10818
10819                     },
10820                     combobox
10821
10822                 ];
10823
10824             }
10825
10826         } else {
10827             
10828 //                Roo.log(" no label && no align");
10829                 cfg = combobox
10830                      
10831                 
10832         }
10833         
10834         var settings=this;
10835         ['xs','sm','md','lg'].map(function(size){
10836             if (settings[size]) {
10837                 cfg.cls += ' col-' + size + '-' + settings[size];
10838             }
10839         });
10840         
10841         return cfg;
10842         
10843     },
10844     
10845     
10846     
10847     // private
10848     onResize : function(w, h){
10849 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10850 //        if(typeof w == 'number'){
10851 //            var x = w - this.trigger.getWidth();
10852 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10853 //            this.trigger.setStyle('left', x+'px');
10854 //        }
10855     },
10856
10857     // private
10858     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10859
10860     // private
10861     getResizeEl : function(){
10862         return this.inputEl();
10863     },
10864
10865     // private
10866     getPositionEl : function(){
10867         return this.inputEl();
10868     },
10869
10870     // private
10871     alignErrorIcon : function(){
10872         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10873     },
10874
10875     // private
10876     initEvents : function(){
10877         
10878         this.createList();
10879         
10880         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10881         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10882         if(!this.multiple && this.showToggleBtn){
10883             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10884             if(this.hideTrigger){
10885                 this.trigger.setDisplayed(false);
10886             }
10887             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10888         }
10889         
10890         if(this.multiple){
10891             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10892         }
10893         
10894         if(this.removable && !this.editable && !this.tickable){
10895             var close = this.closeTriggerEl();
10896             
10897             if(close){
10898                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10899                 close.on('click', this.removeBtnClick, this, close);
10900             }
10901         }
10902         
10903         //this.trigger.addClassOnOver('x-form-trigger-over');
10904         //this.trigger.addClassOnClick('x-form-trigger-click');
10905         
10906         //if(!this.width){
10907         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10908         //}
10909     },
10910     
10911     closeTriggerEl : function()
10912     {
10913         var close = this.el.select('.roo-combo-removable-btn', true).first();
10914         return close ? close : false;
10915     },
10916     
10917     removeBtnClick : function(e, h, el)
10918     {
10919         e.preventDefault();
10920         
10921         if(this.fireEvent("remove", this) !== false){
10922             this.reset();
10923             this.fireEvent("afterremove", this)
10924         }
10925     },
10926     
10927     createList : function()
10928     {
10929         this.list = Roo.get(document.body).createChild({
10930             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10931             cls: 'typeahead typeahead-long dropdown-menu',
10932             style: 'display:none'
10933         });
10934         
10935         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10936         
10937     },
10938
10939     // private
10940     initTrigger : function(){
10941        
10942     },
10943
10944     // private
10945     onDestroy : function(){
10946         if(this.trigger){
10947             this.trigger.removeAllListeners();
10948           //  this.trigger.remove();
10949         }
10950         //if(this.wrap){
10951         //    this.wrap.remove();
10952         //}
10953         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10954     },
10955
10956     // private
10957     onFocus : function(){
10958         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10959         /*
10960         if(!this.mimicing){
10961             this.wrap.addClass('x-trigger-wrap-focus');
10962             this.mimicing = true;
10963             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10964             if(this.monitorTab){
10965                 this.el.on("keydown", this.checkTab, this);
10966             }
10967         }
10968         */
10969     },
10970
10971     // private
10972     checkTab : function(e){
10973         if(e.getKey() == e.TAB){
10974             this.triggerBlur();
10975         }
10976     },
10977
10978     // private
10979     onBlur : function(){
10980         // do nothing
10981     },
10982
10983     // private
10984     mimicBlur : function(e, t){
10985         /*
10986         if(!this.wrap.contains(t) && this.validateBlur()){
10987             this.triggerBlur();
10988         }
10989         */
10990     },
10991
10992     // private
10993     triggerBlur : function(){
10994         this.mimicing = false;
10995         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10996         if(this.monitorTab){
10997             this.el.un("keydown", this.checkTab, this);
10998         }
10999         //this.wrap.removeClass('x-trigger-wrap-focus');
11000         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11001     },
11002
11003     // private
11004     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11005     validateBlur : function(e, t){
11006         return true;
11007     },
11008
11009     // private
11010     onDisable : function(){
11011         this.inputEl().dom.disabled = true;
11012         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11013         //if(this.wrap){
11014         //    this.wrap.addClass('x-item-disabled');
11015         //}
11016     },
11017
11018     // private
11019     onEnable : function(){
11020         this.inputEl().dom.disabled = false;
11021         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11022         //if(this.wrap){
11023         //    this.el.removeClass('x-item-disabled');
11024         //}
11025     },
11026
11027     // private
11028     onShow : function(){
11029         var ae = this.getActionEl();
11030         
11031         if(ae){
11032             ae.dom.style.display = '';
11033             ae.dom.style.visibility = 'visible';
11034         }
11035     },
11036
11037     // private
11038     
11039     onHide : function(){
11040         var ae = this.getActionEl();
11041         ae.dom.style.display = 'none';
11042     },
11043
11044     /**
11045      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11046      * by an implementing function.
11047      * @method
11048      * @param {EventObject} e
11049      */
11050     onTriggerClick : Roo.emptyFn
11051 });
11052  /*
11053  * Based on:
11054  * Ext JS Library 1.1.1
11055  * Copyright(c) 2006-2007, Ext JS, LLC.
11056  *
11057  * Originally Released Under LGPL - original licence link has changed is not relivant.
11058  *
11059  * Fork - LGPL
11060  * <script type="text/javascript">
11061  */
11062
11063
11064 /**
11065  * @class Roo.data.SortTypes
11066  * @singleton
11067  * Defines the default sorting (casting?) comparison functions used when sorting data.
11068  */
11069 Roo.data.SortTypes = {
11070     /**
11071      * Default sort that does nothing
11072      * @param {Mixed} s The value being converted
11073      * @return {Mixed} The comparison value
11074      */
11075     none : function(s){
11076         return s;
11077     },
11078     
11079     /**
11080      * The regular expression used to strip tags
11081      * @type {RegExp}
11082      * @property
11083      */
11084     stripTagsRE : /<\/?[^>]+>/gi,
11085     
11086     /**
11087      * Strips all HTML tags to sort on text only
11088      * @param {Mixed} s The value being converted
11089      * @return {String} The comparison value
11090      */
11091     asText : function(s){
11092         return String(s).replace(this.stripTagsRE, "");
11093     },
11094     
11095     /**
11096      * Strips all HTML tags to sort on text only - Case insensitive
11097      * @param {Mixed} s The value being converted
11098      * @return {String} The comparison value
11099      */
11100     asUCText : function(s){
11101         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11102     },
11103     
11104     /**
11105      * Case insensitive string
11106      * @param {Mixed} s The value being converted
11107      * @return {String} The comparison value
11108      */
11109     asUCString : function(s) {
11110         return String(s).toUpperCase();
11111     },
11112     
11113     /**
11114      * Date sorting
11115      * @param {Mixed} s The value being converted
11116      * @return {Number} The comparison value
11117      */
11118     asDate : function(s) {
11119         if(!s){
11120             return 0;
11121         }
11122         if(s instanceof Date){
11123             return s.getTime();
11124         }
11125         return Date.parse(String(s));
11126     },
11127     
11128     /**
11129      * Float sorting
11130      * @param {Mixed} s The value being converted
11131      * @return {Float} The comparison value
11132      */
11133     asFloat : function(s) {
11134         var val = parseFloat(String(s).replace(/,/g, ""));
11135         if(isNaN(val)) {
11136             val = 0;
11137         }
11138         return val;
11139     },
11140     
11141     /**
11142      * Integer sorting
11143      * @param {Mixed} s The value being converted
11144      * @return {Number} The comparison value
11145      */
11146     asInt : function(s) {
11147         var val = parseInt(String(s).replace(/,/g, ""));
11148         if(isNaN(val)) {
11149             val = 0;
11150         }
11151         return val;
11152     }
11153 };/*
11154  * Based on:
11155  * Ext JS Library 1.1.1
11156  * Copyright(c) 2006-2007, Ext JS, LLC.
11157  *
11158  * Originally Released Under LGPL - original licence link has changed is not relivant.
11159  *
11160  * Fork - LGPL
11161  * <script type="text/javascript">
11162  */
11163
11164 /**
11165 * @class Roo.data.Record
11166  * Instances of this class encapsulate both record <em>definition</em> information, and record
11167  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11168  * to access Records cached in an {@link Roo.data.Store} object.<br>
11169  * <p>
11170  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11171  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11172  * objects.<br>
11173  * <p>
11174  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11175  * @constructor
11176  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11177  * {@link #create}. The parameters are the same.
11178  * @param {Array} data An associative Array of data values keyed by the field name.
11179  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11180  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11181  * not specified an integer id is generated.
11182  */
11183 Roo.data.Record = function(data, id){
11184     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11185     this.data = data;
11186 };
11187
11188 /**
11189  * Generate a constructor for a specific record layout.
11190  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11191  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11192  * Each field definition object may contain the following properties: <ul>
11193  * <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,
11194  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11195  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11196  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11197  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11198  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11199  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11200  * this may be omitted.</p></li>
11201  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11202  * <ul><li>auto (Default, implies no conversion)</li>
11203  * <li>string</li>
11204  * <li>int</li>
11205  * <li>float</li>
11206  * <li>boolean</li>
11207  * <li>date</li></ul></p></li>
11208  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11209  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11210  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11211  * by the Reader into an object that will be stored in the Record. It is passed the
11212  * following parameters:<ul>
11213  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11214  * </ul></p></li>
11215  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11216  * </ul>
11217  * <br>usage:<br><pre><code>
11218 var TopicRecord = Roo.data.Record.create(
11219     {name: 'title', mapping: 'topic_title'},
11220     {name: 'author', mapping: 'username'},
11221     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11222     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11223     {name: 'lastPoster', mapping: 'user2'},
11224     {name: 'excerpt', mapping: 'post_text'}
11225 );
11226
11227 var myNewRecord = new TopicRecord({
11228     title: 'Do my job please',
11229     author: 'noobie',
11230     totalPosts: 1,
11231     lastPost: new Date(),
11232     lastPoster: 'Animal',
11233     excerpt: 'No way dude!'
11234 });
11235 myStore.add(myNewRecord);
11236 </code></pre>
11237  * @method create
11238  * @static
11239  */
11240 Roo.data.Record.create = function(o){
11241     var f = function(){
11242         f.superclass.constructor.apply(this, arguments);
11243     };
11244     Roo.extend(f, Roo.data.Record);
11245     var p = f.prototype;
11246     p.fields = new Roo.util.MixedCollection(false, function(field){
11247         return field.name;
11248     });
11249     for(var i = 0, len = o.length; i < len; i++){
11250         p.fields.add(new Roo.data.Field(o[i]));
11251     }
11252     f.getField = function(name){
11253         return p.fields.get(name);  
11254     };
11255     return f;
11256 };
11257
11258 Roo.data.Record.AUTO_ID = 1000;
11259 Roo.data.Record.EDIT = 'edit';
11260 Roo.data.Record.REJECT = 'reject';
11261 Roo.data.Record.COMMIT = 'commit';
11262
11263 Roo.data.Record.prototype = {
11264     /**
11265      * Readonly flag - true if this record has been modified.
11266      * @type Boolean
11267      */
11268     dirty : false,
11269     editing : false,
11270     error: null,
11271     modified: null,
11272
11273     // private
11274     join : function(store){
11275         this.store = store;
11276     },
11277
11278     /**
11279      * Set the named field to the specified value.
11280      * @param {String} name The name of the field to set.
11281      * @param {Object} value The value to set the field to.
11282      */
11283     set : function(name, value){
11284         if(this.data[name] == value){
11285             return;
11286         }
11287         this.dirty = true;
11288         if(!this.modified){
11289             this.modified = {};
11290         }
11291         if(typeof this.modified[name] == 'undefined'){
11292             this.modified[name] = this.data[name];
11293         }
11294         this.data[name] = value;
11295         if(!this.editing && this.store){
11296             this.store.afterEdit(this);
11297         }       
11298     },
11299
11300     /**
11301      * Get the value of the named field.
11302      * @param {String} name The name of the field to get the value of.
11303      * @return {Object} The value of the field.
11304      */
11305     get : function(name){
11306         return this.data[name]; 
11307     },
11308
11309     // private
11310     beginEdit : function(){
11311         this.editing = true;
11312         this.modified = {}; 
11313     },
11314
11315     // private
11316     cancelEdit : function(){
11317         this.editing = false;
11318         delete this.modified;
11319     },
11320
11321     // private
11322     endEdit : function(){
11323         this.editing = false;
11324         if(this.dirty && this.store){
11325             this.store.afterEdit(this);
11326         }
11327     },
11328
11329     /**
11330      * Usually called by the {@link Roo.data.Store} which owns the Record.
11331      * Rejects all changes made to the Record since either creation, or the last commit operation.
11332      * Modified fields are reverted to their original values.
11333      * <p>
11334      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11335      * of reject operations.
11336      */
11337     reject : function(){
11338         var m = this.modified;
11339         for(var n in m){
11340             if(typeof m[n] != "function"){
11341                 this.data[n] = m[n];
11342             }
11343         }
11344         this.dirty = false;
11345         delete this.modified;
11346         this.editing = false;
11347         if(this.store){
11348             this.store.afterReject(this);
11349         }
11350     },
11351
11352     /**
11353      * Usually called by the {@link Roo.data.Store} which owns the Record.
11354      * Commits all changes made to the Record since either creation, or the last commit operation.
11355      * <p>
11356      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11357      * of commit operations.
11358      */
11359     commit : function(){
11360         this.dirty = false;
11361         delete this.modified;
11362         this.editing = false;
11363         if(this.store){
11364             this.store.afterCommit(this);
11365         }
11366     },
11367
11368     // private
11369     hasError : function(){
11370         return this.error != null;
11371     },
11372
11373     // private
11374     clearError : function(){
11375         this.error = null;
11376     },
11377
11378     /**
11379      * Creates a copy of this record.
11380      * @param {String} id (optional) A new record id if you don't want to use this record's id
11381      * @return {Record}
11382      */
11383     copy : function(newId) {
11384         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11385     }
11386 };/*
11387  * Based on:
11388  * Ext JS Library 1.1.1
11389  * Copyright(c) 2006-2007, Ext JS, LLC.
11390  *
11391  * Originally Released Under LGPL - original licence link has changed is not relivant.
11392  *
11393  * Fork - LGPL
11394  * <script type="text/javascript">
11395  */
11396
11397
11398
11399 /**
11400  * @class Roo.data.Store
11401  * @extends Roo.util.Observable
11402  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11403  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11404  * <p>
11405  * 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
11406  * has no knowledge of the format of the data returned by the Proxy.<br>
11407  * <p>
11408  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11409  * instances from the data object. These records are cached and made available through accessor functions.
11410  * @constructor
11411  * Creates a new Store.
11412  * @param {Object} config A config object containing the objects needed for the Store to access data,
11413  * and read the data into Records.
11414  */
11415 Roo.data.Store = function(config){
11416     this.data = new Roo.util.MixedCollection(false);
11417     this.data.getKey = function(o){
11418         return o.id;
11419     };
11420     this.baseParams = {};
11421     // private
11422     this.paramNames = {
11423         "start" : "start",
11424         "limit" : "limit",
11425         "sort" : "sort",
11426         "dir" : "dir",
11427         "multisort" : "_multisort"
11428     };
11429
11430     if(config && config.data){
11431         this.inlineData = config.data;
11432         delete config.data;
11433     }
11434
11435     Roo.apply(this, config);
11436     
11437     if(this.reader){ // reader passed
11438         this.reader = Roo.factory(this.reader, Roo.data);
11439         this.reader.xmodule = this.xmodule || false;
11440         if(!this.recordType){
11441             this.recordType = this.reader.recordType;
11442         }
11443         if(this.reader.onMetaChange){
11444             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11445         }
11446     }
11447
11448     if(this.recordType){
11449         this.fields = this.recordType.prototype.fields;
11450     }
11451     this.modified = [];
11452
11453     this.addEvents({
11454         /**
11455          * @event datachanged
11456          * Fires when the data cache has changed, and a widget which is using this Store
11457          * as a Record cache should refresh its view.
11458          * @param {Store} this
11459          */
11460         datachanged : true,
11461         /**
11462          * @event metachange
11463          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11464          * @param {Store} this
11465          * @param {Object} meta The JSON metadata
11466          */
11467         metachange : true,
11468         /**
11469          * @event add
11470          * Fires when Records have been added to the Store
11471          * @param {Store} this
11472          * @param {Roo.data.Record[]} records The array of Records added
11473          * @param {Number} index The index at which the record(s) were added
11474          */
11475         add : true,
11476         /**
11477          * @event remove
11478          * Fires when a Record has been removed from the Store
11479          * @param {Store} this
11480          * @param {Roo.data.Record} record The Record that was removed
11481          * @param {Number} index The index at which the record was removed
11482          */
11483         remove : true,
11484         /**
11485          * @event update
11486          * Fires when a Record has been updated
11487          * @param {Store} this
11488          * @param {Roo.data.Record} record The Record that was updated
11489          * @param {String} operation The update operation being performed.  Value may be one of:
11490          * <pre><code>
11491  Roo.data.Record.EDIT
11492  Roo.data.Record.REJECT
11493  Roo.data.Record.COMMIT
11494          * </code></pre>
11495          */
11496         update : true,
11497         /**
11498          * @event clear
11499          * Fires when the data cache has been cleared.
11500          * @param {Store} this
11501          */
11502         clear : true,
11503         /**
11504          * @event beforeload
11505          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11506          * the load action will be canceled.
11507          * @param {Store} this
11508          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11509          */
11510         beforeload : true,
11511         /**
11512          * @event beforeloadadd
11513          * Fires after a new set of Records has been loaded.
11514          * @param {Store} this
11515          * @param {Roo.data.Record[]} records The Records that were loaded
11516          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11517          */
11518         beforeloadadd : true,
11519         /**
11520          * @event load
11521          * Fires after a new set of Records has been loaded, before they are added to the store.
11522          * @param {Store} this
11523          * @param {Roo.data.Record[]} records The Records that were loaded
11524          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11525          * @params {Object} return from reader
11526          */
11527         load : true,
11528         /**
11529          * @event loadexception
11530          * Fires if an exception occurs in the Proxy during loading.
11531          * Called with the signature of the Proxy's "loadexception" event.
11532          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11533          * 
11534          * @param {Proxy} 
11535          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11536          * @param {Object} load options 
11537          * @param {Object} jsonData from your request (normally this contains the Exception)
11538          */
11539         loadexception : true
11540     });
11541     
11542     if(this.proxy){
11543         this.proxy = Roo.factory(this.proxy, Roo.data);
11544         this.proxy.xmodule = this.xmodule || false;
11545         this.relayEvents(this.proxy,  ["loadexception"]);
11546     }
11547     this.sortToggle = {};
11548     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11549
11550     Roo.data.Store.superclass.constructor.call(this);
11551
11552     if(this.inlineData){
11553         this.loadData(this.inlineData);
11554         delete this.inlineData;
11555     }
11556 };
11557
11558 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11559      /**
11560     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11561     * without a remote query - used by combo/forms at present.
11562     */
11563     
11564     /**
11565     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11566     */
11567     /**
11568     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11569     */
11570     /**
11571     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11572     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11573     */
11574     /**
11575     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11576     * on any HTTP request
11577     */
11578     /**
11579     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11580     */
11581     /**
11582     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11583     */
11584     multiSort: false,
11585     /**
11586     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11587     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11588     */
11589     remoteSort : false,
11590
11591     /**
11592     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11593      * loaded or when a record is removed. (defaults to false).
11594     */
11595     pruneModifiedRecords : false,
11596
11597     // private
11598     lastOptions : null,
11599
11600     /**
11601      * Add Records to the Store and fires the add event.
11602      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11603      */
11604     add : function(records){
11605         records = [].concat(records);
11606         for(var i = 0, len = records.length; i < len; i++){
11607             records[i].join(this);
11608         }
11609         var index = this.data.length;
11610         this.data.addAll(records);
11611         this.fireEvent("add", this, records, index);
11612     },
11613
11614     /**
11615      * Remove a Record from the Store and fires the remove event.
11616      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11617      */
11618     remove : function(record){
11619         var index = this.data.indexOf(record);
11620         this.data.removeAt(index);
11621  
11622         if(this.pruneModifiedRecords){
11623             this.modified.remove(record);
11624         }
11625         this.fireEvent("remove", this, record, index);
11626     },
11627
11628     /**
11629      * Remove all Records from the Store and fires the clear event.
11630      */
11631     removeAll : function(){
11632         this.data.clear();
11633         if(this.pruneModifiedRecords){
11634             this.modified = [];
11635         }
11636         this.fireEvent("clear", this);
11637     },
11638
11639     /**
11640      * Inserts Records to the Store at the given index and fires the add event.
11641      * @param {Number} index The start index at which to insert the passed Records.
11642      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11643      */
11644     insert : function(index, records){
11645         records = [].concat(records);
11646         for(var i = 0, len = records.length; i < len; i++){
11647             this.data.insert(index, records[i]);
11648             records[i].join(this);
11649         }
11650         this.fireEvent("add", this, records, index);
11651     },
11652
11653     /**
11654      * Get the index within the cache of the passed Record.
11655      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11656      * @return {Number} The index of the passed Record. Returns -1 if not found.
11657      */
11658     indexOf : function(record){
11659         return this.data.indexOf(record);
11660     },
11661
11662     /**
11663      * Get the index within the cache of the Record with the passed id.
11664      * @param {String} id The id of the Record to find.
11665      * @return {Number} The index of the Record. Returns -1 if not found.
11666      */
11667     indexOfId : function(id){
11668         return this.data.indexOfKey(id);
11669     },
11670
11671     /**
11672      * Get the Record with the specified id.
11673      * @param {String} id The id of the Record to find.
11674      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11675      */
11676     getById : function(id){
11677         return this.data.key(id);
11678     },
11679
11680     /**
11681      * Get the Record at the specified index.
11682      * @param {Number} index The index of the Record to find.
11683      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11684      */
11685     getAt : function(index){
11686         return this.data.itemAt(index);
11687     },
11688
11689     /**
11690      * Returns a range of Records between specified indices.
11691      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11692      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11693      * @return {Roo.data.Record[]} An array of Records
11694      */
11695     getRange : function(start, end){
11696         return this.data.getRange(start, end);
11697     },
11698
11699     // private
11700     storeOptions : function(o){
11701         o = Roo.apply({}, o);
11702         delete o.callback;
11703         delete o.scope;
11704         this.lastOptions = o;
11705     },
11706
11707     /**
11708      * Loads the Record cache from the configured Proxy using the configured Reader.
11709      * <p>
11710      * If using remote paging, then the first load call must specify the <em>start</em>
11711      * and <em>limit</em> properties in the options.params property to establish the initial
11712      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11713      * <p>
11714      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11715      * and this call will return before the new data has been loaded. Perform any post-processing
11716      * in a callback function, or in a "load" event handler.</strong>
11717      * <p>
11718      * @param {Object} options An object containing properties which control loading options:<ul>
11719      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11720      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11721      * passed the following arguments:<ul>
11722      * <li>r : Roo.data.Record[]</li>
11723      * <li>options: Options object from the load call</li>
11724      * <li>success: Boolean success indicator</li></ul></li>
11725      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11726      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11727      * </ul>
11728      */
11729     load : function(options){
11730         options = options || {};
11731         if(this.fireEvent("beforeload", this, options) !== false){
11732             this.storeOptions(options);
11733             var p = Roo.apply(options.params || {}, this.baseParams);
11734             // if meta was not loaded from remote source.. try requesting it.
11735             if (!this.reader.metaFromRemote) {
11736                 p._requestMeta = 1;
11737             }
11738             if(this.sortInfo && this.remoteSort){
11739                 var pn = this.paramNames;
11740                 p[pn["sort"]] = this.sortInfo.field;
11741                 p[pn["dir"]] = this.sortInfo.direction;
11742             }
11743             if (this.multiSort) {
11744                 var pn = this.paramNames;
11745                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11746             }
11747             
11748             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11749         }
11750     },
11751
11752     /**
11753      * Reloads the Record cache from the configured Proxy using the configured Reader and
11754      * the options from the last load operation performed.
11755      * @param {Object} options (optional) An object containing properties which may override the options
11756      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11757      * the most recently used options are reused).
11758      */
11759     reload : function(options){
11760         this.load(Roo.applyIf(options||{}, this.lastOptions));
11761     },
11762
11763     // private
11764     // Called as a callback by the Reader during a load operation.
11765     loadRecords : function(o, options, success){
11766         if(!o || success === false){
11767             if(success !== false){
11768                 this.fireEvent("load", this, [], options, o);
11769             }
11770             if(options.callback){
11771                 options.callback.call(options.scope || this, [], options, false);
11772             }
11773             return;
11774         }
11775         // if data returned failure - throw an exception.
11776         if (o.success === false) {
11777             // show a message if no listener is registered.
11778             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11779                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11780             }
11781             // loadmask wil be hooked into this..
11782             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11783             return;
11784         }
11785         var r = o.records, t = o.totalRecords || r.length;
11786         
11787         this.fireEvent("beforeloadadd", this, r, options, o);
11788         
11789         if(!options || options.add !== true){
11790             if(this.pruneModifiedRecords){
11791                 this.modified = [];
11792             }
11793             for(var i = 0, len = r.length; i < len; i++){
11794                 r[i].join(this);
11795             }
11796             if(this.snapshot){
11797                 this.data = this.snapshot;
11798                 delete this.snapshot;
11799             }
11800             this.data.clear();
11801             this.data.addAll(r);
11802             this.totalLength = t;
11803             this.applySort();
11804             this.fireEvent("datachanged", this);
11805         }else{
11806             this.totalLength = Math.max(t, this.data.length+r.length);
11807             this.add(r);
11808         }
11809         
11810         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11811                 
11812             var e = new Roo.data.Record({});
11813
11814             e.set(this.parent.displayField, this.parent.emptyTitle);
11815             e.set(this.parent.valueField, '');
11816
11817             this.insert(0, e);
11818         }
11819             
11820         this.fireEvent("load", this, r, options, o);
11821         if(options.callback){
11822             options.callback.call(options.scope || this, r, options, true);
11823         }
11824     },
11825
11826
11827     /**
11828      * Loads data from a passed data block. A Reader which understands the format of the data
11829      * must have been configured in the constructor.
11830      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11831      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11832      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11833      */
11834     loadData : function(o, append){
11835         var r = this.reader.readRecords(o);
11836         this.loadRecords(r, {add: append}, true);
11837     },
11838
11839     /**
11840      * Gets the number of cached records.
11841      * <p>
11842      * <em>If using paging, this may not be the total size of the dataset. If the data object
11843      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11844      * the data set size</em>
11845      */
11846     getCount : function(){
11847         return this.data.length || 0;
11848     },
11849
11850     /**
11851      * Gets the total number of records in the dataset as returned by the server.
11852      * <p>
11853      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11854      * the dataset size</em>
11855      */
11856     getTotalCount : function(){
11857         return this.totalLength || 0;
11858     },
11859
11860     /**
11861      * Returns the sort state of the Store as an object with two properties:
11862      * <pre><code>
11863  field {String} The name of the field by which the Records are sorted
11864  direction {String} The sort order, "ASC" or "DESC"
11865      * </code></pre>
11866      */
11867     getSortState : function(){
11868         return this.sortInfo;
11869     },
11870
11871     // private
11872     applySort : function(){
11873         if(this.sortInfo && !this.remoteSort){
11874             var s = this.sortInfo, f = s.field;
11875             var st = this.fields.get(f).sortType;
11876             var fn = function(r1, r2){
11877                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11878                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11879             };
11880             this.data.sort(s.direction, fn);
11881             if(this.snapshot && this.snapshot != this.data){
11882                 this.snapshot.sort(s.direction, fn);
11883             }
11884         }
11885     },
11886
11887     /**
11888      * Sets the default sort column and order to be used by the next load operation.
11889      * @param {String} fieldName The name of the field to sort by.
11890      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11891      */
11892     setDefaultSort : function(field, dir){
11893         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11894     },
11895
11896     /**
11897      * Sort the Records.
11898      * If remote sorting is used, the sort is performed on the server, and the cache is
11899      * reloaded. If local sorting is used, the cache is sorted internally.
11900      * @param {String} fieldName The name of the field to sort by.
11901      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11902      */
11903     sort : function(fieldName, dir){
11904         var f = this.fields.get(fieldName);
11905         if(!dir){
11906             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11907             
11908             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11909                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11910             }else{
11911                 dir = f.sortDir;
11912             }
11913         }
11914         this.sortToggle[f.name] = dir;
11915         this.sortInfo = {field: f.name, direction: dir};
11916         if(!this.remoteSort){
11917             this.applySort();
11918             this.fireEvent("datachanged", this);
11919         }else{
11920             this.load(this.lastOptions);
11921         }
11922     },
11923
11924     /**
11925      * Calls the specified function for each of the Records in the cache.
11926      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11927      * Returning <em>false</em> aborts and exits the iteration.
11928      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11929      */
11930     each : function(fn, scope){
11931         this.data.each(fn, scope);
11932     },
11933
11934     /**
11935      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11936      * (e.g., during paging).
11937      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11938      */
11939     getModifiedRecords : function(){
11940         return this.modified;
11941     },
11942
11943     // private
11944     createFilterFn : function(property, value, anyMatch){
11945         if(!value.exec){ // not a regex
11946             value = String(value);
11947             if(value.length == 0){
11948                 return false;
11949             }
11950             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11951         }
11952         return function(r){
11953             return value.test(r.data[property]);
11954         };
11955     },
11956
11957     /**
11958      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11959      * @param {String} property A field on your records
11960      * @param {Number} start The record index to start at (defaults to 0)
11961      * @param {Number} end The last record index to include (defaults to length - 1)
11962      * @return {Number} The sum
11963      */
11964     sum : function(property, start, end){
11965         var rs = this.data.items, v = 0;
11966         start = start || 0;
11967         end = (end || end === 0) ? end : rs.length-1;
11968
11969         for(var i = start; i <= end; i++){
11970             v += (rs[i].data[property] || 0);
11971         }
11972         return v;
11973     },
11974
11975     /**
11976      * Filter the records by a specified property.
11977      * @param {String} field A field on your records
11978      * @param {String/RegExp} value Either a string that the field
11979      * should start with or a RegExp to test against the field
11980      * @param {Boolean} anyMatch True to match any part not just the beginning
11981      */
11982     filter : function(property, value, anyMatch){
11983         var fn = this.createFilterFn(property, value, anyMatch);
11984         return fn ? this.filterBy(fn) : this.clearFilter();
11985     },
11986
11987     /**
11988      * Filter by a function. The specified function will be called with each
11989      * record in this data source. If the function returns true the record is included,
11990      * otherwise it is filtered.
11991      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11992      * @param {Object} scope (optional) The scope of the function (defaults to this)
11993      */
11994     filterBy : function(fn, scope){
11995         this.snapshot = this.snapshot || this.data;
11996         this.data = this.queryBy(fn, scope||this);
11997         this.fireEvent("datachanged", this);
11998     },
11999
12000     /**
12001      * Query the records by a specified property.
12002      * @param {String} field A field on your records
12003      * @param {String/RegExp} value Either a string that the field
12004      * should start with or a RegExp to test against the field
12005      * @param {Boolean} anyMatch True to match any part not just the beginning
12006      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12007      */
12008     query : function(property, value, anyMatch){
12009         var fn = this.createFilterFn(property, value, anyMatch);
12010         return fn ? this.queryBy(fn) : this.data.clone();
12011     },
12012
12013     /**
12014      * Query by a function. The specified function will be called with each
12015      * record in this data source. If the function returns true the record is included
12016      * in the results.
12017      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12018      * @param {Object} scope (optional) The scope of the function (defaults to this)
12019       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12020      **/
12021     queryBy : function(fn, scope){
12022         var data = this.snapshot || this.data;
12023         return data.filterBy(fn, scope||this);
12024     },
12025
12026     /**
12027      * Collects unique values for a particular dataIndex from this store.
12028      * @param {String} dataIndex The property to collect
12029      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12030      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12031      * @return {Array} An array of the unique values
12032      **/
12033     collect : function(dataIndex, allowNull, bypassFilter){
12034         var d = (bypassFilter === true && this.snapshot) ?
12035                 this.snapshot.items : this.data.items;
12036         var v, sv, r = [], l = {};
12037         for(var i = 0, len = d.length; i < len; i++){
12038             v = d[i].data[dataIndex];
12039             sv = String(v);
12040             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12041                 l[sv] = true;
12042                 r[r.length] = v;
12043             }
12044         }
12045         return r;
12046     },
12047
12048     /**
12049      * Revert to a view of the Record cache with no filtering applied.
12050      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12051      */
12052     clearFilter : function(suppressEvent){
12053         if(this.snapshot && this.snapshot != this.data){
12054             this.data = this.snapshot;
12055             delete this.snapshot;
12056             if(suppressEvent !== true){
12057                 this.fireEvent("datachanged", this);
12058             }
12059         }
12060     },
12061
12062     // private
12063     afterEdit : function(record){
12064         if(this.modified.indexOf(record) == -1){
12065             this.modified.push(record);
12066         }
12067         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12068     },
12069     
12070     // private
12071     afterReject : function(record){
12072         this.modified.remove(record);
12073         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12074     },
12075
12076     // private
12077     afterCommit : function(record){
12078         this.modified.remove(record);
12079         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12080     },
12081
12082     /**
12083      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12084      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12085      */
12086     commitChanges : function(){
12087         var m = this.modified.slice(0);
12088         this.modified = [];
12089         for(var i = 0, len = m.length; i < len; i++){
12090             m[i].commit();
12091         }
12092     },
12093
12094     /**
12095      * Cancel outstanding changes on all changed records.
12096      */
12097     rejectChanges : function(){
12098         var m = this.modified.slice(0);
12099         this.modified = [];
12100         for(var i = 0, len = m.length; i < len; i++){
12101             m[i].reject();
12102         }
12103     },
12104
12105     onMetaChange : function(meta, rtype, o){
12106         this.recordType = rtype;
12107         this.fields = rtype.prototype.fields;
12108         delete this.snapshot;
12109         this.sortInfo = meta.sortInfo || this.sortInfo;
12110         this.modified = [];
12111         this.fireEvent('metachange', this, this.reader.meta);
12112     },
12113     
12114     moveIndex : function(data, type)
12115     {
12116         var index = this.indexOf(data);
12117         
12118         var newIndex = index + type;
12119         
12120         this.remove(data);
12121         
12122         this.insert(newIndex, data);
12123         
12124     }
12125 });/*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136 /**
12137  * @class Roo.data.SimpleStore
12138  * @extends Roo.data.Store
12139  * Small helper class to make creating Stores from Array data easier.
12140  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12141  * @cfg {Array} fields An array of field definition objects, or field name strings.
12142  * @cfg {Array} data The multi-dimensional array of data
12143  * @constructor
12144  * @param {Object} config
12145  */
12146 Roo.data.SimpleStore = function(config){
12147     Roo.data.SimpleStore.superclass.constructor.call(this, {
12148         isLocal : true,
12149         reader: new Roo.data.ArrayReader({
12150                 id: config.id
12151             },
12152             Roo.data.Record.create(config.fields)
12153         ),
12154         proxy : new Roo.data.MemoryProxy(config.data)
12155     });
12156     this.load();
12157 };
12158 Roo.extend(Roo.data.SimpleStore, 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 /**
12171  * @extends Roo.data.Store
12172  * @class Roo.data.JsonStore
12173  * Small helper class to make creating Stores for JSON data easier. <br/>
12174 <pre><code>
12175 var store = new Roo.data.JsonStore({
12176     url: 'get-images.php',
12177     root: 'images',
12178     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12179 });
12180 </code></pre>
12181  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12182  * JsonReader and HttpProxy (unless inline data is provided).</b>
12183  * @cfg {Array} fields An array of field definition objects, or field name strings.
12184  * @constructor
12185  * @param {Object} config
12186  */
12187 Roo.data.JsonStore = function(c){
12188     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12189         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12190         reader: new Roo.data.JsonReader(c, c.fields)
12191     }));
12192 };
12193 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12194  * Based on:
12195  * Ext JS Library 1.1.1
12196  * Copyright(c) 2006-2007, Ext JS, LLC.
12197  *
12198  * Originally Released Under LGPL - original licence link has changed is not relivant.
12199  *
12200  * Fork - LGPL
12201  * <script type="text/javascript">
12202  */
12203
12204  
12205 Roo.data.Field = function(config){
12206     if(typeof config == "string"){
12207         config = {name: config};
12208     }
12209     Roo.apply(this, config);
12210     
12211     if(!this.type){
12212         this.type = "auto";
12213     }
12214     
12215     var st = Roo.data.SortTypes;
12216     // named sortTypes are supported, here we look them up
12217     if(typeof this.sortType == "string"){
12218         this.sortType = st[this.sortType];
12219     }
12220     
12221     // set default sortType for strings and dates
12222     if(!this.sortType){
12223         switch(this.type){
12224             case "string":
12225                 this.sortType = st.asUCString;
12226                 break;
12227             case "date":
12228                 this.sortType = st.asDate;
12229                 break;
12230             default:
12231                 this.sortType = st.none;
12232         }
12233     }
12234
12235     // define once
12236     var stripRe = /[\$,%]/g;
12237
12238     // prebuilt conversion function for this field, instead of
12239     // switching every time we're reading a value
12240     if(!this.convert){
12241         var cv, dateFormat = this.dateFormat;
12242         switch(this.type){
12243             case "":
12244             case "auto":
12245             case undefined:
12246                 cv = function(v){ return v; };
12247                 break;
12248             case "string":
12249                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12250                 break;
12251             case "int":
12252                 cv = function(v){
12253                     return v !== undefined && v !== null && v !== '' ?
12254                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12255                     };
12256                 break;
12257             case "float":
12258                 cv = function(v){
12259                     return v !== undefined && v !== null && v !== '' ?
12260                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12261                     };
12262                 break;
12263             case "bool":
12264             case "boolean":
12265                 cv = function(v){ return v === true || v === "true" || v == 1; };
12266                 break;
12267             case "date":
12268                 cv = function(v){
12269                     if(!v){
12270                         return '';
12271                     }
12272                     if(v instanceof Date){
12273                         return v;
12274                     }
12275                     if(dateFormat){
12276                         if(dateFormat == "timestamp"){
12277                             return new Date(v*1000);
12278                         }
12279                         return Date.parseDate(v, dateFormat);
12280                     }
12281                     var parsed = Date.parse(v);
12282                     return parsed ? new Date(parsed) : null;
12283                 };
12284              break;
12285             
12286         }
12287         this.convert = cv;
12288     }
12289 };
12290
12291 Roo.data.Field.prototype = {
12292     dateFormat: null,
12293     defaultValue: "",
12294     mapping: null,
12295     sortType : null,
12296     sortDir : "ASC"
12297 };/*
12298  * Based on:
12299  * Ext JS Library 1.1.1
12300  * Copyright(c) 2006-2007, Ext JS, LLC.
12301  *
12302  * Originally Released Under LGPL - original licence link has changed is not relivant.
12303  *
12304  * Fork - LGPL
12305  * <script type="text/javascript">
12306  */
12307  
12308 // Base class for reading structured data from a data source.  This class is intended to be
12309 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12310
12311 /**
12312  * @class Roo.data.DataReader
12313  * Base class for reading structured data from a data source.  This class is intended to be
12314  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12315  */
12316
12317 Roo.data.DataReader = function(meta, recordType){
12318     
12319     this.meta = meta;
12320     
12321     this.recordType = recordType instanceof Array ? 
12322         Roo.data.Record.create(recordType) : recordType;
12323 };
12324
12325 Roo.data.DataReader.prototype = {
12326      /**
12327      * Create an empty record
12328      * @param {Object} data (optional) - overlay some values
12329      * @return {Roo.data.Record} record created.
12330      */
12331     newRow :  function(d) {
12332         var da =  {};
12333         this.recordType.prototype.fields.each(function(c) {
12334             switch( c.type) {
12335                 case 'int' : da[c.name] = 0; break;
12336                 case 'date' : da[c.name] = new Date(); break;
12337                 case 'float' : da[c.name] = 0.0; break;
12338                 case 'boolean' : da[c.name] = false; break;
12339                 default : da[c.name] = ""; break;
12340             }
12341             
12342         });
12343         return new this.recordType(Roo.apply(da, d));
12344     }
12345     
12346 };/*
12347  * Based on:
12348  * Ext JS Library 1.1.1
12349  * Copyright(c) 2006-2007, Ext JS, LLC.
12350  *
12351  * Originally Released Under LGPL - original licence link has changed is not relivant.
12352  *
12353  * Fork - LGPL
12354  * <script type="text/javascript">
12355  */
12356
12357 /**
12358  * @class Roo.data.DataProxy
12359  * @extends Roo.data.Observable
12360  * This class is an abstract base class for implementations which provide retrieval of
12361  * unformatted data objects.<br>
12362  * <p>
12363  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12364  * (of the appropriate type which knows how to parse the data object) to provide a block of
12365  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12366  * <p>
12367  * Custom implementations must implement the load method as described in
12368  * {@link Roo.data.HttpProxy#load}.
12369  */
12370 Roo.data.DataProxy = function(){
12371     this.addEvents({
12372         /**
12373          * @event beforeload
12374          * Fires before a network request is made to retrieve a data object.
12375          * @param {Object} This DataProxy object.
12376          * @param {Object} params The params parameter to the load function.
12377          */
12378         beforeload : true,
12379         /**
12380          * @event load
12381          * Fires before the load method's callback is called.
12382          * @param {Object} This DataProxy object.
12383          * @param {Object} o The data object.
12384          * @param {Object} arg The callback argument object passed to the load function.
12385          */
12386         load : true,
12387         /**
12388          * @event loadexception
12389          * Fires if an Exception occurs during data retrieval.
12390          * @param {Object} This DataProxy object.
12391          * @param {Object} o The data object.
12392          * @param {Object} arg The callback argument object passed to the load function.
12393          * @param {Object} e The Exception.
12394          */
12395         loadexception : true
12396     });
12397     Roo.data.DataProxy.superclass.constructor.call(this);
12398 };
12399
12400 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12401
12402     /**
12403      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12404      */
12405 /*
12406  * Based on:
12407  * Ext JS Library 1.1.1
12408  * Copyright(c) 2006-2007, Ext JS, LLC.
12409  *
12410  * Originally Released Under LGPL - original licence link has changed is not relivant.
12411  *
12412  * Fork - LGPL
12413  * <script type="text/javascript">
12414  */
12415 /**
12416  * @class Roo.data.MemoryProxy
12417  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12418  * to the Reader when its load method is called.
12419  * @constructor
12420  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12421  */
12422 Roo.data.MemoryProxy = function(data){
12423     if (data.data) {
12424         data = data.data;
12425     }
12426     Roo.data.MemoryProxy.superclass.constructor.call(this);
12427     this.data = data;
12428 };
12429
12430 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12431     
12432     /**
12433      * Load data from the requested source (in this case an in-memory
12434      * data object passed to the constructor), read the data object into
12435      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12436      * process that block using the passed callback.
12437      * @param {Object} params This parameter is not used by the MemoryProxy class.
12438      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12439      * object into a block of Roo.data.Records.
12440      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12441      * The function must be passed <ul>
12442      * <li>The Record block object</li>
12443      * <li>The "arg" argument from the load function</li>
12444      * <li>A boolean success indicator</li>
12445      * </ul>
12446      * @param {Object} scope The scope in which to call the callback
12447      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12448      */
12449     load : function(params, reader, callback, scope, arg){
12450         params = params || {};
12451         var result;
12452         try {
12453             result = reader.readRecords(params.data ? params.data :this.data);
12454         }catch(e){
12455             this.fireEvent("loadexception", this, arg, null, e);
12456             callback.call(scope, null, arg, false);
12457             return;
12458         }
12459         callback.call(scope, result, arg, true);
12460     },
12461     
12462     // private
12463     update : function(params, records){
12464         
12465     }
12466 });/*
12467  * Based on:
12468  * Ext JS Library 1.1.1
12469  * Copyright(c) 2006-2007, Ext JS, LLC.
12470  *
12471  * Originally Released Under LGPL - original licence link has changed is not relivant.
12472  *
12473  * Fork - LGPL
12474  * <script type="text/javascript">
12475  */
12476 /**
12477  * @class Roo.data.HttpProxy
12478  * @extends Roo.data.DataProxy
12479  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12480  * configured to reference a certain URL.<br><br>
12481  * <p>
12482  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12483  * from which the running page was served.<br><br>
12484  * <p>
12485  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12486  * <p>
12487  * Be aware that to enable the browser to parse an XML document, the server must set
12488  * the Content-Type header in the HTTP response to "text/xml".
12489  * @constructor
12490  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12491  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12492  * will be used to make the request.
12493  */
12494 Roo.data.HttpProxy = function(conn){
12495     Roo.data.HttpProxy.superclass.constructor.call(this);
12496     // is conn a conn config or a real conn?
12497     this.conn = conn;
12498     this.useAjax = !conn || !conn.events;
12499   
12500 };
12501
12502 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12503     // thse are take from connection...
12504     
12505     /**
12506      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12507      */
12508     /**
12509      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12510      * extra parameters to each request made by this object. (defaults to undefined)
12511      */
12512     /**
12513      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12514      *  to each request made by this object. (defaults to undefined)
12515      */
12516     /**
12517      * @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)
12518      */
12519     /**
12520      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12521      */
12522      /**
12523      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12524      * @type Boolean
12525      */
12526   
12527
12528     /**
12529      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12530      * @type Boolean
12531      */
12532     /**
12533      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12534      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12535      * a finer-grained basis than the DataProxy events.
12536      */
12537     getConnection : function(){
12538         return this.useAjax ? Roo.Ajax : this.conn;
12539     },
12540
12541     /**
12542      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12543      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12544      * process that block using the passed callback.
12545      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12546      * for the request to the remote server.
12547      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12548      * object into a block of Roo.data.Records.
12549      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12550      * The function must be passed <ul>
12551      * <li>The Record block object</li>
12552      * <li>The "arg" argument from the load function</li>
12553      * <li>A boolean success indicator</li>
12554      * </ul>
12555      * @param {Object} scope The scope in which to call the callback
12556      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12557      */
12558     load : function(params, reader, callback, scope, arg){
12559         if(this.fireEvent("beforeload", this, params) !== false){
12560             var  o = {
12561                 params : params || {},
12562                 request: {
12563                     callback : callback,
12564                     scope : scope,
12565                     arg : arg
12566                 },
12567                 reader: reader,
12568                 callback : this.loadResponse,
12569                 scope: this
12570             };
12571             if(this.useAjax){
12572                 Roo.applyIf(o, this.conn);
12573                 if(this.activeRequest){
12574                     Roo.Ajax.abort(this.activeRequest);
12575                 }
12576                 this.activeRequest = Roo.Ajax.request(o);
12577             }else{
12578                 this.conn.request(o);
12579             }
12580         }else{
12581             callback.call(scope||this, null, arg, false);
12582         }
12583     },
12584
12585     // private
12586     loadResponse : function(o, success, response){
12587         delete this.activeRequest;
12588         if(!success){
12589             this.fireEvent("loadexception", this, o, response);
12590             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12591             return;
12592         }
12593         var result;
12594         try {
12595             result = o.reader.read(response);
12596         }catch(e){
12597             this.fireEvent("loadexception", this, o, response, e);
12598             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12599             return;
12600         }
12601         
12602         this.fireEvent("load", this, o, o.request.arg);
12603         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12604     },
12605
12606     // private
12607     update : function(dataSet){
12608
12609     },
12610
12611     // private
12612     updateResponse : function(dataSet){
12613
12614     }
12615 });/*
12616  * Based on:
12617  * Ext JS Library 1.1.1
12618  * Copyright(c) 2006-2007, Ext JS, LLC.
12619  *
12620  * Originally Released Under LGPL - original licence link has changed is not relivant.
12621  *
12622  * Fork - LGPL
12623  * <script type="text/javascript">
12624  */
12625
12626 /**
12627  * @class Roo.data.ScriptTagProxy
12628  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12629  * other than the originating domain of the running page.<br><br>
12630  * <p>
12631  * <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
12632  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12633  * <p>
12634  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12635  * source code that is used as the source inside a &lt;script> tag.<br><br>
12636  * <p>
12637  * In order for the browser to process the returned data, the server must wrap the data object
12638  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12639  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12640  * depending on whether the callback name was passed:
12641  * <p>
12642  * <pre><code>
12643 boolean scriptTag = false;
12644 String cb = request.getParameter("callback");
12645 if (cb != null) {
12646     scriptTag = true;
12647     response.setContentType("text/javascript");
12648 } else {
12649     response.setContentType("application/x-json");
12650 }
12651 Writer out = response.getWriter();
12652 if (scriptTag) {
12653     out.write(cb + "(");
12654 }
12655 out.print(dataBlock.toJsonString());
12656 if (scriptTag) {
12657     out.write(");");
12658 }
12659 </pre></code>
12660  *
12661  * @constructor
12662  * @param {Object} config A configuration object.
12663  */
12664 Roo.data.ScriptTagProxy = function(config){
12665     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12666     Roo.apply(this, config);
12667     this.head = document.getElementsByTagName("head")[0];
12668 };
12669
12670 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12671
12672 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12673     /**
12674      * @cfg {String} url The URL from which to request the data object.
12675      */
12676     /**
12677      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12678      */
12679     timeout : 30000,
12680     /**
12681      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12682      * the server the name of the callback function set up by the load call to process the returned data object.
12683      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12684      * javascript output which calls this named function passing the data object as its only parameter.
12685      */
12686     callbackParam : "callback",
12687     /**
12688      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12689      * name to the request.
12690      */
12691     nocache : true,
12692
12693     /**
12694      * Load data from the configured URL, read the data object into
12695      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12696      * process that block using the passed callback.
12697      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12698      * for the request to the remote server.
12699      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12700      * object into a block of Roo.data.Records.
12701      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12702      * The function must be passed <ul>
12703      * <li>The Record block object</li>
12704      * <li>The "arg" argument from the load function</li>
12705      * <li>A boolean success indicator</li>
12706      * </ul>
12707      * @param {Object} scope The scope in which to call the callback
12708      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12709      */
12710     load : function(params, reader, callback, scope, arg){
12711         if(this.fireEvent("beforeload", this, params) !== false){
12712
12713             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12714
12715             var url = this.url;
12716             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12717             if(this.nocache){
12718                 url += "&_dc=" + (new Date().getTime());
12719             }
12720             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12721             var trans = {
12722                 id : transId,
12723                 cb : "stcCallback"+transId,
12724                 scriptId : "stcScript"+transId,
12725                 params : params,
12726                 arg : arg,
12727                 url : url,
12728                 callback : callback,
12729                 scope : scope,
12730                 reader : reader
12731             };
12732             var conn = this;
12733
12734             window[trans.cb] = function(o){
12735                 conn.handleResponse(o, trans);
12736             };
12737
12738             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12739
12740             if(this.autoAbort !== false){
12741                 this.abort();
12742             }
12743
12744             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12745
12746             var script = document.createElement("script");
12747             script.setAttribute("src", url);
12748             script.setAttribute("type", "text/javascript");
12749             script.setAttribute("id", trans.scriptId);
12750             this.head.appendChild(script);
12751
12752             this.trans = trans;
12753         }else{
12754             callback.call(scope||this, null, arg, false);
12755         }
12756     },
12757
12758     // private
12759     isLoading : function(){
12760         return this.trans ? true : false;
12761     },
12762
12763     /**
12764      * Abort the current server request.
12765      */
12766     abort : function(){
12767         if(this.isLoading()){
12768             this.destroyTrans(this.trans);
12769         }
12770     },
12771
12772     // private
12773     destroyTrans : function(trans, isLoaded){
12774         this.head.removeChild(document.getElementById(trans.scriptId));
12775         clearTimeout(trans.timeoutId);
12776         if(isLoaded){
12777             window[trans.cb] = undefined;
12778             try{
12779                 delete window[trans.cb];
12780             }catch(e){}
12781         }else{
12782             // if hasn't been loaded, wait for load to remove it to prevent script error
12783             window[trans.cb] = function(){
12784                 window[trans.cb] = undefined;
12785                 try{
12786                     delete window[trans.cb];
12787                 }catch(e){}
12788             };
12789         }
12790     },
12791
12792     // private
12793     handleResponse : function(o, trans){
12794         this.trans = false;
12795         this.destroyTrans(trans, true);
12796         var result;
12797         try {
12798             result = trans.reader.readRecords(o);
12799         }catch(e){
12800             this.fireEvent("loadexception", this, o, trans.arg, e);
12801             trans.callback.call(trans.scope||window, null, trans.arg, false);
12802             return;
12803         }
12804         this.fireEvent("load", this, o, trans.arg);
12805         trans.callback.call(trans.scope||window, result, trans.arg, true);
12806     },
12807
12808     // private
12809     handleFailure : function(trans){
12810         this.trans = false;
12811         this.destroyTrans(trans, false);
12812         this.fireEvent("loadexception", this, null, trans.arg);
12813         trans.callback.call(trans.scope||window, null, trans.arg, false);
12814     }
12815 });/*
12816  * Based on:
12817  * Ext JS Library 1.1.1
12818  * Copyright(c) 2006-2007, Ext JS, LLC.
12819  *
12820  * Originally Released Under LGPL - original licence link has changed is not relivant.
12821  *
12822  * Fork - LGPL
12823  * <script type="text/javascript">
12824  */
12825
12826 /**
12827  * @class Roo.data.JsonReader
12828  * @extends Roo.data.DataReader
12829  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12830  * based on mappings in a provided Roo.data.Record constructor.
12831  * 
12832  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12833  * in the reply previously. 
12834  * 
12835  * <p>
12836  * Example code:
12837  * <pre><code>
12838 var RecordDef = Roo.data.Record.create([
12839     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12840     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12841 ]);
12842 var myReader = new Roo.data.JsonReader({
12843     totalProperty: "results",    // The property which contains the total dataset size (optional)
12844     root: "rows",                // The property which contains an Array of row objects
12845     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12846 }, RecordDef);
12847 </code></pre>
12848  * <p>
12849  * This would consume a JSON file like this:
12850  * <pre><code>
12851 { 'results': 2, 'rows': [
12852     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12853     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12854 }
12855 </code></pre>
12856  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12857  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12858  * paged from the remote server.
12859  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12860  * @cfg {String} root name of the property which contains the Array of row objects.
12861  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12862  * @cfg {Array} fields Array of field definition objects
12863  * @constructor
12864  * Create a new JsonReader
12865  * @param {Object} meta Metadata configuration options
12866  * @param {Object} recordType Either an Array of field definition objects,
12867  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12868  */
12869 Roo.data.JsonReader = function(meta, recordType){
12870     
12871     meta = meta || {};
12872     // set some defaults:
12873     Roo.applyIf(meta, {
12874         totalProperty: 'total',
12875         successProperty : 'success',
12876         root : 'data',
12877         id : 'id'
12878     });
12879     
12880     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12881 };
12882 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12883     
12884     /**
12885      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12886      * Used by Store query builder to append _requestMeta to params.
12887      * 
12888      */
12889     metaFromRemote : false,
12890     /**
12891      * This method is only used by a DataProxy which has retrieved data from a remote server.
12892      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12893      * @return {Object} data A data block which is used by an Roo.data.Store object as
12894      * a cache of Roo.data.Records.
12895      */
12896     read : function(response){
12897         var json = response.responseText;
12898        
12899         var o = /* eval:var:o */ eval("("+json+")");
12900         if(!o) {
12901             throw {message: "JsonReader.read: Json object not found"};
12902         }
12903         
12904         if(o.metaData){
12905             
12906             delete this.ef;
12907             this.metaFromRemote = true;
12908             this.meta = o.metaData;
12909             this.recordType = Roo.data.Record.create(o.metaData.fields);
12910             this.onMetaChange(this.meta, this.recordType, o);
12911         }
12912         return this.readRecords(o);
12913     },
12914
12915     // private function a store will implement
12916     onMetaChange : function(meta, recordType, o){
12917
12918     },
12919
12920     /**
12921          * @ignore
12922          */
12923     simpleAccess: function(obj, subsc) {
12924         return obj[subsc];
12925     },
12926
12927         /**
12928          * @ignore
12929          */
12930     getJsonAccessor: function(){
12931         var re = /[\[\.]/;
12932         return function(expr) {
12933             try {
12934                 return(re.test(expr))
12935                     ? new Function("obj", "return obj." + expr)
12936                     : function(obj){
12937                         return obj[expr];
12938                     };
12939             } catch(e){}
12940             return Roo.emptyFn;
12941         };
12942     }(),
12943
12944     /**
12945      * Create a data block containing Roo.data.Records from an XML document.
12946      * @param {Object} o An object which contains an Array of row objects in the property specified
12947      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12948      * which contains the total size of the dataset.
12949      * @return {Object} data A data block which is used by an Roo.data.Store object as
12950      * a cache of Roo.data.Records.
12951      */
12952     readRecords : function(o){
12953         /**
12954          * After any data loads, the raw JSON data is available for further custom processing.
12955          * @type Object
12956          */
12957         this.o = o;
12958         var s = this.meta, Record = this.recordType,
12959             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12960
12961 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12962         if (!this.ef) {
12963             if(s.totalProperty) {
12964                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12965                 }
12966                 if(s.successProperty) {
12967                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12968                 }
12969                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12970                 if (s.id) {
12971                         var g = this.getJsonAccessor(s.id);
12972                         this.getId = function(rec) {
12973                                 var r = g(rec);  
12974                                 return (r === undefined || r === "") ? null : r;
12975                         };
12976                 } else {
12977                         this.getId = function(){return null;};
12978                 }
12979             this.ef = [];
12980             for(var jj = 0; jj < fl; jj++){
12981                 f = fi[jj];
12982                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12983                 this.ef[jj] = this.getJsonAccessor(map);
12984             }
12985         }
12986
12987         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12988         if(s.totalProperty){
12989             var vt = parseInt(this.getTotal(o), 10);
12990             if(!isNaN(vt)){
12991                 totalRecords = vt;
12992             }
12993         }
12994         if(s.successProperty){
12995             var vs = this.getSuccess(o);
12996             if(vs === false || vs === 'false'){
12997                 success = false;
12998             }
12999         }
13000         var records = [];
13001         for(var i = 0; i < c; i++){
13002                 var n = root[i];
13003             var values = {};
13004             var id = this.getId(n);
13005             for(var j = 0; j < fl; j++){
13006                 f = fi[j];
13007             var v = this.ef[j](n);
13008             if (!f.convert) {
13009                 Roo.log('missing convert for ' + f.name);
13010                 Roo.log(f);
13011                 continue;
13012             }
13013             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13014             }
13015             var record = new Record(values, id);
13016             record.json = n;
13017             records[i] = record;
13018         }
13019         return {
13020             raw : o,
13021             success : success,
13022             records : records,
13023             totalRecords : totalRecords
13024         };
13025     }
13026 });/*
13027  * Based on:
13028  * Ext JS Library 1.1.1
13029  * Copyright(c) 2006-2007, Ext JS, LLC.
13030  *
13031  * Originally Released Under LGPL - original licence link has changed is not relivant.
13032  *
13033  * Fork - LGPL
13034  * <script type="text/javascript">
13035  */
13036
13037 /**
13038  * @class Roo.data.ArrayReader
13039  * @extends Roo.data.DataReader
13040  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13041  * Each element of that Array represents a row of data fields. The
13042  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13043  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13044  * <p>
13045  * Example code:.
13046  * <pre><code>
13047 var RecordDef = Roo.data.Record.create([
13048     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13049     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13050 ]);
13051 var myReader = new Roo.data.ArrayReader({
13052     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13053 }, RecordDef);
13054 </code></pre>
13055  * <p>
13056  * This would consume an Array like this:
13057  * <pre><code>
13058 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13059   </code></pre>
13060  
13061  * @constructor
13062  * Create a new JsonReader
13063  * @param {Object} meta Metadata configuration options.
13064  * @param {Object|Array} recordType Either an Array of field definition objects
13065  * 
13066  * @cfg {Array} fields Array of field definition objects
13067  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13068  * as specified to {@link Roo.data.Record#create},
13069  * or an {@link Roo.data.Record} object
13070  *
13071  * 
13072  * created using {@link Roo.data.Record#create}.
13073  */
13074 Roo.data.ArrayReader = function(meta, recordType){
13075     
13076      
13077     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13078 };
13079
13080 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13081     /**
13082      * Create a data block containing Roo.data.Records from an XML document.
13083      * @param {Object} o An Array of row objects which represents the dataset.
13084      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13085      * a cache of Roo.data.Records.
13086      */
13087     readRecords : function(o){
13088         var sid = this.meta ? this.meta.id : null;
13089         var recordType = this.recordType, fields = recordType.prototype.fields;
13090         var records = [];
13091         var root = o;
13092             for(var i = 0; i < root.length; i++){
13093                     var n = root[i];
13094                 var values = {};
13095                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13096                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13097                 var f = fields.items[j];
13098                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13099                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13100                 v = f.convert(v);
13101                 values[f.name] = v;
13102             }
13103                 var record = new recordType(values, id);
13104                 record.json = n;
13105                 records[records.length] = record;
13106             }
13107             return {
13108                 records : records,
13109                 totalRecords : records.length
13110             };
13111     }
13112 });/*
13113  * - LGPL
13114  * * 
13115  */
13116
13117 /**
13118  * @class Roo.bootstrap.ComboBox
13119  * @extends Roo.bootstrap.TriggerField
13120  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13121  * @cfg {Boolean} append (true|false) default false
13122  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13123  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13124  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13125  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13126  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13127  * @cfg {Boolean} animate default true
13128  * @cfg {Boolean} emptyResultText only for touch device
13129  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13130  * @cfg {String} emptyTitle default ''
13131  * @constructor
13132  * Create a new ComboBox.
13133  * @param {Object} config Configuration options
13134  */
13135 Roo.bootstrap.ComboBox = function(config){
13136     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13137     this.addEvents({
13138         /**
13139          * @event expand
13140          * Fires when the dropdown list is expanded
13141         * @param {Roo.bootstrap.ComboBox} combo This combo box
13142         */
13143         'expand' : true,
13144         /**
13145          * @event collapse
13146          * Fires when the dropdown list is collapsed
13147         * @param {Roo.bootstrap.ComboBox} combo This combo box
13148         */
13149         'collapse' : true,
13150         /**
13151          * @event beforeselect
13152          * Fires before a list item is selected. Return false to cancel the selection.
13153         * @param {Roo.bootstrap.ComboBox} combo This combo box
13154         * @param {Roo.data.Record} record The data record returned from the underlying store
13155         * @param {Number} index The index of the selected item in the dropdown list
13156         */
13157         'beforeselect' : true,
13158         /**
13159          * @event select
13160          * Fires when a list item is selected
13161         * @param {Roo.bootstrap.ComboBox} combo This combo box
13162         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13163         * @param {Number} index The index of the selected item in the dropdown list
13164         */
13165         'select' : true,
13166         /**
13167          * @event beforequery
13168          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13169          * The event object passed has these properties:
13170         * @param {Roo.bootstrap.ComboBox} combo This combo box
13171         * @param {String} query The query
13172         * @param {Boolean} forceAll true to force "all" query
13173         * @param {Boolean} cancel true to cancel the query
13174         * @param {Object} e The query event object
13175         */
13176         'beforequery': true,
13177          /**
13178          * @event add
13179          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13180         * @param {Roo.bootstrap.ComboBox} combo This combo box
13181         */
13182         'add' : true,
13183         /**
13184          * @event edit
13185          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13186         * @param {Roo.bootstrap.ComboBox} combo This combo box
13187         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13188         */
13189         'edit' : true,
13190         /**
13191          * @event remove
13192          * Fires when the remove value from the combobox array
13193         * @param {Roo.bootstrap.ComboBox} combo This combo box
13194         */
13195         'remove' : true,
13196         /**
13197          * @event afterremove
13198          * Fires when the remove value from the combobox array
13199         * @param {Roo.bootstrap.ComboBox} combo This combo box
13200         */
13201         'afterremove' : true,
13202         /**
13203          * @event specialfilter
13204          * Fires when specialfilter
13205             * @param {Roo.bootstrap.ComboBox} combo This combo box
13206             */
13207         'specialfilter' : true,
13208         /**
13209          * @event tick
13210          * Fires when tick the element
13211             * @param {Roo.bootstrap.ComboBox} combo This combo box
13212             */
13213         'tick' : true,
13214         /**
13215          * @event touchviewdisplay
13216          * Fires when touch view require special display (default is using displayField)
13217             * @param {Roo.bootstrap.ComboBox} combo This combo box
13218             * @param {Object} cfg set html .
13219             */
13220         'touchviewdisplay' : true
13221         
13222     });
13223     
13224     this.item = [];
13225     this.tickItems = [];
13226     
13227     this.selectedIndex = -1;
13228     if(this.mode == 'local'){
13229         if(config.queryDelay === undefined){
13230             this.queryDelay = 10;
13231         }
13232         if(config.minChars === undefined){
13233             this.minChars = 0;
13234         }
13235     }
13236 };
13237
13238 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13239      
13240     /**
13241      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13242      * rendering into an Roo.Editor, defaults to false)
13243      */
13244     /**
13245      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13246      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13247      */
13248     /**
13249      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13250      */
13251     /**
13252      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13253      * the dropdown list (defaults to undefined, with no header element)
13254      */
13255
13256      /**
13257      * @cfg {String/Roo.Template} tpl The template to use to render the output
13258      */
13259      
13260      /**
13261      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13262      */
13263     listWidth: undefined,
13264     /**
13265      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13266      * mode = 'remote' or 'text' if mode = 'local')
13267      */
13268     displayField: undefined,
13269     
13270     /**
13271      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13272      * mode = 'remote' or 'value' if mode = 'local'). 
13273      * Note: use of a valueField requires the user make a selection
13274      * in order for a value to be mapped.
13275      */
13276     valueField: undefined,
13277     /**
13278      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13279      */
13280     modalTitle : '',
13281     
13282     /**
13283      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13284      * field's data value (defaults to the underlying DOM element's name)
13285      */
13286     hiddenName: undefined,
13287     /**
13288      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13289      */
13290     listClass: '',
13291     /**
13292      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13293      */
13294     selectedClass: 'active',
13295     
13296     /**
13297      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13298      */
13299     shadow:'sides',
13300     /**
13301      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13302      * anchor positions (defaults to 'tl-bl')
13303      */
13304     listAlign: 'tl-bl?',
13305     /**
13306      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13307      */
13308     maxHeight: 300,
13309     /**
13310      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13311      * query specified by the allQuery config option (defaults to 'query')
13312      */
13313     triggerAction: 'query',
13314     /**
13315      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13316      * (defaults to 4, does not apply if editable = false)
13317      */
13318     minChars : 4,
13319     /**
13320      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13321      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13322      */
13323     typeAhead: false,
13324     /**
13325      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13326      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13327      */
13328     queryDelay: 500,
13329     /**
13330      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13331      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13332      */
13333     pageSize: 0,
13334     /**
13335      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13336      * when editable = true (defaults to false)
13337      */
13338     selectOnFocus:false,
13339     /**
13340      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13341      */
13342     queryParam: 'query',
13343     /**
13344      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13345      * when mode = 'remote' (defaults to 'Loading...')
13346      */
13347     loadingText: 'Loading...',
13348     /**
13349      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13350      */
13351     resizable: false,
13352     /**
13353      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13354      */
13355     handleHeight : 8,
13356     /**
13357      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13358      * traditional select (defaults to true)
13359      */
13360     editable: true,
13361     /**
13362      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13363      */
13364     allQuery: '',
13365     /**
13366      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13367      */
13368     mode: 'remote',
13369     /**
13370      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13371      * listWidth has a higher value)
13372      */
13373     minListWidth : 70,
13374     /**
13375      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13376      * allow the user to set arbitrary text into the field (defaults to false)
13377      */
13378     forceSelection:false,
13379     /**
13380      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13381      * if typeAhead = true (defaults to 250)
13382      */
13383     typeAheadDelay : 250,
13384     /**
13385      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13386      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13387      */
13388     valueNotFoundText : undefined,
13389     /**
13390      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13391      */
13392     blockFocus : false,
13393     
13394     /**
13395      * @cfg {Boolean} disableClear Disable showing of clear button.
13396      */
13397     disableClear : false,
13398     /**
13399      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13400      */
13401     alwaysQuery : false,
13402     
13403     /**
13404      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13405      */
13406     multiple : false,
13407     
13408     /**
13409      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13410      */
13411     invalidClass : "has-warning",
13412     
13413     /**
13414      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13415      */
13416     validClass : "has-success",
13417     
13418     /**
13419      * @cfg {Boolean} specialFilter (true|false) special filter default false
13420      */
13421     specialFilter : false,
13422     
13423     /**
13424      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13425      */
13426     mobileTouchView : true,
13427     
13428     /**
13429      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13430      */
13431     useNativeIOS : false,
13432     
13433     /**
13434      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13435      */
13436     mobile_restrict_height : false,
13437     
13438     ios_options : false,
13439     
13440     //private
13441     addicon : false,
13442     editicon: false,
13443     
13444     page: 0,
13445     hasQuery: false,
13446     append: false,
13447     loadNext: false,
13448     autoFocus : true,
13449     tickable : false,
13450     btnPosition : 'right',
13451     triggerList : true,
13452     showToggleBtn : true,
13453     animate : true,
13454     emptyResultText: 'Empty',
13455     triggerText : 'Select',
13456     emptyTitle : '',
13457     
13458     // element that contains real text value.. (when hidden is used..)
13459     
13460     getAutoCreate : function()
13461     {   
13462         var cfg = false;
13463         //render
13464         /*
13465          * Render classic select for iso
13466          */
13467         
13468         if(Roo.isIOS && this.useNativeIOS){
13469             cfg = this.getAutoCreateNativeIOS();
13470             return cfg;
13471         }
13472         
13473         /*
13474          * Touch Devices
13475          */
13476         
13477         if(Roo.isTouch && this.mobileTouchView){
13478             cfg = this.getAutoCreateTouchView();
13479             return cfg;;
13480         }
13481         
13482         /*
13483          *  Normal ComboBox
13484          */
13485         if(!this.tickable){
13486             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13487             return cfg;
13488         }
13489         
13490         /*
13491          *  ComboBox with tickable selections
13492          */
13493              
13494         var align = this.labelAlign || this.parentLabelAlign();
13495         
13496         cfg = {
13497             cls : 'form-group roo-combobox-tickable' //input-group
13498         };
13499         
13500         var btn_text_select = '';
13501         var btn_text_done = '';
13502         var btn_text_cancel = '';
13503         
13504         if (this.btn_text_show) {
13505             btn_text_select = 'Select';
13506             btn_text_done = 'Done';
13507             btn_text_cancel = 'Cancel'; 
13508         }
13509         
13510         var buttons = {
13511             tag : 'div',
13512             cls : 'tickable-buttons',
13513             cn : [
13514                 {
13515                     tag : 'button',
13516                     type : 'button',
13517                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13518                     //html : this.triggerText
13519                     html: btn_text_select
13520                 },
13521                 {
13522                     tag : 'button',
13523                     type : 'button',
13524                     name : 'ok',
13525                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13526                     //html : 'Done'
13527                     html: btn_text_done
13528                 },
13529                 {
13530                     tag : 'button',
13531                     type : 'button',
13532                     name : 'cancel',
13533                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13534                     //html : 'Cancel'
13535                     html: btn_text_cancel
13536                 }
13537             ]
13538         };
13539         
13540         if(this.editable){
13541             buttons.cn.unshift({
13542                 tag: 'input',
13543                 cls: 'roo-select2-search-field-input'
13544             });
13545         }
13546         
13547         var _this = this;
13548         
13549         Roo.each(buttons.cn, function(c){
13550             if (_this.size) {
13551                 c.cls += ' btn-' + _this.size;
13552             }
13553
13554             if (_this.disabled) {
13555                 c.disabled = true;
13556             }
13557         });
13558         
13559         var box = {
13560             tag: 'div',
13561             style : 'display: contents',
13562             cn: [
13563                 {
13564                     tag: 'input',
13565                     type : 'hidden',
13566                     cls: 'form-hidden-field'
13567                 },
13568                 {
13569                     tag: 'ul',
13570                     cls: 'roo-select2-choices',
13571                     cn:[
13572                         {
13573                             tag: 'li',
13574                             cls: 'roo-select2-search-field',
13575                             cn: [
13576                                 buttons
13577                             ]
13578                         }
13579                     ]
13580                 }
13581             ]
13582         };
13583         
13584         var combobox = {
13585             cls: 'roo-select2-container input-group roo-select2-container-multi',
13586             cn: [
13587                 
13588                 box
13589 //                {
13590 //                    tag: 'ul',
13591 //                    cls: 'typeahead typeahead-long dropdown-menu',
13592 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13593 //                }
13594             ]
13595         };
13596         
13597         if(this.hasFeedback && !this.allowBlank){
13598             
13599             var feedback = {
13600                 tag: 'span',
13601                 cls: 'glyphicon form-control-feedback'
13602             };
13603
13604             combobox.cn.push(feedback);
13605         }
13606         
13607         var indicator = {
13608             tag : 'i',
13609             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13610             tooltip : 'This field is required'
13611         };
13612         if (Roo.bootstrap.version == 4) {
13613             indicator = {
13614                 tag : 'i',
13615                 style : 'display:none'
13616             };
13617         }
13618         if (align ==='left' && this.fieldLabel.length) {
13619             
13620             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13621             
13622             cfg.cn = [
13623                 indicator,
13624                 {
13625                     tag: 'label',
13626                     'for' :  id,
13627                     cls : 'control-label col-form-label',
13628                     html : this.fieldLabel
13629
13630                 },
13631                 {
13632                     cls : "", 
13633                     cn: [
13634                         combobox
13635                     ]
13636                 }
13637
13638             ];
13639             
13640             var labelCfg = cfg.cn[1];
13641             var contentCfg = cfg.cn[2];
13642             
13643
13644             if(this.indicatorpos == 'right'){
13645                 
13646                 cfg.cn = [
13647                     {
13648                         tag: 'label',
13649                         'for' :  id,
13650                         cls : 'control-label col-form-label',
13651                         cn : [
13652                             {
13653                                 tag : 'span',
13654                                 html : this.fieldLabel
13655                             },
13656                             indicator
13657                         ]
13658                     },
13659                     {
13660                         cls : "",
13661                         cn: [
13662                             combobox
13663                         ]
13664                     }
13665
13666                 ];
13667                 
13668                 
13669                 
13670                 labelCfg = cfg.cn[0];
13671                 contentCfg = cfg.cn[1];
13672             
13673             }
13674             
13675             if(this.labelWidth > 12){
13676                 labelCfg.style = "width: " + this.labelWidth + 'px';
13677             }
13678             
13679             if(this.labelWidth < 13 && this.labelmd == 0){
13680                 this.labelmd = this.labelWidth;
13681             }
13682             
13683             if(this.labellg > 0){
13684                 labelCfg.cls += ' col-lg-' + this.labellg;
13685                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13686             }
13687             
13688             if(this.labelmd > 0){
13689                 labelCfg.cls += ' col-md-' + this.labelmd;
13690                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13691             }
13692             
13693             if(this.labelsm > 0){
13694                 labelCfg.cls += ' col-sm-' + this.labelsm;
13695                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13696             }
13697             
13698             if(this.labelxs > 0){
13699                 labelCfg.cls += ' col-xs-' + this.labelxs;
13700                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13701             }
13702                 
13703                 
13704         } else if ( this.fieldLabel.length) {
13705 //                Roo.log(" label");
13706                  cfg.cn = [
13707                    indicator,
13708                     {
13709                         tag: 'label',
13710                         //cls : 'input-group-addon',
13711                         html : this.fieldLabel
13712                     },
13713                     combobox
13714                 ];
13715                 
13716                 if(this.indicatorpos == 'right'){
13717                     cfg.cn = [
13718                         {
13719                             tag: 'label',
13720                             //cls : 'input-group-addon',
13721                             html : this.fieldLabel
13722                         },
13723                         indicator,
13724                         combobox
13725                     ];
13726                     
13727                 }
13728
13729         } else {
13730             
13731 //                Roo.log(" no label && no align");
13732                 cfg = combobox
13733                      
13734                 
13735         }
13736          
13737         var settings=this;
13738         ['xs','sm','md','lg'].map(function(size){
13739             if (settings[size]) {
13740                 cfg.cls += ' col-' + size + '-' + settings[size];
13741             }
13742         });
13743         
13744         return cfg;
13745         
13746     },
13747     
13748     _initEventsCalled : false,
13749     
13750     // private
13751     initEvents: function()
13752     {   
13753         if (this._initEventsCalled) { // as we call render... prevent looping...
13754             return;
13755         }
13756         this._initEventsCalled = true;
13757         
13758         if (!this.store) {
13759             throw "can not find store for combo";
13760         }
13761         
13762         this.indicator = this.indicatorEl();
13763         
13764         this.store = Roo.factory(this.store, Roo.data);
13765         this.store.parent = this;
13766         
13767         // if we are building from html. then this element is so complex, that we can not really
13768         // use the rendered HTML.
13769         // so we have to trash and replace the previous code.
13770         if (Roo.XComponent.build_from_html) {
13771             // remove this element....
13772             var e = this.el.dom, k=0;
13773             while (e ) { e = e.previousSibling;  ++k;}
13774
13775             this.el.remove();
13776             
13777             this.el=false;
13778             this.rendered = false;
13779             
13780             this.render(this.parent().getChildContainer(true), k);
13781         }
13782         
13783         if(Roo.isIOS && this.useNativeIOS){
13784             this.initIOSView();
13785             return;
13786         }
13787         
13788         /*
13789          * Touch Devices
13790          */
13791         
13792         if(Roo.isTouch && this.mobileTouchView){
13793             this.initTouchView();
13794             return;
13795         }
13796         
13797         if(this.tickable){
13798             this.initTickableEvents();
13799             return;
13800         }
13801         
13802         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13803         
13804         if(this.hiddenName){
13805             
13806             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13807             
13808             this.hiddenField.dom.value =
13809                 this.hiddenValue !== undefined ? this.hiddenValue :
13810                 this.value !== undefined ? this.value : '';
13811
13812             // prevent input submission
13813             this.el.dom.removeAttribute('name');
13814             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13815              
13816              
13817         }
13818         //if(Roo.isGecko){
13819         //    this.el.dom.setAttribute('autocomplete', 'off');
13820         //}
13821         
13822         var cls = 'x-combo-list';
13823         
13824         //this.list = new Roo.Layer({
13825         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13826         //});
13827         
13828         var _this = this;
13829         
13830         (function(){
13831             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13832             _this.list.setWidth(lw);
13833         }).defer(100);
13834         
13835         this.list.on('mouseover', this.onViewOver, this);
13836         this.list.on('mousemove', this.onViewMove, this);
13837         this.list.on('scroll', this.onViewScroll, this);
13838         
13839         /*
13840         this.list.swallowEvent('mousewheel');
13841         this.assetHeight = 0;
13842
13843         if(this.title){
13844             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13845             this.assetHeight += this.header.getHeight();
13846         }
13847
13848         this.innerList = this.list.createChild({cls:cls+'-inner'});
13849         this.innerList.on('mouseover', this.onViewOver, this);
13850         this.innerList.on('mousemove', this.onViewMove, this);
13851         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13852         
13853         if(this.allowBlank && !this.pageSize && !this.disableClear){
13854             this.footer = this.list.createChild({cls:cls+'-ft'});
13855             this.pageTb = new Roo.Toolbar(this.footer);
13856            
13857         }
13858         if(this.pageSize){
13859             this.footer = this.list.createChild({cls:cls+'-ft'});
13860             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13861                     {pageSize: this.pageSize});
13862             
13863         }
13864         
13865         if (this.pageTb && this.allowBlank && !this.disableClear) {
13866             var _this = this;
13867             this.pageTb.add(new Roo.Toolbar.Fill(), {
13868                 cls: 'x-btn-icon x-btn-clear',
13869                 text: '&#160;',
13870                 handler: function()
13871                 {
13872                     _this.collapse();
13873                     _this.clearValue();
13874                     _this.onSelect(false, -1);
13875                 }
13876             });
13877         }
13878         if (this.footer) {
13879             this.assetHeight += this.footer.getHeight();
13880         }
13881         */
13882             
13883         if(!this.tpl){
13884             this.tpl = Roo.bootstrap.version == 4 ?
13885                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13886                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13887         }
13888
13889         this.view = new Roo.View(this.list, this.tpl, {
13890             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13891         });
13892         //this.view.wrapEl.setDisplayed(false);
13893         this.view.on('click', this.onViewClick, this);
13894         
13895         
13896         this.store.on('beforeload', this.onBeforeLoad, this);
13897         this.store.on('load', this.onLoad, this);
13898         this.store.on('loadexception', this.onLoadException, this);
13899         /*
13900         if(this.resizable){
13901             this.resizer = new Roo.Resizable(this.list,  {
13902                pinned:true, handles:'se'
13903             });
13904             this.resizer.on('resize', function(r, w, h){
13905                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13906                 this.listWidth = w;
13907                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13908                 this.restrictHeight();
13909             }, this);
13910             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13911         }
13912         */
13913         if(!this.editable){
13914             this.editable = true;
13915             this.setEditable(false);
13916         }
13917         
13918         /*
13919         
13920         if (typeof(this.events.add.listeners) != 'undefined') {
13921             
13922             this.addicon = this.wrap.createChild(
13923                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13924        
13925             this.addicon.on('click', function(e) {
13926                 this.fireEvent('add', this);
13927             }, this);
13928         }
13929         if (typeof(this.events.edit.listeners) != 'undefined') {
13930             
13931             this.editicon = this.wrap.createChild(
13932                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13933             if (this.addicon) {
13934                 this.editicon.setStyle('margin-left', '40px');
13935             }
13936             this.editicon.on('click', function(e) {
13937                 
13938                 // we fire even  if inothing is selected..
13939                 this.fireEvent('edit', this, this.lastData );
13940                 
13941             }, this);
13942         }
13943         */
13944         
13945         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13946             "up" : function(e){
13947                 this.inKeyMode = true;
13948                 this.selectPrev();
13949             },
13950
13951             "down" : function(e){
13952                 if(!this.isExpanded()){
13953                     this.onTriggerClick();
13954                 }else{
13955                     this.inKeyMode = true;
13956                     this.selectNext();
13957                 }
13958             },
13959
13960             "enter" : function(e){
13961 //                this.onViewClick();
13962                 //return true;
13963                 this.collapse();
13964                 
13965                 if(this.fireEvent("specialkey", this, e)){
13966                     this.onViewClick(false);
13967                 }
13968                 
13969                 return true;
13970             },
13971
13972             "esc" : function(e){
13973                 this.collapse();
13974             },
13975
13976             "tab" : function(e){
13977                 this.collapse();
13978                 
13979                 if(this.fireEvent("specialkey", this, e)){
13980                     this.onViewClick(false);
13981                 }
13982                 
13983                 return true;
13984             },
13985
13986             scope : this,
13987
13988             doRelay : function(foo, bar, hname){
13989                 if(hname == 'down' || this.scope.isExpanded()){
13990                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13991                 }
13992                 return true;
13993             },
13994
13995             forceKeyDown: true
13996         });
13997         
13998         
13999         this.queryDelay = Math.max(this.queryDelay || 10,
14000                 this.mode == 'local' ? 10 : 250);
14001         
14002         
14003         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14004         
14005         if(this.typeAhead){
14006             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14007         }
14008         if(this.editable !== false){
14009             this.inputEl().on("keyup", this.onKeyUp, this);
14010         }
14011         if(this.forceSelection){
14012             this.inputEl().on('blur', this.doForce, this);
14013         }
14014         
14015         if(this.multiple){
14016             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14017             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14018         }
14019     },
14020     
14021     initTickableEvents: function()
14022     {   
14023         this.createList();
14024         
14025         if(this.hiddenName){
14026             
14027             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14028             
14029             this.hiddenField.dom.value =
14030                 this.hiddenValue !== undefined ? this.hiddenValue :
14031                 this.value !== undefined ? this.value : '';
14032
14033             // prevent input submission
14034             this.el.dom.removeAttribute('name');
14035             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14036              
14037              
14038         }
14039         
14040 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14041         
14042         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14043         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14044         if(this.triggerList){
14045             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14046         }
14047          
14048         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14049         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14050         
14051         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14052         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14053         
14054         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14055         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14056         
14057         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14058         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14059         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14060         
14061         this.okBtn.hide();
14062         this.cancelBtn.hide();
14063         
14064         var _this = this;
14065         
14066         (function(){
14067             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14068             _this.list.setWidth(lw);
14069         }).defer(100);
14070         
14071         this.list.on('mouseover', this.onViewOver, this);
14072         this.list.on('mousemove', this.onViewMove, this);
14073         
14074         this.list.on('scroll', this.onViewScroll, this);
14075         
14076         if(!this.tpl){
14077             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14078                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14079         }
14080
14081         this.view = new Roo.View(this.list, this.tpl, {
14082             singleSelect:true,
14083             tickable:true,
14084             parent:this,
14085             store: this.store,
14086             selectedClass: this.selectedClass
14087         });
14088         
14089         //this.view.wrapEl.setDisplayed(false);
14090         this.view.on('click', this.onViewClick, this);
14091         
14092         
14093         
14094         this.store.on('beforeload', this.onBeforeLoad, this);
14095         this.store.on('load', this.onLoad, this);
14096         this.store.on('loadexception', this.onLoadException, this);
14097         
14098         if(this.editable){
14099             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14100                 "up" : function(e){
14101                     this.inKeyMode = true;
14102                     this.selectPrev();
14103                 },
14104
14105                 "down" : function(e){
14106                     this.inKeyMode = true;
14107                     this.selectNext();
14108                 },
14109
14110                 "enter" : function(e){
14111                     if(this.fireEvent("specialkey", this, e)){
14112                         this.onViewClick(false);
14113                     }
14114                     
14115                     return true;
14116                 },
14117
14118                 "esc" : function(e){
14119                     this.onTickableFooterButtonClick(e, false, false);
14120                 },
14121
14122                 "tab" : function(e){
14123                     this.fireEvent("specialkey", this, e);
14124                     
14125                     this.onTickableFooterButtonClick(e, false, false);
14126                     
14127                     return true;
14128                 },
14129
14130                 scope : this,
14131
14132                 doRelay : function(e, fn, key){
14133                     if(this.scope.isExpanded()){
14134                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14135                     }
14136                     return true;
14137                 },
14138
14139                 forceKeyDown: true
14140             });
14141         }
14142         
14143         this.queryDelay = Math.max(this.queryDelay || 10,
14144                 this.mode == 'local' ? 10 : 250);
14145         
14146         
14147         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14148         
14149         if(this.typeAhead){
14150             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14151         }
14152         
14153         if(this.editable !== false){
14154             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14155         }
14156         
14157         this.indicator = this.indicatorEl();
14158         
14159         if(this.indicator){
14160             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14161             this.indicator.hide();
14162         }
14163         
14164     },
14165
14166     onDestroy : function(){
14167         if(this.view){
14168             this.view.setStore(null);
14169             this.view.el.removeAllListeners();
14170             this.view.el.remove();
14171             this.view.purgeListeners();
14172         }
14173         if(this.list){
14174             this.list.dom.innerHTML  = '';
14175         }
14176         
14177         if(this.store){
14178             this.store.un('beforeload', this.onBeforeLoad, this);
14179             this.store.un('load', this.onLoad, this);
14180             this.store.un('loadexception', this.onLoadException, this);
14181         }
14182         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14183     },
14184
14185     // private
14186     fireKey : function(e){
14187         if(e.isNavKeyPress() && !this.list.isVisible()){
14188             this.fireEvent("specialkey", this, e);
14189         }
14190     },
14191
14192     // private
14193     onResize: function(w, h){
14194 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14195 //        
14196 //        if(typeof w != 'number'){
14197 //            // we do not handle it!?!?
14198 //            return;
14199 //        }
14200 //        var tw = this.trigger.getWidth();
14201 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14202 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14203 //        var x = w - tw;
14204 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14205 //            
14206 //        //this.trigger.setStyle('left', x+'px');
14207 //        
14208 //        if(this.list && this.listWidth === undefined){
14209 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14210 //            this.list.setWidth(lw);
14211 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14212 //        }
14213         
14214     
14215         
14216     },
14217
14218     /**
14219      * Allow or prevent the user from directly editing the field text.  If false is passed,
14220      * the user will only be able to select from the items defined in the dropdown list.  This method
14221      * is the runtime equivalent of setting the 'editable' config option at config time.
14222      * @param {Boolean} value True to allow the user to directly edit the field text
14223      */
14224     setEditable : function(value){
14225         if(value == this.editable){
14226             return;
14227         }
14228         this.editable = value;
14229         if(!value){
14230             this.inputEl().dom.setAttribute('readOnly', true);
14231             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14232             this.inputEl().addClass('x-combo-noedit');
14233         }else{
14234             this.inputEl().dom.setAttribute('readOnly', false);
14235             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14236             this.inputEl().removeClass('x-combo-noedit');
14237         }
14238     },
14239
14240     // private
14241     
14242     onBeforeLoad : function(combo,opts){
14243         if(!this.hasFocus){
14244             return;
14245         }
14246          if (!opts.add) {
14247             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14248          }
14249         this.restrictHeight();
14250         this.selectedIndex = -1;
14251     },
14252
14253     // private
14254     onLoad : function(){
14255         
14256         this.hasQuery = false;
14257         
14258         if(!this.hasFocus){
14259             return;
14260         }
14261         
14262         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14263             this.loading.hide();
14264         }
14265         
14266         if(this.store.getCount() > 0){
14267             
14268             this.expand();
14269             this.restrictHeight();
14270             if(this.lastQuery == this.allQuery){
14271                 if(this.editable && !this.tickable){
14272                     this.inputEl().dom.select();
14273                 }
14274                 
14275                 if(
14276                     !this.selectByValue(this.value, true) &&
14277                     this.autoFocus && 
14278                     (
14279                         !this.store.lastOptions ||
14280                         typeof(this.store.lastOptions.add) == 'undefined' || 
14281                         this.store.lastOptions.add != true
14282                     )
14283                 ){
14284                     this.select(0, true);
14285                 }
14286             }else{
14287                 if(this.autoFocus){
14288                     this.selectNext();
14289                 }
14290                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14291                     this.taTask.delay(this.typeAheadDelay);
14292                 }
14293             }
14294         }else{
14295             this.onEmptyResults();
14296         }
14297         
14298         //this.el.focus();
14299     },
14300     // private
14301     onLoadException : function()
14302     {
14303         this.hasQuery = false;
14304         
14305         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14306             this.loading.hide();
14307         }
14308         
14309         if(this.tickable && this.editable){
14310             return;
14311         }
14312         
14313         this.collapse();
14314         // only causes errors at present
14315         //Roo.log(this.store.reader.jsonData);
14316         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14317             // fixme
14318             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14319         //}
14320         
14321         
14322     },
14323     // private
14324     onTypeAhead : function(){
14325         if(this.store.getCount() > 0){
14326             var r = this.store.getAt(0);
14327             var newValue = r.data[this.displayField];
14328             var len = newValue.length;
14329             var selStart = this.getRawValue().length;
14330             
14331             if(selStart != len){
14332                 this.setRawValue(newValue);
14333                 this.selectText(selStart, newValue.length);
14334             }
14335         }
14336     },
14337
14338     // private
14339     onSelect : function(record, index){
14340         
14341         if(this.fireEvent('beforeselect', this, record, index) !== false){
14342         
14343             this.setFromData(index > -1 ? record.data : false);
14344             
14345             this.collapse();
14346             this.fireEvent('select', this, record, index);
14347         }
14348     },
14349
14350     /**
14351      * Returns the currently selected field value or empty string if no value is set.
14352      * @return {String} value The selected value
14353      */
14354     getValue : function()
14355     {
14356         if(Roo.isIOS && this.useNativeIOS){
14357             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14358         }
14359         
14360         if(this.multiple){
14361             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14362         }
14363         
14364         if(this.valueField){
14365             return typeof this.value != 'undefined' ? this.value : '';
14366         }else{
14367             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14368         }
14369     },
14370     
14371     getRawValue : function()
14372     {
14373         if(Roo.isIOS && this.useNativeIOS){
14374             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14375         }
14376         
14377         var v = this.inputEl().getValue();
14378         
14379         return v;
14380     },
14381
14382     /**
14383      * Clears any text/value currently set in the field
14384      */
14385     clearValue : function(){
14386         
14387         if(this.hiddenField){
14388             this.hiddenField.dom.value = '';
14389         }
14390         this.value = '';
14391         this.setRawValue('');
14392         this.lastSelectionText = '';
14393         this.lastData = false;
14394         
14395         var close = this.closeTriggerEl();
14396         
14397         if(close){
14398             close.hide();
14399         }
14400         
14401         this.validate();
14402         
14403     },
14404
14405     /**
14406      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14407      * will be displayed in the field.  If the value does not match the data value of an existing item,
14408      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14409      * Otherwise the field will be blank (although the value will still be set).
14410      * @param {String} value The value to match
14411      */
14412     setValue : function(v)
14413     {
14414         if(Roo.isIOS && this.useNativeIOS){
14415             this.setIOSValue(v);
14416             return;
14417         }
14418         
14419         if(this.multiple){
14420             this.syncValue();
14421             return;
14422         }
14423         
14424         var text = v;
14425         if(this.valueField){
14426             var r = this.findRecord(this.valueField, v);
14427             if(r){
14428                 text = r.data[this.displayField];
14429             }else if(this.valueNotFoundText !== undefined){
14430                 text = this.valueNotFoundText;
14431             }
14432         }
14433         this.lastSelectionText = text;
14434         if(this.hiddenField){
14435             this.hiddenField.dom.value = v;
14436         }
14437         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14438         this.value = v;
14439         
14440         var close = this.closeTriggerEl();
14441         
14442         if(close){
14443             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14444         }
14445         
14446         this.validate();
14447     },
14448     /**
14449      * @property {Object} the last set data for the element
14450      */
14451     
14452     lastData : false,
14453     /**
14454      * Sets the value of the field based on a object which is related to the record format for the store.
14455      * @param {Object} value the value to set as. or false on reset?
14456      */
14457     setFromData : function(o){
14458         
14459         if(this.multiple){
14460             this.addItem(o);
14461             return;
14462         }
14463             
14464         var dv = ''; // display value
14465         var vv = ''; // value value..
14466         this.lastData = o;
14467         if (this.displayField) {
14468             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14469         } else {
14470             // this is an error condition!!!
14471             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14472         }
14473         
14474         if(this.valueField){
14475             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14476         }
14477         
14478         var close = this.closeTriggerEl();
14479         
14480         if(close){
14481             if(dv.length || vv * 1 > 0){
14482                 close.show() ;
14483                 this.blockFocus=true;
14484             } else {
14485                 close.hide();
14486             }             
14487         }
14488         
14489         if(this.hiddenField){
14490             this.hiddenField.dom.value = vv;
14491             
14492             this.lastSelectionText = dv;
14493             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14494             this.value = vv;
14495             return;
14496         }
14497         // no hidden field.. - we store the value in 'value', but still display
14498         // display field!!!!
14499         this.lastSelectionText = dv;
14500         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14501         this.value = vv;
14502         
14503         
14504         
14505     },
14506     // private
14507     reset : function(){
14508         // overridden so that last data is reset..
14509         
14510         if(this.multiple){
14511             this.clearItem();
14512             return;
14513         }
14514         
14515         this.setValue(this.originalValue);
14516         //this.clearInvalid();
14517         this.lastData = false;
14518         if (this.view) {
14519             this.view.clearSelections();
14520         }
14521         
14522         this.validate();
14523     },
14524     // private
14525     findRecord : function(prop, value){
14526         var record;
14527         if(this.store.getCount() > 0){
14528             this.store.each(function(r){
14529                 if(r.data[prop] == value){
14530                     record = r;
14531                     return false;
14532                 }
14533                 return true;
14534             });
14535         }
14536         return record;
14537     },
14538     
14539     getName: function()
14540     {
14541         // returns hidden if it's set..
14542         if (!this.rendered) {return ''};
14543         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14544         
14545     },
14546     // private
14547     onViewMove : function(e, t){
14548         this.inKeyMode = false;
14549     },
14550
14551     // private
14552     onViewOver : function(e, t){
14553         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14554             return;
14555         }
14556         var item = this.view.findItemFromChild(t);
14557         
14558         if(item){
14559             var index = this.view.indexOf(item);
14560             this.select(index, false);
14561         }
14562     },
14563
14564     // private
14565     onViewClick : function(view, doFocus, el, e)
14566     {
14567         var index = this.view.getSelectedIndexes()[0];
14568         
14569         var r = this.store.getAt(index);
14570         
14571         if(this.tickable){
14572             
14573             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14574                 return;
14575             }
14576             
14577             var rm = false;
14578             var _this = this;
14579             
14580             Roo.each(this.tickItems, function(v,k){
14581                 
14582                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14583                     Roo.log(v);
14584                     _this.tickItems.splice(k, 1);
14585                     
14586                     if(typeof(e) == 'undefined' && view == false){
14587                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14588                     }
14589                     
14590                     rm = true;
14591                     return;
14592                 }
14593             });
14594             
14595             if(rm){
14596                 return;
14597             }
14598             
14599             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14600                 this.tickItems.push(r.data);
14601             }
14602             
14603             if(typeof(e) == 'undefined' && view == false){
14604                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14605             }
14606                     
14607             return;
14608         }
14609         
14610         if(r){
14611             this.onSelect(r, index);
14612         }
14613         if(doFocus !== false && !this.blockFocus){
14614             this.inputEl().focus();
14615         }
14616     },
14617
14618     // private
14619     restrictHeight : function(){
14620         //this.innerList.dom.style.height = '';
14621         //var inner = this.innerList.dom;
14622         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14623         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14624         //this.list.beginUpdate();
14625         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14626         this.list.alignTo(this.inputEl(), this.listAlign);
14627         this.list.alignTo(this.inputEl(), this.listAlign);
14628         //this.list.endUpdate();
14629     },
14630
14631     // private
14632     onEmptyResults : function(){
14633         
14634         if(this.tickable && this.editable){
14635             this.hasFocus = false;
14636             this.restrictHeight();
14637             return;
14638         }
14639         
14640         this.collapse();
14641     },
14642
14643     /**
14644      * Returns true if the dropdown list is expanded, else false.
14645      */
14646     isExpanded : function(){
14647         return this.list.isVisible();
14648     },
14649
14650     /**
14651      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14652      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14653      * @param {String} value The data value of the item to select
14654      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14655      * selected item if it is not currently in view (defaults to true)
14656      * @return {Boolean} True if the value matched an item in the list, else false
14657      */
14658     selectByValue : function(v, scrollIntoView){
14659         if(v !== undefined && v !== null){
14660             var r = this.findRecord(this.valueField || this.displayField, v);
14661             if(r){
14662                 this.select(this.store.indexOf(r), scrollIntoView);
14663                 return true;
14664             }
14665         }
14666         return false;
14667     },
14668
14669     /**
14670      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14671      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14672      * @param {Number} index The zero-based index of the list item to select
14673      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14674      * selected item if it is not currently in view (defaults to true)
14675      */
14676     select : function(index, scrollIntoView){
14677         this.selectedIndex = index;
14678         this.view.select(index);
14679         if(scrollIntoView !== false){
14680             var el = this.view.getNode(index);
14681             /*
14682              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14683              */
14684             if(el){
14685                 this.list.scrollChildIntoView(el, false);
14686             }
14687         }
14688     },
14689
14690     // private
14691     selectNext : function(){
14692         var ct = this.store.getCount();
14693         if(ct > 0){
14694             if(this.selectedIndex == -1){
14695                 this.select(0);
14696             }else if(this.selectedIndex < ct-1){
14697                 this.select(this.selectedIndex+1);
14698             }
14699         }
14700     },
14701
14702     // private
14703     selectPrev : function(){
14704         var ct = this.store.getCount();
14705         if(ct > 0){
14706             if(this.selectedIndex == -1){
14707                 this.select(0);
14708             }else if(this.selectedIndex != 0){
14709                 this.select(this.selectedIndex-1);
14710             }
14711         }
14712     },
14713
14714     // private
14715     onKeyUp : function(e){
14716         if(this.editable !== false && !e.isSpecialKey()){
14717             this.lastKey = e.getKey();
14718             this.dqTask.delay(this.queryDelay);
14719         }
14720     },
14721
14722     // private
14723     validateBlur : function(){
14724         return !this.list || !this.list.isVisible();   
14725     },
14726
14727     // private
14728     initQuery : function(){
14729         
14730         var v = this.getRawValue();
14731         
14732         if(this.tickable && this.editable){
14733             v = this.tickableInputEl().getValue();
14734         }
14735         
14736         this.doQuery(v);
14737     },
14738
14739     // private
14740     doForce : function(){
14741         if(this.inputEl().dom.value.length > 0){
14742             this.inputEl().dom.value =
14743                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14744              
14745         }
14746     },
14747
14748     /**
14749      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14750      * query allowing the query action to be canceled if needed.
14751      * @param {String} query The SQL query to execute
14752      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14753      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14754      * saved in the current store (defaults to false)
14755      */
14756     doQuery : function(q, forceAll){
14757         
14758         if(q === undefined || q === null){
14759             q = '';
14760         }
14761         var qe = {
14762             query: q,
14763             forceAll: forceAll,
14764             combo: this,
14765             cancel:false
14766         };
14767         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14768             return false;
14769         }
14770         q = qe.query;
14771         
14772         forceAll = qe.forceAll;
14773         if(forceAll === true || (q.length >= this.minChars)){
14774             
14775             this.hasQuery = true;
14776             
14777             if(this.lastQuery != q || this.alwaysQuery){
14778                 this.lastQuery = q;
14779                 if(this.mode == 'local'){
14780                     this.selectedIndex = -1;
14781                     if(forceAll){
14782                         this.store.clearFilter();
14783                     }else{
14784                         
14785                         if(this.specialFilter){
14786                             this.fireEvent('specialfilter', this);
14787                             this.onLoad();
14788                             return;
14789                         }
14790                         
14791                         this.store.filter(this.displayField, q);
14792                     }
14793                     
14794                     this.store.fireEvent("datachanged", this.store);
14795                     
14796                     this.onLoad();
14797                     
14798                     
14799                 }else{
14800                     
14801                     this.store.baseParams[this.queryParam] = q;
14802                     
14803                     var options = {params : this.getParams(q)};
14804                     
14805                     if(this.loadNext){
14806                         options.add = true;
14807                         options.params.start = this.page * this.pageSize;
14808                     }
14809                     
14810                     this.store.load(options);
14811                     
14812                     /*
14813                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14814                      *  we should expand the list on onLoad
14815                      *  so command out it
14816                      */
14817 //                    this.expand();
14818                 }
14819             }else{
14820                 this.selectedIndex = -1;
14821                 this.onLoad();   
14822             }
14823         }
14824         
14825         this.loadNext = false;
14826     },
14827     
14828     // private
14829     getParams : function(q){
14830         var p = {};
14831         //p[this.queryParam] = q;
14832         
14833         if(this.pageSize){
14834             p.start = 0;
14835             p.limit = this.pageSize;
14836         }
14837         return p;
14838     },
14839
14840     /**
14841      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14842      */
14843     collapse : function(){
14844         if(!this.isExpanded()){
14845             return;
14846         }
14847         
14848         this.list.hide();
14849         
14850         this.hasFocus = false;
14851         
14852         if(this.tickable){
14853             this.okBtn.hide();
14854             this.cancelBtn.hide();
14855             this.trigger.show();
14856             
14857             if(this.editable){
14858                 this.tickableInputEl().dom.value = '';
14859                 this.tickableInputEl().blur();
14860             }
14861             
14862         }
14863         
14864         Roo.get(document).un('mousedown', this.collapseIf, this);
14865         Roo.get(document).un('mousewheel', this.collapseIf, this);
14866         if (!this.editable) {
14867             Roo.get(document).un('keydown', this.listKeyPress, this);
14868         }
14869         this.fireEvent('collapse', this);
14870         
14871         this.validate();
14872     },
14873
14874     // private
14875     collapseIf : function(e){
14876         var in_combo  = e.within(this.el);
14877         var in_list =  e.within(this.list);
14878         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14879         
14880         if (in_combo || in_list || is_list) {
14881             //e.stopPropagation();
14882             return;
14883         }
14884         
14885         if(this.tickable){
14886             this.onTickableFooterButtonClick(e, false, false);
14887         }
14888
14889         this.collapse();
14890         
14891     },
14892
14893     /**
14894      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14895      */
14896     expand : function(){
14897        
14898         if(this.isExpanded() || !this.hasFocus){
14899             return;
14900         }
14901         
14902         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14903         this.list.setWidth(lw);
14904         
14905         Roo.log('expand');
14906         
14907         this.list.show();
14908         
14909         this.restrictHeight();
14910         
14911         if(this.tickable){
14912             
14913             this.tickItems = Roo.apply([], this.item);
14914             
14915             this.okBtn.show();
14916             this.cancelBtn.show();
14917             this.trigger.hide();
14918             
14919             if(this.editable){
14920                 this.tickableInputEl().focus();
14921             }
14922             
14923         }
14924         
14925         Roo.get(document).on('mousedown', this.collapseIf, this);
14926         Roo.get(document).on('mousewheel', this.collapseIf, this);
14927         if (!this.editable) {
14928             Roo.get(document).on('keydown', this.listKeyPress, this);
14929         }
14930         
14931         this.fireEvent('expand', this);
14932     },
14933
14934     // private
14935     // Implements the default empty TriggerField.onTriggerClick function
14936     onTriggerClick : function(e)
14937     {
14938         Roo.log('trigger click');
14939         
14940         if(this.disabled || !this.triggerList){
14941             return;
14942         }
14943         
14944         this.page = 0;
14945         this.loadNext = false;
14946         
14947         if(this.isExpanded()){
14948             this.collapse();
14949             if (!this.blockFocus) {
14950                 this.inputEl().focus();
14951             }
14952             
14953         }else {
14954             this.hasFocus = true;
14955             if(this.triggerAction == 'all') {
14956                 this.doQuery(this.allQuery, true);
14957             } else {
14958                 this.doQuery(this.getRawValue());
14959             }
14960             if (!this.blockFocus) {
14961                 this.inputEl().focus();
14962             }
14963         }
14964     },
14965     
14966     onTickableTriggerClick : function(e)
14967     {
14968         if(this.disabled){
14969             return;
14970         }
14971         
14972         this.page = 0;
14973         this.loadNext = false;
14974         this.hasFocus = true;
14975         
14976         if(this.triggerAction == 'all') {
14977             this.doQuery(this.allQuery, true);
14978         } else {
14979             this.doQuery(this.getRawValue());
14980         }
14981     },
14982     
14983     onSearchFieldClick : function(e)
14984     {
14985         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14986             this.onTickableFooterButtonClick(e, false, false);
14987             return;
14988         }
14989         
14990         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14991             return;
14992         }
14993         
14994         this.page = 0;
14995         this.loadNext = false;
14996         this.hasFocus = true;
14997         
14998         if(this.triggerAction == 'all') {
14999             this.doQuery(this.allQuery, true);
15000         } else {
15001             this.doQuery(this.getRawValue());
15002         }
15003     },
15004     
15005     listKeyPress : function(e)
15006     {
15007         //Roo.log('listkeypress');
15008         // scroll to first matching element based on key pres..
15009         if (e.isSpecialKey()) {
15010             return false;
15011         }
15012         var k = String.fromCharCode(e.getKey()).toUpperCase();
15013         //Roo.log(k);
15014         var match  = false;
15015         var csel = this.view.getSelectedNodes();
15016         var cselitem = false;
15017         if (csel.length) {
15018             var ix = this.view.indexOf(csel[0]);
15019             cselitem  = this.store.getAt(ix);
15020             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15021                 cselitem = false;
15022             }
15023             
15024         }
15025         
15026         this.store.each(function(v) { 
15027             if (cselitem) {
15028                 // start at existing selection.
15029                 if (cselitem.id == v.id) {
15030                     cselitem = false;
15031                 }
15032                 return true;
15033             }
15034                 
15035             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15036                 match = this.store.indexOf(v);
15037                 return false;
15038             }
15039             return true;
15040         }, this);
15041         
15042         if (match === false) {
15043             return true; // no more action?
15044         }
15045         // scroll to?
15046         this.view.select(match);
15047         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15048         sn.scrollIntoView(sn.dom.parentNode, false);
15049     },
15050     
15051     onViewScroll : function(e, t){
15052         
15053         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){
15054             return;
15055         }
15056         
15057         this.hasQuery = true;
15058         
15059         this.loading = this.list.select('.loading', true).first();
15060         
15061         if(this.loading === null){
15062             this.list.createChild({
15063                 tag: 'div',
15064                 cls: 'loading roo-select2-more-results roo-select2-active',
15065                 html: 'Loading more results...'
15066             });
15067             
15068             this.loading = this.list.select('.loading', true).first();
15069             
15070             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15071             
15072             this.loading.hide();
15073         }
15074         
15075         this.loading.show();
15076         
15077         var _combo = this;
15078         
15079         this.page++;
15080         this.loadNext = true;
15081         
15082         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15083         
15084         return;
15085     },
15086     
15087     addItem : function(o)
15088     {   
15089         var dv = ''; // display value
15090         
15091         if (this.displayField) {
15092             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15093         } else {
15094             // this is an error condition!!!
15095             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15096         }
15097         
15098         if(!dv.length){
15099             return;
15100         }
15101         
15102         var choice = this.choices.createChild({
15103             tag: 'li',
15104             cls: 'roo-select2-search-choice',
15105             cn: [
15106                 {
15107                     tag: 'div',
15108                     html: dv
15109                 },
15110                 {
15111                     tag: 'a',
15112                     href: '#',
15113                     cls: 'roo-select2-search-choice-close fa fa-times',
15114                     tabindex: '-1'
15115                 }
15116             ]
15117             
15118         }, this.searchField);
15119         
15120         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15121         
15122         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15123         
15124         this.item.push(o);
15125         
15126         this.lastData = o;
15127         
15128         this.syncValue();
15129         
15130         this.inputEl().dom.value = '';
15131         
15132         this.validate();
15133     },
15134     
15135     onRemoveItem : function(e, _self, o)
15136     {
15137         e.preventDefault();
15138         
15139         this.lastItem = Roo.apply([], this.item);
15140         
15141         var index = this.item.indexOf(o.data) * 1;
15142         
15143         if( index < 0){
15144             Roo.log('not this item?!');
15145             return;
15146         }
15147         
15148         this.item.splice(index, 1);
15149         o.item.remove();
15150         
15151         this.syncValue();
15152         
15153         this.fireEvent('remove', this, e);
15154         
15155         this.validate();
15156         
15157     },
15158     
15159     syncValue : function()
15160     {
15161         if(!this.item.length){
15162             this.clearValue();
15163             return;
15164         }
15165             
15166         var value = [];
15167         var _this = this;
15168         Roo.each(this.item, function(i){
15169             if(_this.valueField){
15170                 value.push(i[_this.valueField]);
15171                 return;
15172             }
15173
15174             value.push(i);
15175         });
15176
15177         this.value = value.join(',');
15178
15179         if(this.hiddenField){
15180             this.hiddenField.dom.value = this.value;
15181         }
15182         
15183         this.store.fireEvent("datachanged", this.store);
15184         
15185         this.validate();
15186     },
15187     
15188     clearItem : function()
15189     {
15190         if(!this.multiple){
15191             return;
15192         }
15193         
15194         this.item = [];
15195         
15196         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15197            c.remove();
15198         });
15199         
15200         this.syncValue();
15201         
15202         this.validate();
15203         
15204         if(this.tickable && !Roo.isTouch){
15205             this.view.refresh();
15206         }
15207     },
15208     
15209     inputEl: function ()
15210     {
15211         if(Roo.isIOS && this.useNativeIOS){
15212             return this.el.select('select.roo-ios-select', true).first();
15213         }
15214         
15215         if(Roo.isTouch && this.mobileTouchView){
15216             return this.el.select('input.form-control',true).first();
15217         }
15218         
15219         if(this.tickable){
15220             return this.searchField;
15221         }
15222         
15223         return this.el.select('input.form-control',true).first();
15224     },
15225     
15226     onTickableFooterButtonClick : function(e, btn, el)
15227     {
15228         e.preventDefault();
15229         
15230         this.lastItem = Roo.apply([], this.item);
15231         
15232         if(btn && btn.name == 'cancel'){
15233             this.tickItems = Roo.apply([], this.item);
15234             this.collapse();
15235             return;
15236         }
15237         
15238         this.clearItem();
15239         
15240         var _this = this;
15241         
15242         Roo.each(this.tickItems, function(o){
15243             _this.addItem(o);
15244         });
15245         
15246         this.collapse();
15247         
15248     },
15249     
15250     validate : function()
15251     {
15252         if(this.getVisibilityEl().hasClass('hidden')){
15253             return true;
15254         }
15255         
15256         var v = this.getRawValue();
15257         
15258         if(this.multiple){
15259             v = this.getValue();
15260         }
15261         
15262         if(this.disabled || this.allowBlank || v.length){
15263             this.markValid();
15264             return true;
15265         }
15266         
15267         this.markInvalid();
15268         return false;
15269     },
15270     
15271     tickableInputEl : function()
15272     {
15273         if(!this.tickable || !this.editable){
15274             return this.inputEl();
15275         }
15276         
15277         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15278     },
15279     
15280     
15281     getAutoCreateTouchView : function()
15282     {
15283         var id = Roo.id();
15284         
15285         var cfg = {
15286             cls: 'form-group' //input-group
15287         };
15288         
15289         var input =  {
15290             tag: 'input',
15291             id : id,
15292             type : this.inputType,
15293             cls : 'form-control x-combo-noedit',
15294             autocomplete: 'new-password',
15295             placeholder : this.placeholder || '',
15296             readonly : true
15297         };
15298         
15299         if (this.name) {
15300             input.name = this.name;
15301         }
15302         
15303         if (this.size) {
15304             input.cls += ' input-' + this.size;
15305         }
15306         
15307         if (this.disabled) {
15308             input.disabled = true;
15309         }
15310         
15311         var inputblock = {
15312             cls : '',
15313             cn : [
15314                 input
15315             ]
15316         };
15317         
15318         if(this.before){
15319             inputblock.cls += ' input-group';
15320             
15321             inputblock.cn.unshift({
15322                 tag :'span',
15323                 cls : 'input-group-addon input-group-prepend input-group-text',
15324                 html : this.before
15325             });
15326         }
15327         
15328         if(this.removable && !this.multiple){
15329             inputblock.cls += ' roo-removable';
15330             
15331             inputblock.cn.push({
15332                 tag: 'button',
15333                 html : 'x',
15334                 cls : 'roo-combo-removable-btn close'
15335             });
15336         }
15337
15338         if(this.hasFeedback && !this.allowBlank){
15339             
15340             inputblock.cls += ' has-feedback';
15341             
15342             inputblock.cn.push({
15343                 tag: 'span',
15344                 cls: 'glyphicon form-control-feedback'
15345             });
15346             
15347         }
15348         
15349         if (this.after) {
15350             
15351             inputblock.cls += (this.before) ? '' : ' input-group';
15352             
15353             inputblock.cn.push({
15354                 tag :'span',
15355                 cls : 'input-group-addon input-group-append input-group-text',
15356                 html : this.after
15357             });
15358         }
15359
15360         
15361         var ibwrap = inputblock;
15362         
15363         if(this.multiple){
15364             ibwrap = {
15365                 tag: 'ul',
15366                 cls: 'roo-select2-choices',
15367                 cn:[
15368                     {
15369                         tag: 'li',
15370                         cls: 'roo-select2-search-field',
15371                         cn: [
15372
15373                             inputblock
15374                         ]
15375                     }
15376                 ]
15377             };
15378         
15379             
15380         }
15381         
15382         var combobox = {
15383             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15384             cn: [
15385                 {
15386                     tag: 'input',
15387                     type : 'hidden',
15388                     cls: 'form-hidden-field'
15389                 },
15390                 ibwrap
15391             ]
15392         };
15393         
15394         if(!this.multiple && this.showToggleBtn){
15395             
15396             var caret = {
15397                 cls: 'caret'
15398             };
15399             
15400             if (this.caret != false) {
15401                 caret = {
15402                      tag: 'i',
15403                      cls: 'fa fa-' + this.caret
15404                 };
15405                 
15406             }
15407             
15408             combobox.cn.push({
15409                 tag :'span',
15410                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15411                 cn : [
15412                     Roo.bootstrap.version == 3 ? caret : '',
15413                     {
15414                         tag: 'span',
15415                         cls: 'combobox-clear',
15416                         cn  : [
15417                             {
15418                                 tag : 'i',
15419                                 cls: 'icon-remove'
15420                             }
15421                         ]
15422                     }
15423                 ]
15424
15425             })
15426         }
15427         
15428         if(this.multiple){
15429             combobox.cls += ' roo-select2-container-multi';
15430         }
15431         
15432         var align = this.labelAlign || this.parentLabelAlign();
15433         
15434         if (align ==='left' && this.fieldLabel.length) {
15435
15436             cfg.cn = [
15437                 {
15438                    tag : 'i',
15439                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15440                    tooltip : 'This field is required'
15441                 },
15442                 {
15443                     tag: 'label',
15444                     cls : 'control-label col-form-label',
15445                     html : this.fieldLabel
15446
15447                 },
15448                 {
15449                     cls : '', 
15450                     cn: [
15451                         combobox
15452                     ]
15453                 }
15454             ];
15455             
15456             var labelCfg = cfg.cn[1];
15457             var contentCfg = cfg.cn[2];
15458             
15459
15460             if(this.indicatorpos == 'right'){
15461                 cfg.cn = [
15462                     {
15463                         tag: 'label',
15464                         'for' :  id,
15465                         cls : 'control-label col-form-label',
15466                         cn : [
15467                             {
15468                                 tag : 'span',
15469                                 html : this.fieldLabel
15470                             },
15471                             {
15472                                 tag : 'i',
15473                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15474                                 tooltip : 'This field is required'
15475                             }
15476                         ]
15477                     },
15478                     {
15479                         cls : "",
15480                         cn: [
15481                             combobox
15482                         ]
15483                     }
15484
15485                 ];
15486                 
15487                 labelCfg = cfg.cn[0];
15488                 contentCfg = cfg.cn[1];
15489             }
15490             
15491            
15492             
15493             if(this.labelWidth > 12){
15494                 labelCfg.style = "width: " + this.labelWidth + 'px';
15495             }
15496             
15497             if(this.labelWidth < 13 && this.labelmd == 0){
15498                 this.labelmd = this.labelWidth;
15499             }
15500             
15501             if(this.labellg > 0){
15502                 labelCfg.cls += ' col-lg-' + this.labellg;
15503                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15504             }
15505             
15506             if(this.labelmd > 0){
15507                 labelCfg.cls += ' col-md-' + this.labelmd;
15508                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15509             }
15510             
15511             if(this.labelsm > 0){
15512                 labelCfg.cls += ' col-sm-' + this.labelsm;
15513                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15514             }
15515             
15516             if(this.labelxs > 0){
15517                 labelCfg.cls += ' col-xs-' + this.labelxs;
15518                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15519             }
15520                 
15521                 
15522         } else if ( this.fieldLabel.length) {
15523             cfg.cn = [
15524                 {
15525                    tag : 'i',
15526                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15527                    tooltip : 'This field is required'
15528                 },
15529                 {
15530                     tag: 'label',
15531                     cls : 'control-label',
15532                     html : this.fieldLabel
15533
15534                 },
15535                 {
15536                     cls : '', 
15537                     cn: [
15538                         combobox
15539                     ]
15540                 }
15541             ];
15542             
15543             if(this.indicatorpos == 'right'){
15544                 cfg.cn = [
15545                     {
15546                         tag: 'label',
15547                         cls : 'control-label',
15548                         html : this.fieldLabel,
15549                         cn : [
15550                             {
15551                                tag : 'i',
15552                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15553                                tooltip : 'This field is required'
15554                             }
15555                         ]
15556                     },
15557                     {
15558                         cls : '', 
15559                         cn: [
15560                             combobox
15561                         ]
15562                     }
15563                 ];
15564             }
15565         } else {
15566             cfg.cn = combobox;    
15567         }
15568         
15569         
15570         var settings = this;
15571         
15572         ['xs','sm','md','lg'].map(function(size){
15573             if (settings[size]) {
15574                 cfg.cls += ' col-' + size + '-' + settings[size];
15575             }
15576         });
15577         
15578         return cfg;
15579     },
15580     
15581     initTouchView : function()
15582     {
15583         this.renderTouchView();
15584         
15585         this.touchViewEl.on('scroll', function(){
15586             this.el.dom.scrollTop = 0;
15587         }, this);
15588         
15589         this.originalValue = this.getValue();
15590         
15591         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15592         
15593         this.inputEl().on("click", this.showTouchView, this);
15594         if (this.triggerEl) {
15595             this.triggerEl.on("click", this.showTouchView, this);
15596         }
15597         
15598         
15599         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15600         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15601         
15602         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15603         
15604         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15605         this.store.on('load', this.onTouchViewLoad, this);
15606         this.store.on('loadexception', this.onTouchViewLoadException, this);
15607         
15608         if(this.hiddenName){
15609             
15610             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15611             
15612             this.hiddenField.dom.value =
15613                 this.hiddenValue !== undefined ? this.hiddenValue :
15614                 this.value !== undefined ? this.value : '';
15615         
15616             this.el.dom.removeAttribute('name');
15617             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15618         }
15619         
15620         if(this.multiple){
15621             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15622             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15623         }
15624         
15625         if(this.removable && !this.multiple){
15626             var close = this.closeTriggerEl();
15627             if(close){
15628                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15629                 close.on('click', this.removeBtnClick, this, close);
15630             }
15631         }
15632         /*
15633          * fix the bug in Safari iOS8
15634          */
15635         this.inputEl().on("focus", function(e){
15636             document.activeElement.blur();
15637         }, this);
15638         
15639         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15640         
15641         return;
15642         
15643         
15644     },
15645     
15646     renderTouchView : function()
15647     {
15648         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15649         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15650         
15651         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15652         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15653         
15654         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15655         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15656         this.touchViewBodyEl.setStyle('overflow', 'auto');
15657         
15658         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15659         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15660         
15661         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15662         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15663         
15664     },
15665     
15666     showTouchView : function()
15667     {
15668         if(this.disabled){
15669             return;
15670         }
15671         
15672         this.touchViewHeaderEl.hide();
15673
15674         if(this.modalTitle.length){
15675             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15676             this.touchViewHeaderEl.show();
15677         }
15678
15679         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15680         this.touchViewEl.show();
15681
15682         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15683         
15684         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15685         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15686
15687         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15688
15689         if(this.modalTitle.length){
15690             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15691         }
15692         
15693         this.touchViewBodyEl.setHeight(bodyHeight);
15694
15695         if(this.animate){
15696             var _this = this;
15697             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15698         }else{
15699             this.touchViewEl.addClass('in');
15700         }
15701         
15702         if(this._touchViewMask){
15703             Roo.get(document.body).addClass("x-body-masked");
15704             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15705             this._touchViewMask.setStyle('z-index', 10000);
15706             this._touchViewMask.addClass('show');
15707         }
15708         
15709         this.doTouchViewQuery();
15710         
15711     },
15712     
15713     hideTouchView : function()
15714     {
15715         this.touchViewEl.removeClass('in');
15716
15717         if(this.animate){
15718             var _this = this;
15719             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15720         }else{
15721             this.touchViewEl.setStyle('display', 'none');
15722         }
15723         
15724         if(this._touchViewMask){
15725             this._touchViewMask.removeClass('show');
15726             Roo.get(document.body).removeClass("x-body-masked");
15727         }
15728     },
15729     
15730     setTouchViewValue : function()
15731     {
15732         if(this.multiple){
15733             this.clearItem();
15734         
15735             var _this = this;
15736
15737             Roo.each(this.tickItems, function(o){
15738                 this.addItem(o);
15739             }, this);
15740         }
15741         
15742         this.hideTouchView();
15743     },
15744     
15745     doTouchViewQuery : function()
15746     {
15747         var qe = {
15748             query: '',
15749             forceAll: true,
15750             combo: this,
15751             cancel:false
15752         };
15753         
15754         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15755             return false;
15756         }
15757         
15758         if(!this.alwaysQuery || this.mode == 'local'){
15759             this.onTouchViewLoad();
15760             return;
15761         }
15762         
15763         this.store.load();
15764     },
15765     
15766     onTouchViewBeforeLoad : function(combo,opts)
15767     {
15768         return;
15769     },
15770
15771     // private
15772     onTouchViewLoad : function()
15773     {
15774         if(this.store.getCount() < 1){
15775             this.onTouchViewEmptyResults();
15776             return;
15777         }
15778         
15779         this.clearTouchView();
15780         
15781         var rawValue = this.getRawValue();
15782         
15783         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15784         
15785         this.tickItems = [];
15786         
15787         this.store.data.each(function(d, rowIndex){
15788             var row = this.touchViewListGroup.createChild(template);
15789             
15790             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15791                 row.addClass(d.data.cls);
15792             }
15793             
15794             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15795                 var cfg = {
15796                     data : d.data,
15797                     html : d.data[this.displayField]
15798                 };
15799                 
15800                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15801                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15802                 }
15803             }
15804             row.removeClass('selected');
15805             if(!this.multiple && this.valueField &&
15806                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15807             {
15808                 // radio buttons..
15809                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15810                 row.addClass('selected');
15811             }
15812             
15813             if(this.multiple && this.valueField &&
15814                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15815             {
15816                 
15817                 // checkboxes...
15818                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15819                 this.tickItems.push(d.data);
15820             }
15821             
15822             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15823             
15824         }, this);
15825         
15826         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15827         
15828         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15829
15830         if(this.modalTitle.length){
15831             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15832         }
15833
15834         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15835         
15836         if(this.mobile_restrict_height && listHeight < bodyHeight){
15837             this.touchViewBodyEl.setHeight(listHeight);
15838         }
15839         
15840         var _this = this;
15841         
15842         if(firstChecked && listHeight > bodyHeight){
15843             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15844         }
15845         
15846     },
15847     
15848     onTouchViewLoadException : function()
15849     {
15850         this.hideTouchView();
15851     },
15852     
15853     onTouchViewEmptyResults : function()
15854     {
15855         this.clearTouchView();
15856         
15857         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15858         
15859         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15860         
15861     },
15862     
15863     clearTouchView : function()
15864     {
15865         this.touchViewListGroup.dom.innerHTML = '';
15866     },
15867     
15868     onTouchViewClick : function(e, el, o)
15869     {
15870         e.preventDefault();
15871         
15872         var row = o.row;
15873         var rowIndex = o.rowIndex;
15874         
15875         var r = this.store.getAt(rowIndex);
15876         
15877         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15878             
15879             if(!this.multiple){
15880                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15881                     c.dom.removeAttribute('checked');
15882                 }, this);
15883
15884                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15885
15886                 this.setFromData(r.data);
15887
15888                 var close = this.closeTriggerEl();
15889
15890                 if(close){
15891                     close.show();
15892                 }
15893
15894                 this.hideTouchView();
15895
15896                 this.fireEvent('select', this, r, rowIndex);
15897
15898                 return;
15899             }
15900
15901             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15902                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15903                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15904                 return;
15905             }
15906
15907             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15908             this.addItem(r.data);
15909             this.tickItems.push(r.data);
15910         }
15911     },
15912     
15913     getAutoCreateNativeIOS : function()
15914     {
15915         var cfg = {
15916             cls: 'form-group' //input-group,
15917         };
15918         
15919         var combobox =  {
15920             tag: 'select',
15921             cls : 'roo-ios-select'
15922         };
15923         
15924         if (this.name) {
15925             combobox.name = this.name;
15926         }
15927         
15928         if (this.disabled) {
15929             combobox.disabled = true;
15930         }
15931         
15932         var settings = this;
15933         
15934         ['xs','sm','md','lg'].map(function(size){
15935             if (settings[size]) {
15936                 cfg.cls += ' col-' + size + '-' + settings[size];
15937             }
15938         });
15939         
15940         cfg.cn = combobox;
15941         
15942         return cfg;
15943         
15944     },
15945     
15946     initIOSView : function()
15947     {
15948         this.store.on('load', this.onIOSViewLoad, this);
15949         
15950         return;
15951     },
15952     
15953     onIOSViewLoad : function()
15954     {
15955         if(this.store.getCount() < 1){
15956             return;
15957         }
15958         
15959         this.clearIOSView();
15960         
15961         if(this.allowBlank) {
15962             
15963             var default_text = '-- SELECT --';
15964             
15965             if(this.placeholder.length){
15966                 default_text = this.placeholder;
15967             }
15968             
15969             if(this.emptyTitle.length){
15970                 default_text += ' - ' + this.emptyTitle + ' -';
15971             }
15972             
15973             var opt = this.inputEl().createChild({
15974                 tag: 'option',
15975                 value : 0,
15976                 html : default_text
15977             });
15978             
15979             var o = {};
15980             o[this.valueField] = 0;
15981             o[this.displayField] = default_text;
15982             
15983             this.ios_options.push({
15984                 data : o,
15985                 el : opt
15986             });
15987             
15988         }
15989         
15990         this.store.data.each(function(d, rowIndex){
15991             
15992             var html = '';
15993             
15994             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15995                 html = d.data[this.displayField];
15996             }
15997             
15998             var value = '';
15999             
16000             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16001                 value = d.data[this.valueField];
16002             }
16003             
16004             var option = {
16005                 tag: 'option',
16006                 value : value,
16007                 html : html
16008             };
16009             
16010             if(this.value == d.data[this.valueField]){
16011                 option['selected'] = true;
16012             }
16013             
16014             var opt = this.inputEl().createChild(option);
16015             
16016             this.ios_options.push({
16017                 data : d.data,
16018                 el : opt
16019             });
16020             
16021         }, this);
16022         
16023         this.inputEl().on('change', function(){
16024            this.fireEvent('select', this);
16025         }, this);
16026         
16027     },
16028     
16029     clearIOSView: function()
16030     {
16031         this.inputEl().dom.innerHTML = '';
16032         
16033         this.ios_options = [];
16034     },
16035     
16036     setIOSValue: function(v)
16037     {
16038         this.value = v;
16039         
16040         if(!this.ios_options){
16041             return;
16042         }
16043         
16044         Roo.each(this.ios_options, function(opts){
16045            
16046            opts.el.dom.removeAttribute('selected');
16047            
16048            if(opts.data[this.valueField] != v){
16049                return;
16050            }
16051            
16052            opts.el.dom.setAttribute('selected', true);
16053            
16054         }, this);
16055     }
16056
16057     /** 
16058     * @cfg {Boolean} grow 
16059     * @hide 
16060     */
16061     /** 
16062     * @cfg {Number} growMin 
16063     * @hide 
16064     */
16065     /** 
16066     * @cfg {Number} growMax 
16067     * @hide 
16068     */
16069     /**
16070      * @hide
16071      * @method autoSize
16072      */
16073 });
16074
16075 Roo.apply(Roo.bootstrap.ComboBox,  {
16076     
16077     header : {
16078         tag: 'div',
16079         cls: 'modal-header',
16080         cn: [
16081             {
16082                 tag: 'h4',
16083                 cls: 'modal-title'
16084             }
16085         ]
16086     },
16087     
16088     body : {
16089         tag: 'div',
16090         cls: 'modal-body',
16091         cn: [
16092             {
16093                 tag: 'ul',
16094                 cls: 'list-group'
16095             }
16096         ]
16097     },
16098     
16099     listItemRadio : {
16100         tag: 'li',
16101         cls: 'list-group-item',
16102         cn: [
16103             {
16104                 tag: 'span',
16105                 cls: 'roo-combobox-list-group-item-value'
16106             },
16107             {
16108                 tag: 'div',
16109                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16110                 cn: [
16111                     {
16112                         tag: 'input',
16113                         type: 'radio'
16114                     },
16115                     {
16116                         tag: 'label'
16117                     }
16118                 ]
16119             }
16120         ]
16121     },
16122     
16123     listItemCheckbox : {
16124         tag: 'li',
16125         cls: 'list-group-item',
16126         cn: [
16127             {
16128                 tag: 'span',
16129                 cls: 'roo-combobox-list-group-item-value'
16130             },
16131             {
16132                 tag: 'div',
16133                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16134                 cn: [
16135                     {
16136                         tag: 'input',
16137                         type: 'checkbox'
16138                     },
16139                     {
16140                         tag: 'label'
16141                     }
16142                 ]
16143             }
16144         ]
16145     },
16146     
16147     emptyResult : {
16148         tag: 'div',
16149         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16150     },
16151     
16152     footer : {
16153         tag: 'div',
16154         cls: 'modal-footer',
16155         cn: [
16156             {
16157                 tag: 'div',
16158                 cls: 'row',
16159                 cn: [
16160                     {
16161                         tag: 'div',
16162                         cls: 'col-xs-6 text-left',
16163                         cn: {
16164                             tag: 'button',
16165                             cls: 'btn btn-danger roo-touch-view-cancel',
16166                             html: 'Cancel'
16167                         }
16168                     },
16169                     {
16170                         tag: 'div',
16171                         cls: 'col-xs-6 text-right',
16172                         cn: {
16173                             tag: 'button',
16174                             cls: 'btn btn-success roo-touch-view-ok',
16175                             html: 'OK'
16176                         }
16177                     }
16178                 ]
16179             }
16180         ]
16181         
16182     }
16183 });
16184
16185 Roo.apply(Roo.bootstrap.ComboBox,  {
16186     
16187     touchViewTemplate : {
16188         tag: 'div',
16189         cls: 'modal fade roo-combobox-touch-view',
16190         cn: [
16191             {
16192                 tag: 'div',
16193                 cls: 'modal-dialog',
16194                 style : 'position:fixed', // we have to fix position....
16195                 cn: [
16196                     {
16197                         tag: 'div',
16198                         cls: 'modal-content',
16199                         cn: [
16200                             Roo.bootstrap.ComboBox.header,
16201                             Roo.bootstrap.ComboBox.body,
16202                             Roo.bootstrap.ComboBox.footer
16203                         ]
16204                     }
16205                 ]
16206             }
16207         ]
16208     }
16209 });/*
16210  * Based on:
16211  * Ext JS Library 1.1.1
16212  * Copyright(c) 2006-2007, Ext JS, LLC.
16213  *
16214  * Originally Released Under LGPL - original licence link has changed is not relivant.
16215  *
16216  * Fork - LGPL
16217  * <script type="text/javascript">
16218  */
16219
16220 /**
16221  * @class Roo.View
16222  * @extends Roo.util.Observable
16223  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16224  * This class also supports single and multi selection modes. <br>
16225  * Create a data model bound view:
16226  <pre><code>
16227  var store = new Roo.data.Store(...);
16228
16229  var view = new Roo.View({
16230     el : "my-element",
16231     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16232  
16233     singleSelect: true,
16234     selectedClass: "ydataview-selected",
16235     store: store
16236  });
16237
16238  // listen for node click?
16239  view.on("click", function(vw, index, node, e){
16240  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16241  });
16242
16243  // load XML data
16244  dataModel.load("foobar.xml");
16245  </code></pre>
16246  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16247  * <br><br>
16248  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16249  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16250  * 
16251  * Note: old style constructor is still suported (container, template, config)
16252  * 
16253  * @constructor
16254  * Create a new View
16255  * @param {Object} config The config object
16256  * 
16257  */
16258 Roo.View = function(config, depreciated_tpl, depreciated_config){
16259     
16260     this.parent = false;
16261     
16262     if (typeof(depreciated_tpl) == 'undefined') {
16263         // new way.. - universal constructor.
16264         Roo.apply(this, config);
16265         this.el  = Roo.get(this.el);
16266     } else {
16267         // old format..
16268         this.el  = Roo.get(config);
16269         this.tpl = depreciated_tpl;
16270         Roo.apply(this, depreciated_config);
16271     }
16272     this.wrapEl  = this.el.wrap().wrap();
16273     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16274     
16275     
16276     if(typeof(this.tpl) == "string"){
16277         this.tpl = new Roo.Template(this.tpl);
16278     } else {
16279         // support xtype ctors..
16280         this.tpl = new Roo.factory(this.tpl, Roo);
16281     }
16282     
16283     
16284     this.tpl.compile();
16285     
16286     /** @private */
16287     this.addEvents({
16288         /**
16289          * @event beforeclick
16290          * Fires before a click is processed. Returns false to cancel the default action.
16291          * @param {Roo.View} this
16292          * @param {Number} index The index of the target node
16293          * @param {HTMLElement} node The target node
16294          * @param {Roo.EventObject} e The raw event object
16295          */
16296             "beforeclick" : true,
16297         /**
16298          * @event click
16299          * Fires when a template node is clicked.
16300          * @param {Roo.View} this
16301          * @param {Number} index The index of the target node
16302          * @param {HTMLElement} node The target node
16303          * @param {Roo.EventObject} e The raw event object
16304          */
16305             "click" : true,
16306         /**
16307          * @event dblclick
16308          * Fires when a template node is double clicked.
16309          * @param {Roo.View} this
16310          * @param {Number} index The index of the target node
16311          * @param {HTMLElement} node The target node
16312          * @param {Roo.EventObject} e The raw event object
16313          */
16314             "dblclick" : true,
16315         /**
16316          * @event contextmenu
16317          * Fires when a template node is right clicked.
16318          * @param {Roo.View} this
16319          * @param {Number} index The index of the target node
16320          * @param {HTMLElement} node The target node
16321          * @param {Roo.EventObject} e The raw event object
16322          */
16323             "contextmenu" : true,
16324         /**
16325          * @event selectionchange
16326          * Fires when the selected nodes change.
16327          * @param {Roo.View} this
16328          * @param {Array} selections Array of the selected nodes
16329          */
16330             "selectionchange" : true,
16331     
16332         /**
16333          * @event beforeselect
16334          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16335          * @param {Roo.View} this
16336          * @param {HTMLElement} node The node to be selected
16337          * @param {Array} selections Array of currently selected nodes
16338          */
16339             "beforeselect" : true,
16340         /**
16341          * @event preparedata
16342          * Fires on every row to render, to allow you to change the data.
16343          * @param {Roo.View} this
16344          * @param {Object} data to be rendered (change this)
16345          */
16346           "preparedata" : true
16347           
16348           
16349         });
16350
16351
16352
16353     this.el.on({
16354         "click": this.onClick,
16355         "dblclick": this.onDblClick,
16356         "contextmenu": this.onContextMenu,
16357         scope:this
16358     });
16359
16360     this.selections = [];
16361     this.nodes = [];
16362     this.cmp = new Roo.CompositeElementLite([]);
16363     if(this.store){
16364         this.store = Roo.factory(this.store, Roo.data);
16365         this.setStore(this.store, true);
16366     }
16367     
16368     if ( this.footer && this.footer.xtype) {
16369            
16370          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16371         
16372         this.footer.dataSource = this.store;
16373         this.footer.container = fctr;
16374         this.footer = Roo.factory(this.footer, Roo);
16375         fctr.insertFirst(this.el);
16376         
16377         // this is a bit insane - as the paging toolbar seems to detach the el..
16378 //        dom.parentNode.parentNode.parentNode
16379          // they get detached?
16380     }
16381     
16382     
16383     Roo.View.superclass.constructor.call(this);
16384     
16385     
16386 };
16387
16388 Roo.extend(Roo.View, Roo.util.Observable, {
16389     
16390      /**
16391      * @cfg {Roo.data.Store} store Data store to load data from.
16392      */
16393     store : false,
16394     
16395     /**
16396      * @cfg {String|Roo.Element} el The container element.
16397      */
16398     el : '',
16399     
16400     /**
16401      * @cfg {String|Roo.Template} tpl The template used by this View 
16402      */
16403     tpl : false,
16404     /**
16405      * @cfg {String} dataName the named area of the template to use as the data area
16406      *                          Works with domtemplates roo-name="name"
16407      */
16408     dataName: false,
16409     /**
16410      * @cfg {String} selectedClass The css class to add to selected nodes
16411      */
16412     selectedClass : "x-view-selected",
16413      /**
16414      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16415      */
16416     emptyText : "",
16417     
16418     /**
16419      * @cfg {String} text to display on mask (default Loading)
16420      */
16421     mask : false,
16422     /**
16423      * @cfg {Boolean} multiSelect Allow multiple selection
16424      */
16425     multiSelect : false,
16426     /**
16427      * @cfg {Boolean} singleSelect Allow single selection
16428      */
16429     singleSelect:  false,
16430     
16431     /**
16432      * @cfg {Boolean} toggleSelect - selecting 
16433      */
16434     toggleSelect : false,
16435     
16436     /**
16437      * @cfg {Boolean} tickable - selecting 
16438      */
16439     tickable : false,
16440     
16441     /**
16442      * Returns the element this view is bound to.
16443      * @return {Roo.Element}
16444      */
16445     getEl : function(){
16446         return this.wrapEl;
16447     },
16448     
16449     
16450
16451     /**
16452      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16453      */
16454     refresh : function(){
16455         //Roo.log('refresh');
16456         var t = this.tpl;
16457         
16458         // if we are using something like 'domtemplate', then
16459         // the what gets used is:
16460         // t.applySubtemplate(NAME, data, wrapping data..)
16461         // the outer template then get' applied with
16462         //     the store 'extra data'
16463         // and the body get's added to the
16464         //      roo-name="data" node?
16465         //      <span class='roo-tpl-{name}'></span> ?????
16466         
16467         
16468         
16469         this.clearSelections();
16470         this.el.update("");
16471         var html = [];
16472         var records = this.store.getRange();
16473         if(records.length < 1) {
16474             
16475             // is this valid??  = should it render a template??
16476             
16477             this.el.update(this.emptyText);
16478             return;
16479         }
16480         var el = this.el;
16481         if (this.dataName) {
16482             this.el.update(t.apply(this.store.meta)); //????
16483             el = this.el.child('.roo-tpl-' + this.dataName);
16484         }
16485         
16486         for(var i = 0, len = records.length; i < len; i++){
16487             var data = this.prepareData(records[i].data, i, records[i]);
16488             this.fireEvent("preparedata", this, data, i, records[i]);
16489             
16490             var d = Roo.apply({}, data);
16491             
16492             if(this.tickable){
16493                 Roo.apply(d, {'roo-id' : Roo.id()});
16494                 
16495                 var _this = this;
16496             
16497                 Roo.each(this.parent.item, function(item){
16498                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16499                         return;
16500                     }
16501                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16502                 });
16503             }
16504             
16505             html[html.length] = Roo.util.Format.trim(
16506                 this.dataName ?
16507                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16508                     t.apply(d)
16509             );
16510         }
16511         
16512         
16513         
16514         el.update(html.join(""));
16515         this.nodes = el.dom.childNodes;
16516         this.updateIndexes(0);
16517     },
16518     
16519
16520     /**
16521      * Function to override to reformat the data that is sent to
16522      * the template for each node.
16523      * DEPRICATED - use the preparedata event handler.
16524      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16525      * a JSON object for an UpdateManager bound view).
16526      */
16527     prepareData : function(data, index, record)
16528     {
16529         this.fireEvent("preparedata", this, data, index, record);
16530         return data;
16531     },
16532
16533     onUpdate : function(ds, record){
16534         // Roo.log('on update');   
16535         this.clearSelections();
16536         var index = this.store.indexOf(record);
16537         var n = this.nodes[index];
16538         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16539         n.parentNode.removeChild(n);
16540         this.updateIndexes(index, index);
16541     },
16542
16543     
16544     
16545 // --------- FIXME     
16546     onAdd : function(ds, records, index)
16547     {
16548         //Roo.log(['on Add', ds, records, index] );        
16549         this.clearSelections();
16550         if(this.nodes.length == 0){
16551             this.refresh();
16552             return;
16553         }
16554         var n = this.nodes[index];
16555         for(var i = 0, len = records.length; i < len; i++){
16556             var d = this.prepareData(records[i].data, i, records[i]);
16557             if(n){
16558                 this.tpl.insertBefore(n, d);
16559             }else{
16560                 
16561                 this.tpl.append(this.el, d);
16562             }
16563         }
16564         this.updateIndexes(index);
16565     },
16566
16567     onRemove : function(ds, record, index){
16568        // Roo.log('onRemove');
16569         this.clearSelections();
16570         var el = this.dataName  ?
16571             this.el.child('.roo-tpl-' + this.dataName) :
16572             this.el; 
16573         
16574         el.dom.removeChild(this.nodes[index]);
16575         this.updateIndexes(index);
16576     },
16577
16578     /**
16579      * Refresh an individual node.
16580      * @param {Number} index
16581      */
16582     refreshNode : function(index){
16583         this.onUpdate(this.store, this.store.getAt(index));
16584     },
16585
16586     updateIndexes : function(startIndex, endIndex){
16587         var ns = this.nodes;
16588         startIndex = startIndex || 0;
16589         endIndex = endIndex || ns.length - 1;
16590         for(var i = startIndex; i <= endIndex; i++){
16591             ns[i].nodeIndex = i;
16592         }
16593     },
16594
16595     /**
16596      * Changes the data store this view uses and refresh the view.
16597      * @param {Store} store
16598      */
16599     setStore : function(store, initial){
16600         if(!initial && this.store){
16601             this.store.un("datachanged", this.refresh);
16602             this.store.un("add", this.onAdd);
16603             this.store.un("remove", this.onRemove);
16604             this.store.un("update", this.onUpdate);
16605             this.store.un("clear", this.refresh);
16606             this.store.un("beforeload", this.onBeforeLoad);
16607             this.store.un("load", this.onLoad);
16608             this.store.un("loadexception", this.onLoad);
16609         }
16610         if(store){
16611           
16612             store.on("datachanged", this.refresh, this);
16613             store.on("add", this.onAdd, this);
16614             store.on("remove", this.onRemove, this);
16615             store.on("update", this.onUpdate, this);
16616             store.on("clear", this.refresh, this);
16617             store.on("beforeload", this.onBeforeLoad, this);
16618             store.on("load", this.onLoad, this);
16619             store.on("loadexception", this.onLoad, this);
16620         }
16621         
16622         if(store){
16623             this.refresh();
16624         }
16625     },
16626     /**
16627      * onbeforeLoad - masks the loading area.
16628      *
16629      */
16630     onBeforeLoad : function(store,opts)
16631     {
16632          //Roo.log('onBeforeLoad');   
16633         if (!opts.add) {
16634             this.el.update("");
16635         }
16636         this.el.mask(this.mask ? this.mask : "Loading" ); 
16637     },
16638     onLoad : function ()
16639     {
16640         this.el.unmask();
16641     },
16642     
16643
16644     /**
16645      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16646      * @param {HTMLElement} node
16647      * @return {HTMLElement} The template node
16648      */
16649     findItemFromChild : function(node){
16650         var el = this.dataName  ?
16651             this.el.child('.roo-tpl-' + this.dataName,true) :
16652             this.el.dom; 
16653         
16654         if(!node || node.parentNode == el){
16655                     return node;
16656             }
16657             var p = node.parentNode;
16658             while(p && p != el){
16659             if(p.parentNode == el){
16660                 return p;
16661             }
16662             p = p.parentNode;
16663         }
16664             return null;
16665     },
16666
16667     /** @ignore */
16668     onClick : function(e){
16669         var item = this.findItemFromChild(e.getTarget());
16670         if(item){
16671             var index = this.indexOf(item);
16672             if(this.onItemClick(item, index, e) !== false){
16673                 this.fireEvent("click", this, index, item, e);
16674             }
16675         }else{
16676             this.clearSelections();
16677         }
16678     },
16679
16680     /** @ignore */
16681     onContextMenu : function(e){
16682         var item = this.findItemFromChild(e.getTarget());
16683         if(item){
16684             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16685         }
16686     },
16687
16688     /** @ignore */
16689     onDblClick : function(e){
16690         var item = this.findItemFromChild(e.getTarget());
16691         if(item){
16692             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16693         }
16694     },
16695
16696     onItemClick : function(item, index, e)
16697     {
16698         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16699             return false;
16700         }
16701         if (this.toggleSelect) {
16702             var m = this.isSelected(item) ? 'unselect' : 'select';
16703             //Roo.log(m);
16704             var _t = this;
16705             _t[m](item, true, false);
16706             return true;
16707         }
16708         if(this.multiSelect || this.singleSelect){
16709             if(this.multiSelect && e.shiftKey && this.lastSelection){
16710                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16711             }else{
16712                 this.select(item, this.multiSelect && e.ctrlKey);
16713                 this.lastSelection = item;
16714             }
16715             
16716             if(!this.tickable){
16717                 e.preventDefault();
16718             }
16719             
16720         }
16721         return true;
16722     },
16723
16724     /**
16725      * Get the number of selected nodes.
16726      * @return {Number}
16727      */
16728     getSelectionCount : function(){
16729         return this.selections.length;
16730     },
16731
16732     /**
16733      * Get the currently selected nodes.
16734      * @return {Array} An array of HTMLElements
16735      */
16736     getSelectedNodes : function(){
16737         return this.selections;
16738     },
16739
16740     /**
16741      * Get the indexes of the selected nodes.
16742      * @return {Array}
16743      */
16744     getSelectedIndexes : function(){
16745         var indexes = [], s = this.selections;
16746         for(var i = 0, len = s.length; i < len; i++){
16747             indexes.push(s[i].nodeIndex);
16748         }
16749         return indexes;
16750     },
16751
16752     /**
16753      * Clear all selections
16754      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16755      */
16756     clearSelections : function(suppressEvent){
16757         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16758             this.cmp.elements = this.selections;
16759             this.cmp.removeClass(this.selectedClass);
16760             this.selections = [];
16761             if(!suppressEvent){
16762                 this.fireEvent("selectionchange", this, this.selections);
16763             }
16764         }
16765     },
16766
16767     /**
16768      * Returns true if the passed node is selected
16769      * @param {HTMLElement/Number} node The node or node index
16770      * @return {Boolean}
16771      */
16772     isSelected : function(node){
16773         var s = this.selections;
16774         if(s.length < 1){
16775             return false;
16776         }
16777         node = this.getNode(node);
16778         return s.indexOf(node) !== -1;
16779     },
16780
16781     /**
16782      * Selects 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 to keep existing selections
16785      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16786      */
16787     select : function(nodeInfo, keepExisting, suppressEvent){
16788         if(nodeInfo instanceof Array){
16789             if(!keepExisting){
16790                 this.clearSelections(true);
16791             }
16792             for(var i = 0, len = nodeInfo.length; i < len; i++){
16793                 this.select(nodeInfo[i], true, true);
16794             }
16795             return;
16796         } 
16797         var node = this.getNode(nodeInfo);
16798         if(!node || this.isSelected(node)){
16799             return; // already selected.
16800         }
16801         if(!keepExisting){
16802             this.clearSelections(true);
16803         }
16804         
16805         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16806             Roo.fly(node).addClass(this.selectedClass);
16807             this.selections.push(node);
16808             if(!suppressEvent){
16809                 this.fireEvent("selectionchange", this, this.selections);
16810             }
16811         }
16812         
16813         
16814     },
16815       /**
16816      * Unselects nodes.
16817      * @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
16818      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16819      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16820      */
16821     unselect : function(nodeInfo, keepExisting, suppressEvent)
16822     {
16823         if(nodeInfo instanceof Array){
16824             Roo.each(this.selections, function(s) {
16825                 this.unselect(s, nodeInfo);
16826             }, this);
16827             return;
16828         }
16829         var node = this.getNode(nodeInfo);
16830         if(!node || !this.isSelected(node)){
16831             //Roo.log("not selected");
16832             return; // not selected.
16833         }
16834         // fireevent???
16835         var ns = [];
16836         Roo.each(this.selections, function(s) {
16837             if (s == node ) {
16838                 Roo.fly(node).removeClass(this.selectedClass);
16839
16840                 return;
16841             }
16842             ns.push(s);
16843         },this);
16844         
16845         this.selections= ns;
16846         this.fireEvent("selectionchange", this, this.selections);
16847     },
16848
16849     /**
16850      * Gets a template node.
16851      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16852      * @return {HTMLElement} The node or null if it wasn't found
16853      */
16854     getNode : function(nodeInfo){
16855         if(typeof nodeInfo == "string"){
16856             return document.getElementById(nodeInfo);
16857         }else if(typeof nodeInfo == "number"){
16858             return this.nodes[nodeInfo];
16859         }
16860         return nodeInfo;
16861     },
16862
16863     /**
16864      * Gets a range template nodes.
16865      * @param {Number} startIndex
16866      * @param {Number} endIndex
16867      * @return {Array} An array of nodes
16868      */
16869     getNodes : function(start, end){
16870         var ns = this.nodes;
16871         start = start || 0;
16872         end = typeof end == "undefined" ? ns.length - 1 : end;
16873         var nodes = [];
16874         if(start <= end){
16875             for(var i = start; i <= end; i++){
16876                 nodes.push(ns[i]);
16877             }
16878         } else{
16879             for(var i = start; i >= end; i--){
16880                 nodes.push(ns[i]);
16881             }
16882         }
16883         return nodes;
16884     },
16885
16886     /**
16887      * Finds the index of the passed node
16888      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16889      * @return {Number} The index of the node or -1
16890      */
16891     indexOf : function(node){
16892         node = this.getNode(node);
16893         if(typeof node.nodeIndex == "number"){
16894             return node.nodeIndex;
16895         }
16896         var ns = this.nodes;
16897         for(var i = 0, len = ns.length; i < len; i++){
16898             if(ns[i] == node){
16899                 return i;
16900             }
16901         }
16902         return -1;
16903     }
16904 });
16905 /*
16906  * - LGPL
16907  *
16908  * based on jquery fullcalendar
16909  * 
16910  */
16911
16912 Roo.bootstrap = Roo.bootstrap || {};
16913 /**
16914  * @class Roo.bootstrap.Calendar
16915  * @extends Roo.bootstrap.Component
16916  * Bootstrap Calendar class
16917  * @cfg {Boolean} loadMask (true|false) default false
16918  * @cfg {Object} header generate the user specific header of the calendar, default false
16919
16920  * @constructor
16921  * Create a new Container
16922  * @param {Object} config The config object
16923  */
16924
16925
16926
16927 Roo.bootstrap.Calendar = function(config){
16928     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16929      this.addEvents({
16930         /**
16931              * @event select
16932              * Fires when a date is selected
16933              * @param {DatePicker} this
16934              * @param {Date} date The selected date
16935              */
16936         'select': true,
16937         /**
16938              * @event monthchange
16939              * Fires when the displayed month changes 
16940              * @param {DatePicker} this
16941              * @param {Date} date The selected month
16942              */
16943         'monthchange': true,
16944         /**
16945              * @event evententer
16946              * Fires when mouse over an event
16947              * @param {Calendar} this
16948              * @param {event} Event
16949              */
16950         'evententer': true,
16951         /**
16952              * @event eventleave
16953              * Fires when the mouse leaves an
16954              * @param {Calendar} this
16955              * @param {event}
16956              */
16957         'eventleave': true,
16958         /**
16959              * @event eventclick
16960              * Fires when the mouse click an
16961              * @param {Calendar} this
16962              * @param {event}
16963              */
16964         'eventclick': true
16965         
16966     });
16967
16968 };
16969
16970 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16971     
16972      /**
16973      * @cfg {Number} startDay
16974      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16975      */
16976     startDay : 0,
16977     
16978     loadMask : false,
16979     
16980     header : false,
16981       
16982     getAutoCreate : function(){
16983         
16984         
16985         var fc_button = function(name, corner, style, content ) {
16986             return Roo.apply({},{
16987                 tag : 'span',
16988                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16989                          (corner.length ?
16990                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16991                             ''
16992                         ),
16993                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16994                 unselectable: 'on'
16995             });
16996         };
16997         
16998         var header = {};
16999         
17000         if(!this.header){
17001             header = {
17002                 tag : 'table',
17003                 cls : 'fc-header',
17004                 style : 'width:100%',
17005                 cn : [
17006                     {
17007                         tag: 'tr',
17008                         cn : [
17009                             {
17010                                 tag : 'td',
17011                                 cls : 'fc-header-left',
17012                                 cn : [
17013                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17014                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17015                                     { tag: 'span', cls: 'fc-header-space' },
17016                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17017
17018
17019                                 ]
17020                             },
17021
17022                             {
17023                                 tag : 'td',
17024                                 cls : 'fc-header-center',
17025                                 cn : [
17026                                     {
17027                                         tag: 'span',
17028                                         cls: 'fc-header-title',
17029                                         cn : {
17030                                             tag: 'H2',
17031                                             html : 'month / year'
17032                                         }
17033                                     }
17034
17035                                 ]
17036                             },
17037                             {
17038                                 tag : 'td',
17039                                 cls : 'fc-header-right',
17040                                 cn : [
17041                               /*      fc_button('month', 'left', '', 'month' ),
17042                                     fc_button('week', '', '', 'week' ),
17043                                     fc_button('day', 'right', '', 'day' )
17044                                 */    
17045
17046                                 ]
17047                             }
17048
17049                         ]
17050                     }
17051                 ]
17052             };
17053         }
17054         
17055         header = this.header;
17056         
17057        
17058         var cal_heads = function() {
17059             var ret = [];
17060             // fixme - handle this.
17061             
17062             for (var i =0; i < Date.dayNames.length; i++) {
17063                 var d = Date.dayNames[i];
17064                 ret.push({
17065                     tag: 'th',
17066                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17067                     html : d.substring(0,3)
17068                 });
17069                 
17070             }
17071             ret[0].cls += ' fc-first';
17072             ret[6].cls += ' fc-last';
17073             return ret;
17074         };
17075         var cal_cell = function(n) {
17076             return  {
17077                 tag: 'td',
17078                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17079                 cn : [
17080                     {
17081                         cn : [
17082                             {
17083                                 cls: 'fc-day-number',
17084                                 html: 'D'
17085                             },
17086                             {
17087                                 cls: 'fc-day-content',
17088                              
17089                                 cn : [
17090                                      {
17091                                         style: 'position: relative;' // height: 17px;
17092                                     }
17093                                 ]
17094                             }
17095                             
17096                             
17097                         ]
17098                     }
17099                 ]
17100                 
17101             }
17102         };
17103         var cal_rows = function() {
17104             
17105             var ret = [];
17106             for (var r = 0; r < 6; r++) {
17107                 var row= {
17108                     tag : 'tr',
17109                     cls : 'fc-week',
17110                     cn : []
17111                 };
17112                 
17113                 for (var i =0; i < Date.dayNames.length; i++) {
17114                     var d = Date.dayNames[i];
17115                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17116
17117                 }
17118                 row.cn[0].cls+=' fc-first';
17119                 row.cn[0].cn[0].style = 'min-height:90px';
17120                 row.cn[6].cls+=' fc-last';
17121                 ret.push(row);
17122                 
17123             }
17124             ret[0].cls += ' fc-first';
17125             ret[4].cls += ' fc-prev-last';
17126             ret[5].cls += ' fc-last';
17127             return ret;
17128             
17129         };
17130         
17131         var cal_table = {
17132             tag: 'table',
17133             cls: 'fc-border-separate',
17134             style : 'width:100%',
17135             cellspacing  : 0,
17136             cn : [
17137                 { 
17138                     tag: 'thead',
17139                     cn : [
17140                         { 
17141                             tag: 'tr',
17142                             cls : 'fc-first fc-last',
17143                             cn : cal_heads()
17144                         }
17145                     ]
17146                 },
17147                 { 
17148                     tag: 'tbody',
17149                     cn : cal_rows()
17150                 }
17151                   
17152             ]
17153         };
17154          
17155          var cfg = {
17156             cls : 'fc fc-ltr',
17157             cn : [
17158                 header,
17159                 {
17160                     cls : 'fc-content',
17161                     style : "position: relative;",
17162                     cn : [
17163                         {
17164                             cls : 'fc-view fc-view-month fc-grid',
17165                             style : 'position: relative',
17166                             unselectable : 'on',
17167                             cn : [
17168                                 {
17169                                     cls : 'fc-event-container',
17170                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17171                                 },
17172                                 cal_table
17173                             ]
17174                         }
17175                     ]
17176     
17177                 }
17178            ] 
17179             
17180         };
17181         
17182          
17183         
17184         return cfg;
17185     },
17186     
17187     
17188     initEvents : function()
17189     {
17190         if(!this.store){
17191             throw "can not find store for calendar";
17192         }
17193         
17194         var mark = {
17195             tag: "div",
17196             cls:"x-dlg-mask",
17197             style: "text-align:center",
17198             cn: [
17199                 {
17200                     tag: "div",
17201                     style: "background-color:white;width:50%;margin:250 auto",
17202                     cn: [
17203                         {
17204                             tag: "img",
17205                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17206                         },
17207                         {
17208                             tag: "span",
17209                             html: "Loading"
17210                         }
17211                         
17212                     ]
17213                 }
17214             ]
17215         };
17216         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17217         
17218         var size = this.el.select('.fc-content', true).first().getSize();
17219         this.maskEl.setSize(size.width, size.height);
17220         this.maskEl.enableDisplayMode("block");
17221         if(!this.loadMask){
17222             this.maskEl.hide();
17223         }
17224         
17225         this.store = Roo.factory(this.store, Roo.data);
17226         this.store.on('load', this.onLoad, this);
17227         this.store.on('beforeload', this.onBeforeLoad, this);
17228         
17229         this.resize();
17230         
17231         this.cells = this.el.select('.fc-day',true);
17232         //Roo.log(this.cells);
17233         this.textNodes = this.el.query('.fc-day-number');
17234         this.cells.addClassOnOver('fc-state-hover');
17235         
17236         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17237         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17238         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17239         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17240         
17241         this.on('monthchange', this.onMonthChange, this);
17242         
17243         this.update(new Date().clearTime());
17244     },
17245     
17246     resize : function() {
17247         var sz  = this.el.getSize();
17248         
17249         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17250         this.el.select('.fc-day-content div',true).setHeight(34);
17251     },
17252     
17253     
17254     // private
17255     showPrevMonth : function(e){
17256         this.update(this.activeDate.add("mo", -1));
17257     },
17258     showToday : function(e){
17259         this.update(new Date().clearTime());
17260     },
17261     // private
17262     showNextMonth : function(e){
17263         this.update(this.activeDate.add("mo", 1));
17264     },
17265
17266     // private
17267     showPrevYear : function(){
17268         this.update(this.activeDate.add("y", -1));
17269     },
17270
17271     // private
17272     showNextYear : function(){
17273         this.update(this.activeDate.add("y", 1));
17274     },
17275
17276     
17277    // private
17278     update : function(date)
17279     {
17280         var vd = this.activeDate;
17281         this.activeDate = date;
17282 //        if(vd && this.el){
17283 //            var t = date.getTime();
17284 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17285 //                Roo.log('using add remove');
17286 //                
17287 //                this.fireEvent('monthchange', this, date);
17288 //                
17289 //                this.cells.removeClass("fc-state-highlight");
17290 //                this.cells.each(function(c){
17291 //                   if(c.dateValue == t){
17292 //                       c.addClass("fc-state-highlight");
17293 //                       setTimeout(function(){
17294 //                            try{c.dom.firstChild.focus();}catch(e){}
17295 //                       }, 50);
17296 //                       return false;
17297 //                   }
17298 //                   return true;
17299 //                });
17300 //                return;
17301 //            }
17302 //        }
17303         
17304         var days = date.getDaysInMonth();
17305         
17306         var firstOfMonth = date.getFirstDateOfMonth();
17307         var startingPos = firstOfMonth.getDay()-this.startDay;
17308         
17309         if(startingPos < this.startDay){
17310             startingPos += 7;
17311         }
17312         
17313         var pm = date.add(Date.MONTH, -1);
17314         var prevStart = pm.getDaysInMonth()-startingPos;
17315 //        
17316         this.cells = this.el.select('.fc-day',true);
17317         this.textNodes = this.el.query('.fc-day-number');
17318         this.cells.addClassOnOver('fc-state-hover');
17319         
17320         var cells = this.cells.elements;
17321         var textEls = this.textNodes;
17322         
17323         Roo.each(cells, function(cell){
17324             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17325         });
17326         
17327         days += startingPos;
17328
17329         // convert everything to numbers so it's fast
17330         var day = 86400000;
17331         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17332         //Roo.log(d);
17333         //Roo.log(pm);
17334         //Roo.log(prevStart);
17335         
17336         var today = new Date().clearTime().getTime();
17337         var sel = date.clearTime().getTime();
17338         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17339         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17340         var ddMatch = this.disabledDatesRE;
17341         var ddText = this.disabledDatesText;
17342         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17343         var ddaysText = this.disabledDaysText;
17344         var format = this.format;
17345         
17346         var setCellClass = function(cal, cell){
17347             cell.row = 0;
17348             cell.events = [];
17349             cell.more = [];
17350             //Roo.log('set Cell Class');
17351             cell.title = "";
17352             var t = d.getTime();
17353             
17354             //Roo.log(d);
17355             
17356             cell.dateValue = t;
17357             if(t == today){
17358                 cell.className += " fc-today";
17359                 cell.className += " fc-state-highlight";
17360                 cell.title = cal.todayText;
17361             }
17362             if(t == sel){
17363                 // disable highlight in other month..
17364                 //cell.className += " fc-state-highlight";
17365                 
17366             }
17367             // disabling
17368             if(t < min) {
17369                 cell.className = " fc-state-disabled";
17370                 cell.title = cal.minText;
17371                 return;
17372             }
17373             if(t > max) {
17374                 cell.className = " fc-state-disabled";
17375                 cell.title = cal.maxText;
17376                 return;
17377             }
17378             if(ddays){
17379                 if(ddays.indexOf(d.getDay()) != -1){
17380                     cell.title = ddaysText;
17381                     cell.className = " fc-state-disabled";
17382                 }
17383             }
17384             if(ddMatch && format){
17385                 var fvalue = d.dateFormat(format);
17386                 if(ddMatch.test(fvalue)){
17387                     cell.title = ddText.replace("%0", fvalue);
17388                     cell.className = " fc-state-disabled";
17389                 }
17390             }
17391             
17392             if (!cell.initialClassName) {
17393                 cell.initialClassName = cell.dom.className;
17394             }
17395             
17396             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17397         };
17398
17399         var i = 0;
17400         
17401         for(; i < startingPos; i++) {
17402             textEls[i].innerHTML = (++prevStart);
17403             d.setDate(d.getDate()+1);
17404             
17405             cells[i].className = "fc-past fc-other-month";
17406             setCellClass(this, cells[i]);
17407         }
17408         
17409         var intDay = 0;
17410         
17411         for(; i < days; i++){
17412             intDay = i - startingPos + 1;
17413             textEls[i].innerHTML = (intDay);
17414             d.setDate(d.getDate()+1);
17415             
17416             cells[i].className = ''; // "x-date-active";
17417             setCellClass(this, cells[i]);
17418         }
17419         var extraDays = 0;
17420         
17421         for(; i < 42; i++) {
17422             textEls[i].innerHTML = (++extraDays);
17423             d.setDate(d.getDate()+1);
17424             
17425             cells[i].className = "fc-future fc-other-month";
17426             setCellClass(this, cells[i]);
17427         }
17428         
17429         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17430         
17431         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17432         
17433         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17434         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17435         
17436         if(totalRows != 6){
17437             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17438             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17439         }
17440         
17441         this.fireEvent('monthchange', this, date);
17442         
17443         
17444         /*
17445         if(!this.internalRender){
17446             var main = this.el.dom.firstChild;
17447             var w = main.offsetWidth;
17448             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17449             Roo.fly(main).setWidth(w);
17450             this.internalRender = true;
17451             // opera does not respect the auto grow header center column
17452             // then, after it gets a width opera refuses to recalculate
17453             // without a second pass
17454             if(Roo.isOpera && !this.secondPass){
17455                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17456                 this.secondPass = true;
17457                 this.update.defer(10, this, [date]);
17458             }
17459         }
17460         */
17461         
17462     },
17463     
17464     findCell : function(dt) {
17465         dt = dt.clearTime().getTime();
17466         var ret = false;
17467         this.cells.each(function(c){
17468             //Roo.log("check " +c.dateValue + '?=' + dt);
17469             if(c.dateValue == dt){
17470                 ret = c;
17471                 return false;
17472             }
17473             return true;
17474         });
17475         
17476         return ret;
17477     },
17478     
17479     findCells : function(ev) {
17480         var s = ev.start.clone().clearTime().getTime();
17481        // Roo.log(s);
17482         var e= ev.end.clone().clearTime().getTime();
17483        // Roo.log(e);
17484         var ret = [];
17485         this.cells.each(function(c){
17486              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17487             
17488             if(c.dateValue > e){
17489                 return ;
17490             }
17491             if(c.dateValue < s){
17492                 return ;
17493             }
17494             ret.push(c);
17495         });
17496         
17497         return ret;    
17498     },
17499     
17500 //    findBestRow: function(cells)
17501 //    {
17502 //        var ret = 0;
17503 //        
17504 //        for (var i =0 ; i < cells.length;i++) {
17505 //            ret  = Math.max(cells[i].rows || 0,ret);
17506 //        }
17507 //        return ret;
17508 //        
17509 //    },
17510     
17511     
17512     addItem : function(ev)
17513     {
17514         // look for vertical location slot in
17515         var cells = this.findCells(ev);
17516         
17517 //        ev.row = this.findBestRow(cells);
17518         
17519         // work out the location.
17520         
17521         var crow = false;
17522         var rows = [];
17523         for(var i =0; i < cells.length; i++) {
17524             
17525             cells[i].row = cells[0].row;
17526             
17527             if(i == 0){
17528                 cells[i].row = cells[i].row + 1;
17529             }
17530             
17531             if (!crow) {
17532                 crow = {
17533                     start : cells[i],
17534                     end :  cells[i]
17535                 };
17536                 continue;
17537             }
17538             if (crow.start.getY() == cells[i].getY()) {
17539                 // on same row.
17540                 crow.end = cells[i];
17541                 continue;
17542             }
17543             // different row.
17544             rows.push(crow);
17545             crow = {
17546                 start: cells[i],
17547                 end : cells[i]
17548             };
17549             
17550         }
17551         
17552         rows.push(crow);
17553         ev.els = [];
17554         ev.rows = rows;
17555         ev.cells = cells;
17556         
17557         cells[0].events.push(ev);
17558         
17559         this.calevents.push(ev);
17560     },
17561     
17562     clearEvents: function() {
17563         
17564         if(!this.calevents){
17565             return;
17566         }
17567         
17568         Roo.each(this.cells.elements, function(c){
17569             c.row = 0;
17570             c.events = [];
17571             c.more = [];
17572         });
17573         
17574         Roo.each(this.calevents, function(e) {
17575             Roo.each(e.els, function(el) {
17576                 el.un('mouseenter' ,this.onEventEnter, this);
17577                 el.un('mouseleave' ,this.onEventLeave, this);
17578                 el.remove();
17579             },this);
17580         },this);
17581         
17582         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17583             e.remove();
17584         });
17585         
17586     },
17587     
17588     renderEvents: function()
17589     {   
17590         var _this = this;
17591         
17592         this.cells.each(function(c) {
17593             
17594             if(c.row < 5){
17595                 return;
17596             }
17597             
17598             var ev = c.events;
17599             
17600             var r = 4;
17601             if(c.row != c.events.length){
17602                 r = 4 - (4 - (c.row - c.events.length));
17603             }
17604             
17605             c.events = ev.slice(0, r);
17606             c.more = ev.slice(r);
17607             
17608             if(c.more.length && c.more.length == 1){
17609                 c.events.push(c.more.pop());
17610             }
17611             
17612             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17613             
17614         });
17615             
17616         this.cells.each(function(c) {
17617             
17618             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17619             
17620             
17621             for (var e = 0; e < c.events.length; e++){
17622                 var ev = c.events[e];
17623                 var rows = ev.rows;
17624                 
17625                 for(var i = 0; i < rows.length; i++) {
17626                 
17627                     // how many rows should it span..
17628
17629                     var  cfg = {
17630                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17631                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17632
17633                         unselectable : "on",
17634                         cn : [
17635                             {
17636                                 cls: 'fc-event-inner',
17637                                 cn : [
17638     //                                {
17639     //                                  tag:'span',
17640     //                                  cls: 'fc-event-time',
17641     //                                  html : cells.length > 1 ? '' : ev.time
17642     //                                },
17643                                     {
17644                                       tag:'span',
17645                                       cls: 'fc-event-title',
17646                                       html : String.format('{0}', ev.title)
17647                                     }
17648
17649
17650                                 ]
17651                             },
17652                             {
17653                                 cls: 'ui-resizable-handle ui-resizable-e',
17654                                 html : '&nbsp;&nbsp;&nbsp'
17655                             }
17656
17657                         ]
17658                     };
17659
17660                     if (i == 0) {
17661                         cfg.cls += ' fc-event-start';
17662                     }
17663                     if ((i+1) == rows.length) {
17664                         cfg.cls += ' fc-event-end';
17665                     }
17666
17667                     var ctr = _this.el.select('.fc-event-container',true).first();
17668                     var cg = ctr.createChild(cfg);
17669
17670                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17671                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17672
17673                     var r = (c.more.length) ? 1 : 0;
17674                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17675                     cg.setWidth(ebox.right - sbox.x -2);
17676
17677                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17678                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17679                     cg.on('click', _this.onEventClick, _this, ev);
17680
17681                     ev.els.push(cg);
17682                     
17683                 }
17684                 
17685             }
17686             
17687             
17688             if(c.more.length){
17689                 var  cfg = {
17690                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17691                     style : 'position: absolute',
17692                     unselectable : "on",
17693                     cn : [
17694                         {
17695                             cls: 'fc-event-inner',
17696                             cn : [
17697                                 {
17698                                   tag:'span',
17699                                   cls: 'fc-event-title',
17700                                   html : 'More'
17701                                 }
17702
17703
17704                             ]
17705                         },
17706                         {
17707                             cls: 'ui-resizable-handle ui-resizable-e',
17708                             html : '&nbsp;&nbsp;&nbsp'
17709                         }
17710
17711                     ]
17712                 };
17713
17714                 var ctr = _this.el.select('.fc-event-container',true).first();
17715                 var cg = ctr.createChild(cfg);
17716
17717                 var sbox = c.select('.fc-day-content',true).first().getBox();
17718                 var ebox = c.select('.fc-day-content',true).first().getBox();
17719                 //Roo.log(cg);
17720                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17721                 cg.setWidth(ebox.right - sbox.x -2);
17722
17723                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17724                 
17725             }
17726             
17727         });
17728         
17729         
17730         
17731     },
17732     
17733     onEventEnter: function (e, el,event,d) {
17734         this.fireEvent('evententer', this, el, event);
17735     },
17736     
17737     onEventLeave: function (e, el,event,d) {
17738         this.fireEvent('eventleave', this, el, event);
17739     },
17740     
17741     onEventClick: function (e, el,event,d) {
17742         this.fireEvent('eventclick', this, el, event);
17743     },
17744     
17745     onMonthChange: function () {
17746         this.store.load();
17747     },
17748     
17749     onMoreEventClick: function(e, el, more)
17750     {
17751         var _this = this;
17752         
17753         this.calpopover.placement = 'right';
17754         this.calpopover.setTitle('More');
17755         
17756         this.calpopover.setContent('');
17757         
17758         var ctr = this.calpopover.el.select('.popover-content', true).first();
17759         
17760         Roo.each(more, function(m){
17761             var cfg = {
17762                 cls : 'fc-event-hori fc-event-draggable',
17763                 html : m.title
17764             };
17765             var cg = ctr.createChild(cfg);
17766             
17767             cg.on('click', _this.onEventClick, _this, m);
17768         });
17769         
17770         this.calpopover.show(el);
17771         
17772         
17773     },
17774     
17775     onLoad: function () 
17776     {   
17777         this.calevents = [];
17778         var cal = this;
17779         
17780         if(this.store.getCount() > 0){
17781             this.store.data.each(function(d){
17782                cal.addItem({
17783                     id : d.data.id,
17784                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17785                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17786                     time : d.data.start_time,
17787                     title : d.data.title,
17788                     description : d.data.description,
17789                     venue : d.data.venue
17790                 });
17791             });
17792         }
17793         
17794         this.renderEvents();
17795         
17796         if(this.calevents.length && this.loadMask){
17797             this.maskEl.hide();
17798         }
17799     },
17800     
17801     onBeforeLoad: function()
17802     {
17803         this.clearEvents();
17804         if(this.loadMask){
17805             this.maskEl.show();
17806         }
17807     }
17808 });
17809
17810  
17811  /*
17812  * - LGPL
17813  *
17814  * element
17815  * 
17816  */
17817
17818 /**
17819  * @class Roo.bootstrap.Popover
17820  * @extends Roo.bootstrap.Component
17821  * Bootstrap Popover class
17822  * @cfg {String} html contents of the popover   (or false to use children..)
17823  * @cfg {String} title of popover (or false to hide)
17824  * @cfg {String} placement how it is placed
17825  * @cfg {String} trigger click || hover (or false to trigger manually)
17826  * @cfg {String} over what (parent or false to trigger manually.)
17827  * @cfg {Number} delay - delay before showing
17828  
17829  * @constructor
17830  * Create a new Popover
17831  * @param {Object} config The config object
17832  */
17833
17834 Roo.bootstrap.Popover = function(config){
17835     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17836     
17837     this.addEvents({
17838         // raw events
17839          /**
17840          * @event show
17841          * After the popover show
17842          * 
17843          * @param {Roo.bootstrap.Popover} this
17844          */
17845         "show" : true,
17846         /**
17847          * @event hide
17848          * After the popover hide
17849          * 
17850          * @param {Roo.bootstrap.Popover} this
17851          */
17852         "hide" : true
17853     });
17854 };
17855
17856 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17857     
17858     title: 'Fill in a title',
17859     html: false,
17860     
17861     placement : 'right',
17862     trigger : 'hover', // hover
17863     
17864     delay : 0,
17865     
17866     over: 'parent',
17867     
17868     can_build_overlaid : false,
17869     
17870     getChildContainer : function()
17871     {
17872         return this.el.select('.popover-content',true).first();
17873     },
17874     
17875     getAutoCreate : function(){
17876          
17877         var cfg = {
17878            cls : 'popover roo-dynamic',
17879            style: 'display:block',
17880            cn : [
17881                 {
17882                     cls : 'arrow'
17883                 },
17884                 {
17885                     cls : 'popover-inner',
17886                     cn : [
17887                         {
17888                             tag: 'h3',
17889                             cls: 'popover-title popover-header',
17890                             html : this.title
17891                         },
17892                         {
17893                             cls : 'popover-content popover-body',
17894                             html : this.html
17895                         }
17896                     ]
17897                     
17898                 }
17899            ]
17900         };
17901         
17902         return cfg;
17903     },
17904     setTitle: function(str)
17905     {
17906         this.title = str;
17907         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17908     },
17909     setContent: function(str)
17910     {
17911         this.html = str;
17912         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17913     },
17914     // as it get's added to the bottom of the page.
17915     onRender : function(ct, position)
17916     {
17917         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17918         if(!this.el){
17919             var cfg = Roo.apply({},  this.getAutoCreate());
17920             cfg.id = Roo.id();
17921             
17922             if (this.cls) {
17923                 cfg.cls += ' ' + this.cls;
17924             }
17925             if (this.style) {
17926                 cfg.style = this.style;
17927             }
17928             //Roo.log("adding to ");
17929             this.el = Roo.get(document.body).createChild(cfg, position);
17930 //            Roo.log(this.el);
17931         }
17932         this.initEvents();
17933     },
17934     
17935     initEvents : function()
17936     {
17937         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17938         this.el.enableDisplayMode('block');
17939         this.el.hide();
17940         if (this.over === false) {
17941             return; 
17942         }
17943         if (this.triggers === false) {
17944             return;
17945         }
17946         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17947         var triggers = this.trigger ? this.trigger.split(' ') : [];
17948         Roo.each(triggers, function(trigger) {
17949         
17950             if (trigger == 'click') {
17951                 on_el.on('click', this.toggle, this);
17952             } else if (trigger != 'manual') {
17953                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17954                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17955       
17956                 on_el.on(eventIn  ,this.enter, this);
17957                 on_el.on(eventOut, this.leave, this);
17958             }
17959         }, this);
17960         
17961     },
17962     
17963     
17964     // private
17965     timeout : null,
17966     hoverState : null,
17967     
17968     toggle : function () {
17969         this.hoverState == 'in' ? this.leave() : this.enter();
17970     },
17971     
17972     enter : function () {
17973         
17974         clearTimeout(this.timeout);
17975     
17976         this.hoverState = 'in';
17977     
17978         if (!this.delay || !this.delay.show) {
17979             this.show();
17980             return;
17981         }
17982         var _t = this;
17983         this.timeout = setTimeout(function () {
17984             if (_t.hoverState == 'in') {
17985                 _t.show();
17986             }
17987         }, this.delay.show)
17988     },
17989     
17990     leave : function() {
17991         clearTimeout(this.timeout);
17992     
17993         this.hoverState = 'out';
17994     
17995         if (!this.delay || !this.delay.hide) {
17996             this.hide();
17997             return;
17998         }
17999         var _t = this;
18000         this.timeout = setTimeout(function () {
18001             if (_t.hoverState == 'out') {
18002                 _t.hide();
18003             }
18004         }, this.delay.hide)
18005     },
18006     
18007     show : function (on_el)
18008     {
18009         if (!on_el) {
18010             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18011         }
18012         
18013         // set content.
18014         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18015         if (this.html !== false) {
18016             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18017         }
18018         this.el.removeClass([
18019             'fade','top','bottom', 'left', 'right','in',
18020             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18021         ]);
18022         if (!this.title.length) {
18023             this.el.select('.popover-title',true).hide();
18024         }
18025         
18026         var placement = typeof this.placement == 'function' ?
18027             this.placement.call(this, this.el, on_el) :
18028             this.placement;
18029             
18030         var autoToken = /\s?auto?\s?/i;
18031         var autoPlace = autoToken.test(placement);
18032         if (autoPlace) {
18033             placement = placement.replace(autoToken, '') || 'top';
18034         }
18035         
18036         //this.el.detach()
18037         //this.el.setXY([0,0]);
18038         this.el.show();
18039         this.el.dom.style.display='block';
18040         this.el.addClass(placement);
18041         
18042         //this.el.appendTo(on_el);
18043         
18044         var p = this.getPosition();
18045         var box = this.el.getBox();
18046         
18047         if (autoPlace) {
18048             // fixme..
18049         }
18050         var align = Roo.bootstrap.Popover.alignment[placement];
18051         
18052 //        Roo.log(align);
18053         this.el.alignTo(on_el, align[0],align[1]);
18054         //var arrow = this.el.select('.arrow',true).first();
18055         //arrow.set(align[2], 
18056         
18057         this.el.addClass('in');
18058         
18059         
18060         if (this.el.hasClass('fade')) {
18061             // fade it?
18062         }
18063         
18064         this.hoverState = 'in';
18065         
18066         this.fireEvent('show', this);
18067         
18068     },
18069     hide : function()
18070     {
18071         this.el.setXY([0,0]);
18072         this.el.removeClass('in');
18073         this.el.hide();
18074         this.hoverState = null;
18075         
18076         this.fireEvent('hide', this);
18077     }
18078     
18079 });
18080
18081 Roo.bootstrap.Popover.alignment = {
18082     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18083     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18084     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18085     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18086 };
18087
18088  /*
18089  * - LGPL
18090  *
18091  * Progress
18092  * 
18093  */
18094
18095 /**
18096  * @class Roo.bootstrap.Progress
18097  * @extends Roo.bootstrap.Component
18098  * Bootstrap Progress class
18099  * @cfg {Boolean} striped striped of the progress bar
18100  * @cfg {Boolean} active animated of the progress bar
18101  * 
18102  * 
18103  * @constructor
18104  * Create a new Progress
18105  * @param {Object} config The config object
18106  */
18107
18108 Roo.bootstrap.Progress = function(config){
18109     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18110 };
18111
18112 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18113     
18114     striped : false,
18115     active: false,
18116     
18117     getAutoCreate : function(){
18118         var cfg = {
18119             tag: 'div',
18120             cls: 'progress'
18121         };
18122         
18123         
18124         if(this.striped){
18125             cfg.cls += ' progress-striped';
18126         }
18127       
18128         if(this.active){
18129             cfg.cls += ' active';
18130         }
18131         
18132         
18133         return cfg;
18134     }
18135    
18136 });
18137
18138  
18139
18140  /*
18141  * - LGPL
18142  *
18143  * ProgressBar
18144  * 
18145  */
18146
18147 /**
18148  * @class Roo.bootstrap.ProgressBar
18149  * @extends Roo.bootstrap.Component
18150  * Bootstrap ProgressBar class
18151  * @cfg {Number} aria_valuenow aria-value now
18152  * @cfg {Number} aria_valuemin aria-value min
18153  * @cfg {Number} aria_valuemax aria-value max
18154  * @cfg {String} label label for the progress bar
18155  * @cfg {String} panel (success | info | warning | danger )
18156  * @cfg {String} role role of the progress bar
18157  * @cfg {String} sr_only text
18158  * 
18159  * 
18160  * @constructor
18161  * Create a new ProgressBar
18162  * @param {Object} config The config object
18163  */
18164
18165 Roo.bootstrap.ProgressBar = function(config){
18166     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18167 };
18168
18169 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18170     
18171     aria_valuenow : 0,
18172     aria_valuemin : 0,
18173     aria_valuemax : 100,
18174     label : false,
18175     panel : false,
18176     role : false,
18177     sr_only: false,
18178     
18179     getAutoCreate : function()
18180     {
18181         
18182         var cfg = {
18183             tag: 'div',
18184             cls: 'progress-bar',
18185             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18186         };
18187         
18188         if(this.sr_only){
18189             cfg.cn = {
18190                 tag: 'span',
18191                 cls: 'sr-only',
18192                 html: this.sr_only
18193             }
18194         }
18195         
18196         if(this.role){
18197             cfg.role = this.role;
18198         }
18199         
18200         if(this.aria_valuenow){
18201             cfg['aria-valuenow'] = this.aria_valuenow;
18202         }
18203         
18204         if(this.aria_valuemin){
18205             cfg['aria-valuemin'] = this.aria_valuemin;
18206         }
18207         
18208         if(this.aria_valuemax){
18209             cfg['aria-valuemax'] = this.aria_valuemax;
18210         }
18211         
18212         if(this.label && !this.sr_only){
18213             cfg.html = this.label;
18214         }
18215         
18216         if(this.panel){
18217             cfg.cls += ' progress-bar-' + this.panel;
18218         }
18219         
18220         return cfg;
18221     },
18222     
18223     update : function(aria_valuenow)
18224     {
18225         this.aria_valuenow = aria_valuenow;
18226         
18227         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18228     }
18229    
18230 });
18231
18232  
18233
18234  /*
18235  * - LGPL
18236  *
18237  * column
18238  * 
18239  */
18240
18241 /**
18242  * @class Roo.bootstrap.TabGroup
18243  * @extends Roo.bootstrap.Column
18244  * Bootstrap Column class
18245  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18246  * @cfg {Boolean} carousel true to make the group behave like a carousel
18247  * @cfg {Boolean} bullets show bullets for the panels
18248  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18249  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18250  * @cfg {Boolean} showarrow (true|false) show arrow default true
18251  * 
18252  * @constructor
18253  * Create a new TabGroup
18254  * @param {Object} config The config object
18255  */
18256
18257 Roo.bootstrap.TabGroup = function(config){
18258     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18259     if (!this.navId) {
18260         this.navId = Roo.id();
18261     }
18262     this.tabs = [];
18263     Roo.bootstrap.TabGroup.register(this);
18264     
18265 };
18266
18267 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18268     
18269     carousel : false,
18270     transition : false,
18271     bullets : 0,
18272     timer : 0,
18273     autoslide : false,
18274     slideFn : false,
18275     slideOnTouch : false,
18276     showarrow : true,
18277     
18278     getAutoCreate : function()
18279     {
18280         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18281         
18282         cfg.cls += ' tab-content';
18283         
18284         if (this.carousel) {
18285             cfg.cls += ' carousel slide';
18286             
18287             cfg.cn = [{
18288                cls : 'carousel-inner',
18289                cn : []
18290             }];
18291         
18292             if(this.bullets  && !Roo.isTouch){
18293                 
18294                 var bullets = {
18295                     cls : 'carousel-bullets',
18296                     cn : []
18297                 };
18298                
18299                 if(this.bullets_cls){
18300                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18301                 }
18302                 
18303                 bullets.cn.push({
18304                     cls : 'clear'
18305                 });
18306                 
18307                 cfg.cn[0].cn.push(bullets);
18308             }
18309             
18310             if(this.showarrow){
18311                 cfg.cn[0].cn.push({
18312                     tag : 'div',
18313                     class : 'carousel-arrow',
18314                     cn : [
18315                         {
18316                             tag : 'div',
18317                             class : 'carousel-prev',
18318                             cn : [
18319                                 {
18320                                     tag : 'i',
18321                                     class : 'fa fa-chevron-left'
18322                                 }
18323                             ]
18324                         },
18325                         {
18326                             tag : 'div',
18327                             class : 'carousel-next',
18328                             cn : [
18329                                 {
18330                                     tag : 'i',
18331                                     class : 'fa fa-chevron-right'
18332                                 }
18333                             ]
18334                         }
18335                     ]
18336                 });
18337             }
18338             
18339         }
18340         
18341         return cfg;
18342     },
18343     
18344     initEvents:  function()
18345     {
18346 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18347 //            this.el.on("touchstart", this.onTouchStart, this);
18348 //        }
18349         
18350         if(this.autoslide){
18351             var _this = this;
18352             
18353             this.slideFn = window.setInterval(function() {
18354                 _this.showPanelNext();
18355             }, this.timer);
18356         }
18357         
18358         if(this.showarrow){
18359             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18360             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18361         }
18362         
18363         
18364     },
18365     
18366 //    onTouchStart : function(e, el, o)
18367 //    {
18368 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18369 //            return;
18370 //        }
18371 //        
18372 //        this.showPanelNext();
18373 //    },
18374     
18375     
18376     getChildContainer : function()
18377     {
18378         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18379     },
18380     
18381     /**
18382     * register a Navigation item
18383     * @param {Roo.bootstrap.NavItem} the navitem to add
18384     */
18385     register : function(item)
18386     {
18387         this.tabs.push( item);
18388         item.navId = this.navId; // not really needed..
18389         this.addBullet();
18390     
18391     },
18392     
18393     getActivePanel : function()
18394     {
18395         var r = false;
18396         Roo.each(this.tabs, function(t) {
18397             if (t.active) {
18398                 r = t;
18399                 return false;
18400             }
18401             return null;
18402         });
18403         return r;
18404         
18405     },
18406     getPanelByName : function(n)
18407     {
18408         var r = false;
18409         Roo.each(this.tabs, function(t) {
18410             if (t.tabId == n) {
18411                 r = t;
18412                 return false;
18413             }
18414             return null;
18415         });
18416         return r;
18417     },
18418     indexOfPanel : function(p)
18419     {
18420         var r = false;
18421         Roo.each(this.tabs, function(t,i) {
18422             if (t.tabId == p.tabId) {
18423                 r = i;
18424                 return false;
18425             }
18426             return null;
18427         });
18428         return r;
18429     },
18430     /**
18431      * show a specific panel
18432      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18433      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18434      */
18435     showPanel : function (pan)
18436     {
18437         if(this.transition || typeof(pan) == 'undefined'){
18438             Roo.log("waiting for the transitionend");
18439             return false;
18440         }
18441         
18442         if (typeof(pan) == 'number') {
18443             pan = this.tabs[pan];
18444         }
18445         
18446         if (typeof(pan) == 'string') {
18447             pan = this.getPanelByName(pan);
18448         }
18449         
18450         var cur = this.getActivePanel();
18451         
18452         if(!pan || !cur){
18453             Roo.log('pan or acitve pan is undefined');
18454             return false;
18455         }
18456         
18457         if (pan.tabId == this.getActivePanel().tabId) {
18458             return true;
18459         }
18460         
18461         if (false === cur.fireEvent('beforedeactivate')) {
18462             return false;
18463         }
18464         
18465         if(this.bullets > 0 && !Roo.isTouch){
18466             this.setActiveBullet(this.indexOfPanel(pan));
18467         }
18468         
18469         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18470             
18471             //class="carousel-item carousel-item-next carousel-item-left"
18472             
18473             this.transition = true;
18474             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18475             var lr = dir == 'next' ? 'left' : 'right';
18476             pan.el.addClass(dir); // or prev
18477             pan.el.addClass('carousel-item-' + dir); // or prev
18478             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18479             cur.el.addClass(lr); // or right
18480             pan.el.addClass(lr);
18481             cur.el.addClass('carousel-item-' +lr); // or right
18482             pan.el.addClass('carousel-item-' +lr);
18483             
18484             
18485             var _this = this;
18486             cur.el.on('transitionend', function() {
18487                 Roo.log("trans end?");
18488                 
18489                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18490                 pan.setActive(true);
18491                 
18492                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18493                 cur.setActive(false);
18494                 
18495                 _this.transition = false;
18496                 
18497             }, this, { single:  true } );
18498             
18499             return true;
18500         }
18501         
18502         cur.setActive(false);
18503         pan.setActive(true);
18504         
18505         return true;
18506         
18507     },
18508     showPanelNext : function()
18509     {
18510         var i = this.indexOfPanel(this.getActivePanel());
18511         
18512         if (i >= this.tabs.length - 1 && !this.autoslide) {
18513             return;
18514         }
18515         
18516         if (i >= this.tabs.length - 1 && this.autoslide) {
18517             i = -1;
18518         }
18519         
18520         this.showPanel(this.tabs[i+1]);
18521     },
18522     
18523     showPanelPrev : function()
18524     {
18525         var i = this.indexOfPanel(this.getActivePanel());
18526         
18527         if (i  < 1 && !this.autoslide) {
18528             return;
18529         }
18530         
18531         if (i < 1 && this.autoslide) {
18532             i = this.tabs.length;
18533         }
18534         
18535         this.showPanel(this.tabs[i-1]);
18536     },
18537     
18538     
18539     addBullet: function()
18540     {
18541         if(!this.bullets || Roo.isTouch){
18542             return;
18543         }
18544         var ctr = this.el.select('.carousel-bullets',true).first();
18545         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18546         var bullet = ctr.createChild({
18547             cls : 'bullet bullet-' + i
18548         },ctr.dom.lastChild);
18549         
18550         
18551         var _this = this;
18552         
18553         bullet.on('click', (function(e, el, o, ii, t){
18554
18555             e.preventDefault();
18556
18557             this.showPanel(ii);
18558
18559             if(this.autoslide && this.slideFn){
18560                 clearInterval(this.slideFn);
18561                 this.slideFn = window.setInterval(function() {
18562                     _this.showPanelNext();
18563                 }, this.timer);
18564             }
18565
18566         }).createDelegate(this, [i, bullet], true));
18567                 
18568         
18569     },
18570      
18571     setActiveBullet : function(i)
18572     {
18573         if(Roo.isTouch){
18574             return;
18575         }
18576         
18577         Roo.each(this.el.select('.bullet', true).elements, function(el){
18578             el.removeClass('selected');
18579         });
18580
18581         var bullet = this.el.select('.bullet-' + i, true).first();
18582         
18583         if(!bullet){
18584             return;
18585         }
18586         
18587         bullet.addClass('selected');
18588     }
18589     
18590     
18591   
18592 });
18593
18594  
18595
18596  
18597  
18598 Roo.apply(Roo.bootstrap.TabGroup, {
18599     
18600     groups: {},
18601      /**
18602     * register a Navigation Group
18603     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18604     */
18605     register : function(navgrp)
18606     {
18607         this.groups[navgrp.navId] = navgrp;
18608         
18609     },
18610     /**
18611     * fetch a Navigation Group based on the navigation ID
18612     * if one does not exist , it will get created.
18613     * @param {string} the navgroup to add
18614     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18615     */
18616     get: function(navId) {
18617         if (typeof(this.groups[navId]) == 'undefined') {
18618             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18619         }
18620         return this.groups[navId] ;
18621     }
18622     
18623     
18624     
18625 });
18626
18627  /*
18628  * - LGPL
18629  *
18630  * TabPanel
18631  * 
18632  */
18633
18634 /**
18635  * @class Roo.bootstrap.TabPanel
18636  * @extends Roo.bootstrap.Component
18637  * Bootstrap TabPanel class
18638  * @cfg {Boolean} active panel active
18639  * @cfg {String} html panel content
18640  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18641  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18642  * @cfg {String} href click to link..
18643  * 
18644  * 
18645  * @constructor
18646  * Create a new TabPanel
18647  * @param {Object} config The config object
18648  */
18649
18650 Roo.bootstrap.TabPanel = function(config){
18651     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18652     this.addEvents({
18653         /**
18654              * @event changed
18655              * Fires when the active status changes
18656              * @param {Roo.bootstrap.TabPanel} this
18657              * @param {Boolean} state the new state
18658             
18659          */
18660         'changed': true,
18661         /**
18662              * @event beforedeactivate
18663              * Fires before a tab is de-activated - can be used to do validation on a form.
18664              * @param {Roo.bootstrap.TabPanel} this
18665              * @return {Boolean} false if there is an error
18666             
18667          */
18668         'beforedeactivate': true
18669      });
18670     
18671     this.tabId = this.tabId || Roo.id();
18672   
18673 };
18674
18675 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18676     
18677     active: false,
18678     html: false,
18679     tabId: false,
18680     navId : false,
18681     href : '',
18682     
18683     getAutoCreate : function(){
18684         
18685         
18686         var cfg = {
18687             tag: 'div',
18688             // item is needed for carousel - not sure if it has any effect otherwise
18689             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18690             html: this.html || ''
18691         };
18692         
18693         if(this.active){
18694             cfg.cls += ' active';
18695         }
18696         
18697         if(this.tabId){
18698             cfg.tabId = this.tabId;
18699         }
18700         
18701         
18702         
18703         return cfg;
18704     },
18705     
18706     initEvents:  function()
18707     {
18708         var p = this.parent();
18709         
18710         this.navId = this.navId || p.navId;
18711         
18712         if (typeof(this.navId) != 'undefined') {
18713             // not really needed.. but just in case.. parent should be a NavGroup.
18714             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18715             
18716             tg.register(this);
18717             
18718             var i = tg.tabs.length - 1;
18719             
18720             if(this.active && tg.bullets > 0 && i < tg.bullets){
18721                 tg.setActiveBullet(i);
18722             }
18723         }
18724         
18725         this.el.on('click', this.onClick, this);
18726         
18727         if(Roo.isTouch){
18728             this.el.on("touchstart", this.onTouchStart, this);
18729             this.el.on("touchmove", this.onTouchMove, this);
18730             this.el.on("touchend", this.onTouchEnd, this);
18731         }
18732         
18733     },
18734     
18735     onRender : function(ct, position)
18736     {
18737         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18738     },
18739     
18740     setActive : function(state)
18741     {
18742         Roo.log("panel - set active " + this.tabId + "=" + state);
18743         
18744         this.active = state;
18745         if (!state) {
18746             this.el.removeClass('active');
18747             
18748         } else  if (!this.el.hasClass('active')) {
18749             this.el.addClass('active');
18750         }
18751         
18752         this.fireEvent('changed', this, state);
18753     },
18754     
18755     onClick : function(e)
18756     {
18757         e.preventDefault();
18758         
18759         if(!this.href.length){
18760             return;
18761         }
18762         
18763         window.location.href = this.href;
18764     },
18765     
18766     startX : 0,
18767     startY : 0,
18768     endX : 0,
18769     endY : 0,
18770     swiping : false,
18771     
18772     onTouchStart : function(e)
18773     {
18774         this.swiping = false;
18775         
18776         this.startX = e.browserEvent.touches[0].clientX;
18777         this.startY = e.browserEvent.touches[0].clientY;
18778     },
18779     
18780     onTouchMove : function(e)
18781     {
18782         this.swiping = true;
18783         
18784         this.endX = e.browserEvent.touches[0].clientX;
18785         this.endY = e.browserEvent.touches[0].clientY;
18786     },
18787     
18788     onTouchEnd : function(e)
18789     {
18790         if(!this.swiping){
18791             this.onClick(e);
18792             return;
18793         }
18794         
18795         var tabGroup = this.parent();
18796         
18797         if(this.endX > this.startX){ // swiping right
18798             tabGroup.showPanelPrev();
18799             return;
18800         }
18801         
18802         if(this.startX > this.endX){ // swiping left
18803             tabGroup.showPanelNext();
18804             return;
18805         }
18806     }
18807     
18808     
18809 });
18810  
18811
18812  
18813
18814  /*
18815  * - LGPL
18816  *
18817  * DateField
18818  * 
18819  */
18820
18821 /**
18822  * @class Roo.bootstrap.DateField
18823  * @extends Roo.bootstrap.Input
18824  * Bootstrap DateField class
18825  * @cfg {Number} weekStart default 0
18826  * @cfg {String} viewMode default empty, (months|years)
18827  * @cfg {String} minViewMode default empty, (months|years)
18828  * @cfg {Number} startDate default -Infinity
18829  * @cfg {Number} endDate default Infinity
18830  * @cfg {Boolean} todayHighlight default false
18831  * @cfg {Boolean} todayBtn default false
18832  * @cfg {Boolean} calendarWeeks default false
18833  * @cfg {Object} daysOfWeekDisabled default empty
18834  * @cfg {Boolean} singleMode default false (true | false)
18835  * 
18836  * @cfg {Boolean} keyboardNavigation default true
18837  * @cfg {String} language default en
18838  * 
18839  * @constructor
18840  * Create a new DateField
18841  * @param {Object} config The config object
18842  */
18843
18844 Roo.bootstrap.DateField = function(config){
18845     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18846      this.addEvents({
18847             /**
18848              * @event show
18849              * Fires when this field show.
18850              * @param {Roo.bootstrap.DateField} this
18851              * @param {Mixed} date The date value
18852              */
18853             show : true,
18854             /**
18855              * @event show
18856              * Fires when this field hide.
18857              * @param {Roo.bootstrap.DateField} this
18858              * @param {Mixed} date The date value
18859              */
18860             hide : true,
18861             /**
18862              * @event select
18863              * Fires when select a date.
18864              * @param {Roo.bootstrap.DateField} this
18865              * @param {Mixed} date The date value
18866              */
18867             select : true,
18868             /**
18869              * @event beforeselect
18870              * Fires when before select a date.
18871              * @param {Roo.bootstrap.DateField} this
18872              * @param {Mixed} date The date value
18873              */
18874             beforeselect : true
18875         });
18876 };
18877
18878 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18879     
18880     /**
18881      * @cfg {String} format
18882      * The default date format string which can be overriden for localization support.  The format must be
18883      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18884      */
18885     format : "m/d/y",
18886     /**
18887      * @cfg {String} altFormats
18888      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18889      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18890      */
18891     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18892     
18893     weekStart : 0,
18894     
18895     viewMode : '',
18896     
18897     minViewMode : '',
18898     
18899     todayHighlight : false,
18900     
18901     todayBtn: false,
18902     
18903     language: 'en',
18904     
18905     keyboardNavigation: true,
18906     
18907     calendarWeeks: false,
18908     
18909     startDate: -Infinity,
18910     
18911     endDate: Infinity,
18912     
18913     daysOfWeekDisabled: [],
18914     
18915     _events: [],
18916     
18917     singleMode : false,
18918     
18919     UTCDate: function()
18920     {
18921         return new Date(Date.UTC.apply(Date, arguments));
18922     },
18923     
18924     UTCToday: function()
18925     {
18926         var today = new Date();
18927         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18928     },
18929     
18930     getDate: function() {
18931             var d = this.getUTCDate();
18932             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18933     },
18934     
18935     getUTCDate: function() {
18936             return this.date;
18937     },
18938     
18939     setDate: function(d) {
18940             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18941     },
18942     
18943     setUTCDate: function(d) {
18944             this.date = d;
18945             this.setValue(this.formatDate(this.date));
18946     },
18947         
18948     onRender: function(ct, position)
18949     {
18950         
18951         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18952         
18953         this.language = this.language || 'en';
18954         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18955         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18956         
18957         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18958         this.format = this.format || 'm/d/y';
18959         this.isInline = false;
18960         this.isInput = true;
18961         this.component = this.el.select('.add-on', true).first() || false;
18962         this.component = (this.component && this.component.length === 0) ? false : this.component;
18963         this.hasInput = this.component && this.inputEl().length;
18964         
18965         if (typeof(this.minViewMode === 'string')) {
18966             switch (this.minViewMode) {
18967                 case 'months':
18968                     this.minViewMode = 1;
18969                     break;
18970                 case 'years':
18971                     this.minViewMode = 2;
18972                     break;
18973                 default:
18974                     this.minViewMode = 0;
18975                     break;
18976             }
18977         }
18978         
18979         if (typeof(this.viewMode === 'string')) {
18980             switch (this.viewMode) {
18981                 case 'months':
18982                     this.viewMode = 1;
18983                     break;
18984                 case 'years':
18985                     this.viewMode = 2;
18986                     break;
18987                 default:
18988                     this.viewMode = 0;
18989                     break;
18990             }
18991         }
18992                 
18993         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18994         
18995 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18996         
18997         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18998         
18999         this.picker().on('mousedown', this.onMousedown, this);
19000         this.picker().on('click', this.onClick, this);
19001         
19002         this.picker().addClass('datepicker-dropdown');
19003         
19004         this.startViewMode = this.viewMode;
19005         
19006         if(this.singleMode){
19007             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19008                 v.setVisibilityMode(Roo.Element.DISPLAY);
19009                 v.hide();
19010             });
19011             
19012             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19013                 v.setStyle('width', '189px');
19014             });
19015         }
19016         
19017         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19018             if(!this.calendarWeeks){
19019                 v.remove();
19020                 return;
19021             }
19022             
19023             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19024             v.attr('colspan', function(i, val){
19025                 return parseInt(val) + 1;
19026             });
19027         });
19028                         
19029         
19030         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19031         
19032         this.setStartDate(this.startDate);
19033         this.setEndDate(this.endDate);
19034         
19035         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19036         
19037         this.fillDow();
19038         this.fillMonths();
19039         this.update();
19040         this.showMode();
19041         
19042         if(this.isInline) {
19043             this.showPopup();
19044         }
19045     },
19046     
19047     picker : function()
19048     {
19049         return this.pickerEl;
19050 //        return this.el.select('.datepicker', true).first();
19051     },
19052     
19053     fillDow: function()
19054     {
19055         var dowCnt = this.weekStart;
19056         
19057         var dow = {
19058             tag: 'tr',
19059             cn: [
19060                 
19061             ]
19062         };
19063         
19064         if(this.calendarWeeks){
19065             dow.cn.push({
19066                 tag: 'th',
19067                 cls: 'cw',
19068                 html: '&nbsp;'
19069             })
19070         }
19071         
19072         while (dowCnt < this.weekStart + 7) {
19073             dow.cn.push({
19074                 tag: 'th',
19075                 cls: 'dow',
19076                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19077             });
19078         }
19079         
19080         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19081     },
19082     
19083     fillMonths: function()
19084     {    
19085         var i = 0;
19086         var months = this.picker().select('>.datepicker-months td', true).first();
19087         
19088         months.dom.innerHTML = '';
19089         
19090         while (i < 12) {
19091             var month = {
19092                 tag: 'span',
19093                 cls: 'month',
19094                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19095             };
19096             
19097             months.createChild(month);
19098         }
19099         
19100     },
19101     
19102     update: function()
19103     {
19104         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;
19105         
19106         if (this.date < this.startDate) {
19107             this.viewDate = new Date(this.startDate);
19108         } else if (this.date > this.endDate) {
19109             this.viewDate = new Date(this.endDate);
19110         } else {
19111             this.viewDate = new Date(this.date);
19112         }
19113         
19114         this.fill();
19115     },
19116     
19117     fill: function() 
19118     {
19119         var d = new Date(this.viewDate),
19120                 year = d.getUTCFullYear(),
19121                 month = d.getUTCMonth(),
19122                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19123                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19124                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19125                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19126                 currentDate = this.date && this.date.valueOf(),
19127                 today = this.UTCToday();
19128         
19129         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19130         
19131 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19132         
19133 //        this.picker.select('>tfoot th.today').
19134 //                                              .text(dates[this.language].today)
19135 //                                              .toggle(this.todayBtn !== false);
19136     
19137         this.updateNavArrows();
19138         this.fillMonths();
19139                                                 
19140         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19141         
19142         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19143          
19144         prevMonth.setUTCDate(day);
19145         
19146         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19147         
19148         var nextMonth = new Date(prevMonth);
19149         
19150         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19151         
19152         nextMonth = nextMonth.valueOf();
19153         
19154         var fillMonths = false;
19155         
19156         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19157         
19158         while(prevMonth.valueOf() <= nextMonth) {
19159             var clsName = '';
19160             
19161             if (prevMonth.getUTCDay() === this.weekStart) {
19162                 if(fillMonths){
19163                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19164                 }
19165                     
19166                 fillMonths = {
19167                     tag: 'tr',
19168                     cn: []
19169                 };
19170                 
19171                 if(this.calendarWeeks){
19172                     // ISO 8601: First week contains first thursday.
19173                     // ISO also states week starts on Monday, but we can be more abstract here.
19174                     var
19175                     // Start of current week: based on weekstart/current date
19176                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19177                     // Thursday of this week
19178                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19179                     // First Thursday of year, year from thursday
19180                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19181                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19182                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19183                     
19184                     fillMonths.cn.push({
19185                         tag: 'td',
19186                         cls: 'cw',
19187                         html: calWeek
19188                     });
19189                 }
19190             }
19191             
19192             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19193                 clsName += ' old';
19194             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19195                 clsName += ' new';
19196             }
19197             if (this.todayHighlight &&
19198                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19199                 prevMonth.getUTCMonth() == today.getMonth() &&
19200                 prevMonth.getUTCDate() == today.getDate()) {
19201                 clsName += ' today';
19202             }
19203             
19204             if (currentDate && prevMonth.valueOf() === currentDate) {
19205                 clsName += ' active';
19206             }
19207             
19208             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19209                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19210                     clsName += ' disabled';
19211             }
19212             
19213             fillMonths.cn.push({
19214                 tag: 'td',
19215                 cls: 'day ' + clsName,
19216                 html: prevMonth.getDate()
19217             });
19218             
19219             prevMonth.setDate(prevMonth.getDate()+1);
19220         }
19221           
19222         var currentYear = this.date && this.date.getUTCFullYear();
19223         var currentMonth = this.date && this.date.getUTCMonth();
19224         
19225         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19226         
19227         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19228             v.removeClass('active');
19229             
19230             if(currentYear === year && k === currentMonth){
19231                 v.addClass('active');
19232             }
19233             
19234             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19235                 v.addClass('disabled');
19236             }
19237             
19238         });
19239         
19240         
19241         year = parseInt(year/10, 10) * 10;
19242         
19243         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19244         
19245         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19246         
19247         year -= 1;
19248         for (var i = -1; i < 11; i++) {
19249             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19250                 tag: 'span',
19251                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19252                 html: year
19253             });
19254             
19255             year += 1;
19256         }
19257     },
19258     
19259     showMode: function(dir) 
19260     {
19261         if (dir) {
19262             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19263         }
19264         
19265         Roo.each(this.picker().select('>div',true).elements, function(v){
19266             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19267             v.hide();
19268         });
19269         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19270     },
19271     
19272     place: function()
19273     {
19274         if(this.isInline) {
19275             return;
19276         }
19277         
19278         this.picker().removeClass(['bottom', 'top']);
19279         
19280         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19281             /*
19282              * place to the top of element!
19283              *
19284              */
19285             
19286             this.picker().addClass('top');
19287             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19288             
19289             return;
19290         }
19291         
19292         this.picker().addClass('bottom');
19293         
19294         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19295     },
19296     
19297     parseDate : function(value)
19298     {
19299         if(!value || value instanceof Date){
19300             return value;
19301         }
19302         var v = Date.parseDate(value, this.format);
19303         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19304             v = Date.parseDate(value, 'Y-m-d');
19305         }
19306         if(!v && this.altFormats){
19307             if(!this.altFormatsArray){
19308                 this.altFormatsArray = this.altFormats.split("|");
19309             }
19310             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19311                 v = Date.parseDate(value, this.altFormatsArray[i]);
19312             }
19313         }
19314         return v;
19315     },
19316     
19317     formatDate : function(date, fmt)
19318     {   
19319         return (!date || !(date instanceof Date)) ?
19320         date : date.dateFormat(fmt || this.format);
19321     },
19322     
19323     onFocus : function()
19324     {
19325         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19326         this.showPopup();
19327     },
19328     
19329     onBlur : function()
19330     {
19331         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19332         
19333         var d = this.inputEl().getValue();
19334         
19335         this.setValue(d);
19336                 
19337         this.hidePopup();
19338     },
19339     
19340     showPopup : function()
19341     {
19342         this.picker().show();
19343         this.update();
19344         this.place();
19345         
19346         this.fireEvent('showpopup', this, this.date);
19347     },
19348     
19349     hidePopup : function()
19350     {
19351         if(this.isInline) {
19352             return;
19353         }
19354         this.picker().hide();
19355         this.viewMode = this.startViewMode;
19356         this.showMode();
19357         
19358         this.fireEvent('hidepopup', this, this.date);
19359         
19360     },
19361     
19362     onMousedown: function(e)
19363     {
19364         e.stopPropagation();
19365         e.preventDefault();
19366     },
19367     
19368     keyup: function(e)
19369     {
19370         Roo.bootstrap.DateField.superclass.keyup.call(this);
19371         this.update();
19372     },
19373
19374     setValue: function(v)
19375     {
19376         if(this.fireEvent('beforeselect', this, v) !== false){
19377             var d = new Date(this.parseDate(v) ).clearTime();
19378         
19379             if(isNaN(d.getTime())){
19380                 this.date = this.viewDate = '';
19381                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19382                 return;
19383             }
19384
19385             v = this.formatDate(d);
19386
19387             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19388
19389             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19390
19391             this.update();
19392
19393             this.fireEvent('select', this, this.date);
19394         }
19395     },
19396     
19397     getValue: function()
19398     {
19399         return this.formatDate(this.date);
19400     },
19401     
19402     fireKey: function(e)
19403     {
19404         if (!this.picker().isVisible()){
19405             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19406                 this.showPopup();
19407             }
19408             return;
19409         }
19410         
19411         var dateChanged = false,
19412         dir, day, month,
19413         newDate, newViewDate;
19414         
19415         switch(e.keyCode){
19416             case 27: // escape
19417                 this.hidePopup();
19418                 e.preventDefault();
19419                 break;
19420             case 37: // left
19421             case 39: // right
19422                 if (!this.keyboardNavigation) {
19423                     break;
19424                 }
19425                 dir = e.keyCode == 37 ? -1 : 1;
19426                 
19427                 if (e.ctrlKey){
19428                     newDate = this.moveYear(this.date, dir);
19429                     newViewDate = this.moveYear(this.viewDate, dir);
19430                 } else if (e.shiftKey){
19431                     newDate = this.moveMonth(this.date, dir);
19432                     newViewDate = this.moveMonth(this.viewDate, dir);
19433                 } else {
19434                     newDate = new Date(this.date);
19435                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19436                     newViewDate = new Date(this.viewDate);
19437                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19438                 }
19439                 if (this.dateWithinRange(newDate)){
19440                     this.date = newDate;
19441                     this.viewDate = newViewDate;
19442                     this.setValue(this.formatDate(this.date));
19443 //                    this.update();
19444                     e.preventDefault();
19445                     dateChanged = true;
19446                 }
19447                 break;
19448             case 38: // up
19449             case 40: // down
19450                 if (!this.keyboardNavigation) {
19451                     break;
19452                 }
19453                 dir = e.keyCode == 38 ? -1 : 1;
19454                 if (e.ctrlKey){
19455                     newDate = this.moveYear(this.date, dir);
19456                     newViewDate = this.moveYear(this.viewDate, dir);
19457                 } else if (e.shiftKey){
19458                     newDate = this.moveMonth(this.date, dir);
19459                     newViewDate = this.moveMonth(this.viewDate, dir);
19460                 } else {
19461                     newDate = new Date(this.date);
19462                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19463                     newViewDate = new Date(this.viewDate);
19464                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19465                 }
19466                 if (this.dateWithinRange(newDate)){
19467                     this.date = newDate;
19468                     this.viewDate = newViewDate;
19469                     this.setValue(this.formatDate(this.date));
19470 //                    this.update();
19471                     e.preventDefault();
19472                     dateChanged = true;
19473                 }
19474                 break;
19475             case 13: // enter
19476                 this.setValue(this.formatDate(this.date));
19477                 this.hidePopup();
19478                 e.preventDefault();
19479                 break;
19480             case 9: // tab
19481                 this.setValue(this.formatDate(this.date));
19482                 this.hidePopup();
19483                 break;
19484             case 16: // shift
19485             case 17: // ctrl
19486             case 18: // alt
19487                 break;
19488             default :
19489                 this.hidePopup();
19490                 
19491         }
19492     },
19493     
19494     
19495     onClick: function(e) 
19496     {
19497         e.stopPropagation();
19498         e.preventDefault();
19499         
19500         var target = e.getTarget();
19501         
19502         if(target.nodeName.toLowerCase() === 'i'){
19503             target = Roo.get(target).dom.parentNode;
19504         }
19505         
19506         var nodeName = target.nodeName;
19507         var className = target.className;
19508         var html = target.innerHTML;
19509         //Roo.log(nodeName);
19510         
19511         switch(nodeName.toLowerCase()) {
19512             case 'th':
19513                 switch(className) {
19514                     case 'switch':
19515                         this.showMode(1);
19516                         break;
19517                     case 'prev':
19518                     case 'next':
19519                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19520                         switch(this.viewMode){
19521                                 case 0:
19522                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19523                                         break;
19524                                 case 1:
19525                                 case 2:
19526                                         this.viewDate = this.moveYear(this.viewDate, dir);
19527                                         break;
19528                         }
19529                         this.fill();
19530                         break;
19531                     case 'today':
19532                         var date = new Date();
19533                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19534 //                        this.fill()
19535                         this.setValue(this.formatDate(this.date));
19536                         
19537                         this.hidePopup();
19538                         break;
19539                 }
19540                 break;
19541             case 'span':
19542                 if (className.indexOf('disabled') < 0) {
19543                     this.viewDate.setUTCDate(1);
19544                     if (className.indexOf('month') > -1) {
19545                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19546                     } else {
19547                         var year = parseInt(html, 10) || 0;
19548                         this.viewDate.setUTCFullYear(year);
19549                         
19550                     }
19551                     
19552                     if(this.singleMode){
19553                         this.setValue(this.formatDate(this.viewDate));
19554                         this.hidePopup();
19555                         return;
19556                     }
19557                     
19558                     this.showMode(-1);
19559                     this.fill();
19560                 }
19561                 break;
19562                 
19563             case 'td':
19564                 //Roo.log(className);
19565                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19566                     var day = parseInt(html, 10) || 1;
19567                     var year = this.viewDate.getUTCFullYear(),
19568                         month = this.viewDate.getUTCMonth();
19569
19570                     if (className.indexOf('old') > -1) {
19571                         if(month === 0 ){
19572                             month = 11;
19573                             year -= 1;
19574                         }else{
19575                             month -= 1;
19576                         }
19577                     } else if (className.indexOf('new') > -1) {
19578                         if (month == 11) {
19579                             month = 0;
19580                             year += 1;
19581                         } else {
19582                             month += 1;
19583                         }
19584                     }
19585                     //Roo.log([year,month,day]);
19586                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19587                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19588 //                    this.fill();
19589                     //Roo.log(this.formatDate(this.date));
19590                     this.setValue(this.formatDate(this.date));
19591                     this.hidePopup();
19592                 }
19593                 break;
19594         }
19595     },
19596     
19597     setStartDate: function(startDate)
19598     {
19599         this.startDate = startDate || -Infinity;
19600         if (this.startDate !== -Infinity) {
19601             this.startDate = this.parseDate(this.startDate);
19602         }
19603         this.update();
19604         this.updateNavArrows();
19605     },
19606
19607     setEndDate: function(endDate)
19608     {
19609         this.endDate = endDate || Infinity;
19610         if (this.endDate !== Infinity) {
19611             this.endDate = this.parseDate(this.endDate);
19612         }
19613         this.update();
19614         this.updateNavArrows();
19615     },
19616     
19617     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19618     {
19619         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19620         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19621             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19622         }
19623         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19624             return parseInt(d, 10);
19625         });
19626         this.update();
19627         this.updateNavArrows();
19628     },
19629     
19630     updateNavArrows: function() 
19631     {
19632         if(this.singleMode){
19633             return;
19634         }
19635         
19636         var d = new Date(this.viewDate),
19637         year = d.getUTCFullYear(),
19638         month = d.getUTCMonth();
19639         
19640         Roo.each(this.picker().select('.prev', true).elements, function(v){
19641             v.show();
19642             switch (this.viewMode) {
19643                 case 0:
19644
19645                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19646                         v.hide();
19647                     }
19648                     break;
19649                 case 1:
19650                 case 2:
19651                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19652                         v.hide();
19653                     }
19654                     break;
19655             }
19656         });
19657         
19658         Roo.each(this.picker().select('.next', true).elements, function(v){
19659             v.show();
19660             switch (this.viewMode) {
19661                 case 0:
19662
19663                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19664                         v.hide();
19665                     }
19666                     break;
19667                 case 1:
19668                 case 2:
19669                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19670                         v.hide();
19671                     }
19672                     break;
19673             }
19674         })
19675     },
19676     
19677     moveMonth: function(date, dir)
19678     {
19679         if (!dir) {
19680             return date;
19681         }
19682         var new_date = new Date(date.valueOf()),
19683         day = new_date.getUTCDate(),
19684         month = new_date.getUTCMonth(),
19685         mag = Math.abs(dir),
19686         new_month, test;
19687         dir = dir > 0 ? 1 : -1;
19688         if (mag == 1){
19689             test = dir == -1
19690             // If going back one month, make sure month is not current month
19691             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19692             ? function(){
19693                 return new_date.getUTCMonth() == month;
19694             }
19695             // If going forward one month, make sure month is as expected
19696             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19697             : function(){
19698                 return new_date.getUTCMonth() != new_month;
19699             };
19700             new_month = month + dir;
19701             new_date.setUTCMonth(new_month);
19702             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19703             if (new_month < 0 || new_month > 11) {
19704                 new_month = (new_month + 12) % 12;
19705             }
19706         } else {
19707             // For magnitudes >1, move one month at a time...
19708             for (var i=0; i<mag; i++) {
19709                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19710                 new_date = this.moveMonth(new_date, dir);
19711             }
19712             // ...then reset the day, keeping it in the new month
19713             new_month = new_date.getUTCMonth();
19714             new_date.setUTCDate(day);
19715             test = function(){
19716                 return new_month != new_date.getUTCMonth();
19717             };
19718         }
19719         // Common date-resetting loop -- if date is beyond end of month, make it
19720         // end of month
19721         while (test()){
19722             new_date.setUTCDate(--day);
19723             new_date.setUTCMonth(new_month);
19724         }
19725         return new_date;
19726     },
19727
19728     moveYear: function(date, dir)
19729     {
19730         return this.moveMonth(date, dir*12);
19731     },
19732
19733     dateWithinRange: function(date)
19734     {
19735         return date >= this.startDate && date <= this.endDate;
19736     },
19737
19738     
19739     remove: function() 
19740     {
19741         this.picker().remove();
19742     },
19743     
19744     validateValue : function(value)
19745     {
19746         if(this.getVisibilityEl().hasClass('hidden')){
19747             return true;
19748         }
19749         
19750         if(value.length < 1)  {
19751             if(this.allowBlank){
19752                 return true;
19753             }
19754             return false;
19755         }
19756         
19757         if(value.length < this.minLength){
19758             return false;
19759         }
19760         if(value.length > this.maxLength){
19761             return false;
19762         }
19763         if(this.vtype){
19764             var vt = Roo.form.VTypes;
19765             if(!vt[this.vtype](value, this)){
19766                 return false;
19767             }
19768         }
19769         if(typeof this.validator == "function"){
19770             var msg = this.validator(value);
19771             if(msg !== true){
19772                 return false;
19773             }
19774         }
19775         
19776         if(this.regex && !this.regex.test(value)){
19777             return false;
19778         }
19779         
19780         if(typeof(this.parseDate(value)) == 'undefined'){
19781             return false;
19782         }
19783         
19784         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19785             return false;
19786         }      
19787         
19788         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19789             return false;
19790         } 
19791         
19792         
19793         return true;
19794     },
19795     
19796     reset : function()
19797     {
19798         this.date = this.viewDate = '';
19799         
19800         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19801     }
19802    
19803 });
19804
19805 Roo.apply(Roo.bootstrap.DateField,  {
19806     
19807     head : {
19808         tag: 'thead',
19809         cn: [
19810         {
19811             tag: 'tr',
19812             cn: [
19813             {
19814                 tag: 'th',
19815                 cls: 'prev',
19816                 html: '<i class="fa fa-arrow-left"/>'
19817             },
19818             {
19819                 tag: 'th',
19820                 cls: 'switch',
19821                 colspan: '5'
19822             },
19823             {
19824                 tag: 'th',
19825                 cls: 'next',
19826                 html: '<i class="fa fa-arrow-right"/>'
19827             }
19828
19829             ]
19830         }
19831         ]
19832     },
19833     
19834     content : {
19835         tag: 'tbody',
19836         cn: [
19837         {
19838             tag: 'tr',
19839             cn: [
19840             {
19841                 tag: 'td',
19842                 colspan: '7'
19843             }
19844             ]
19845         }
19846         ]
19847     },
19848     
19849     footer : {
19850         tag: 'tfoot',
19851         cn: [
19852         {
19853             tag: 'tr',
19854             cn: [
19855             {
19856                 tag: 'th',
19857                 colspan: '7',
19858                 cls: 'today'
19859             }
19860                     
19861             ]
19862         }
19863         ]
19864     },
19865     
19866     dates:{
19867         en: {
19868             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19869             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19870             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19871             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19872             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19873             today: "Today"
19874         }
19875     },
19876     
19877     modes: [
19878     {
19879         clsName: 'days',
19880         navFnc: 'Month',
19881         navStep: 1
19882     },
19883     {
19884         clsName: 'months',
19885         navFnc: 'FullYear',
19886         navStep: 1
19887     },
19888     {
19889         clsName: 'years',
19890         navFnc: 'FullYear',
19891         navStep: 10
19892     }]
19893 });
19894
19895 Roo.apply(Roo.bootstrap.DateField,  {
19896   
19897     template : {
19898         tag: 'div',
19899         cls: 'datepicker dropdown-menu roo-dynamic',
19900         cn: [
19901         {
19902             tag: 'div',
19903             cls: 'datepicker-days',
19904             cn: [
19905             {
19906                 tag: 'table',
19907                 cls: 'table-condensed',
19908                 cn:[
19909                 Roo.bootstrap.DateField.head,
19910                 {
19911                     tag: 'tbody'
19912                 },
19913                 Roo.bootstrap.DateField.footer
19914                 ]
19915             }
19916             ]
19917         },
19918         {
19919             tag: 'div',
19920             cls: 'datepicker-months',
19921             cn: [
19922             {
19923                 tag: 'table',
19924                 cls: 'table-condensed',
19925                 cn:[
19926                 Roo.bootstrap.DateField.head,
19927                 Roo.bootstrap.DateField.content,
19928                 Roo.bootstrap.DateField.footer
19929                 ]
19930             }
19931             ]
19932         },
19933         {
19934             tag: 'div',
19935             cls: 'datepicker-years',
19936             cn: [
19937             {
19938                 tag: 'table',
19939                 cls: 'table-condensed',
19940                 cn:[
19941                 Roo.bootstrap.DateField.head,
19942                 Roo.bootstrap.DateField.content,
19943                 Roo.bootstrap.DateField.footer
19944                 ]
19945             }
19946             ]
19947         }
19948         ]
19949     }
19950 });
19951
19952  
19953
19954  /*
19955  * - LGPL
19956  *
19957  * TimeField
19958  * 
19959  */
19960
19961 /**
19962  * @class Roo.bootstrap.TimeField
19963  * @extends Roo.bootstrap.Input
19964  * Bootstrap DateField class
19965  * 
19966  * 
19967  * @constructor
19968  * Create a new TimeField
19969  * @param {Object} config The config object
19970  */
19971
19972 Roo.bootstrap.TimeField = function(config){
19973     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19974     this.addEvents({
19975             /**
19976              * @event show
19977              * Fires when this field show.
19978              * @param {Roo.bootstrap.DateField} thisthis
19979              * @param {Mixed} date The date value
19980              */
19981             show : true,
19982             /**
19983              * @event show
19984              * Fires when this field hide.
19985              * @param {Roo.bootstrap.DateField} this
19986              * @param {Mixed} date The date value
19987              */
19988             hide : true,
19989             /**
19990              * @event select
19991              * Fires when select a date.
19992              * @param {Roo.bootstrap.DateField} this
19993              * @param {Mixed} date The date value
19994              */
19995             select : true
19996         });
19997 };
19998
19999 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20000     
20001     /**
20002      * @cfg {String} format
20003      * The default time format string which can be overriden for localization support.  The format must be
20004      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20005      */
20006     format : "H:i",
20007        
20008     onRender: function(ct, position)
20009     {
20010         
20011         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20012                 
20013         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20014         
20015         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20016         
20017         this.pop = this.picker().select('>.datepicker-time',true).first();
20018         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20019         
20020         this.picker().on('mousedown', this.onMousedown, this);
20021         this.picker().on('click', this.onClick, this);
20022         
20023         this.picker().addClass('datepicker-dropdown');
20024     
20025         this.fillTime();
20026         this.update();
20027             
20028         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20029         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20030         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20031         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20032         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20033         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20034
20035     },
20036     
20037     fireKey: function(e){
20038         if (!this.picker().isVisible()){
20039             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20040                 this.show();
20041             }
20042             return;
20043         }
20044
20045         e.preventDefault();
20046         
20047         switch(e.keyCode){
20048             case 27: // escape
20049                 this.hide();
20050                 break;
20051             case 37: // left
20052             case 39: // right
20053                 this.onTogglePeriod();
20054                 break;
20055             case 38: // up
20056                 this.onIncrementMinutes();
20057                 break;
20058             case 40: // down
20059                 this.onDecrementMinutes();
20060                 break;
20061             case 13: // enter
20062             case 9: // tab
20063                 this.setTime();
20064                 break;
20065         }
20066     },
20067     
20068     onClick: function(e) {
20069         e.stopPropagation();
20070         e.preventDefault();
20071     },
20072     
20073     picker : function()
20074     {
20075         return this.el.select('.datepicker', true).first();
20076     },
20077     
20078     fillTime: function()
20079     {    
20080         var time = this.pop.select('tbody', true).first();
20081         
20082         time.dom.innerHTML = '';
20083         
20084         time.createChild({
20085             tag: 'tr',
20086             cn: [
20087                 {
20088                     tag: 'td',
20089                     cn: [
20090                         {
20091                             tag: 'a',
20092                             href: '#',
20093                             cls: 'btn',
20094                             cn: [
20095                                 {
20096                                     tag: 'span',
20097                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20098                                 }
20099                             ]
20100                         } 
20101                     ]
20102                 },
20103                 {
20104                     tag: 'td',
20105                     cls: 'separator'
20106                 },
20107                 {
20108                     tag: 'td',
20109                     cn: [
20110                         {
20111                             tag: 'a',
20112                             href: '#',
20113                             cls: 'btn',
20114                             cn: [
20115                                 {
20116                                     tag: 'span',
20117                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20118                                 }
20119                             ]
20120                         }
20121                     ]
20122                 },
20123                 {
20124                     tag: 'td',
20125                     cls: 'separator'
20126                 }
20127             ]
20128         });
20129         
20130         time.createChild({
20131             tag: 'tr',
20132             cn: [
20133                 {
20134                     tag: 'td',
20135                     cn: [
20136                         {
20137                             tag: 'span',
20138                             cls: 'timepicker-hour',
20139                             html: '00'
20140                         }  
20141                     ]
20142                 },
20143                 {
20144                     tag: 'td',
20145                     cls: 'separator',
20146                     html: ':'
20147                 },
20148                 {
20149                     tag: 'td',
20150                     cn: [
20151                         {
20152                             tag: 'span',
20153                             cls: 'timepicker-minute',
20154                             html: '00'
20155                         }  
20156                     ]
20157                 },
20158                 {
20159                     tag: 'td',
20160                     cls: 'separator'
20161                 },
20162                 {
20163                     tag: 'td',
20164                     cn: [
20165                         {
20166                             tag: 'button',
20167                             type: 'button',
20168                             cls: 'btn btn-primary period',
20169                             html: 'AM'
20170                             
20171                         }
20172                     ]
20173                 }
20174             ]
20175         });
20176         
20177         time.createChild({
20178             tag: 'tr',
20179             cn: [
20180                 {
20181                     tag: 'td',
20182                     cn: [
20183                         {
20184                             tag: 'a',
20185                             href: '#',
20186                             cls: 'btn',
20187                             cn: [
20188                                 {
20189                                     tag: 'span',
20190                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20191                                 }
20192                             ]
20193                         }
20194                     ]
20195                 },
20196                 {
20197                     tag: 'td',
20198                     cls: 'separator'
20199                 },
20200                 {
20201                     tag: 'td',
20202                     cn: [
20203                         {
20204                             tag: 'a',
20205                             href: '#',
20206                             cls: 'btn',
20207                             cn: [
20208                                 {
20209                                     tag: 'span',
20210                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20211                                 }
20212                             ]
20213                         }
20214                     ]
20215                 },
20216                 {
20217                     tag: 'td',
20218                     cls: 'separator'
20219                 }
20220             ]
20221         });
20222         
20223     },
20224     
20225     update: function()
20226     {
20227         
20228         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20229         
20230         this.fill();
20231     },
20232     
20233     fill: function() 
20234     {
20235         var hours = this.time.getHours();
20236         var minutes = this.time.getMinutes();
20237         var period = 'AM';
20238         
20239         if(hours > 11){
20240             period = 'PM';
20241         }
20242         
20243         if(hours == 0){
20244             hours = 12;
20245         }
20246         
20247         
20248         if(hours > 12){
20249             hours = hours - 12;
20250         }
20251         
20252         if(hours < 10){
20253             hours = '0' + hours;
20254         }
20255         
20256         if(minutes < 10){
20257             minutes = '0' + minutes;
20258         }
20259         
20260         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20261         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20262         this.pop.select('button', true).first().dom.innerHTML = period;
20263         
20264     },
20265     
20266     place: function()
20267     {   
20268         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20269         
20270         var cls = ['bottom'];
20271         
20272         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20273             cls.pop();
20274             cls.push('top');
20275         }
20276         
20277         cls.push('right');
20278         
20279         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20280             cls.pop();
20281             cls.push('left');
20282         }
20283         
20284         this.picker().addClass(cls.join('-'));
20285         
20286         var _this = this;
20287         
20288         Roo.each(cls, function(c){
20289             if(c == 'bottom'){
20290                 _this.picker().setTop(_this.inputEl().getHeight());
20291                 return;
20292             }
20293             if(c == 'top'){
20294                 _this.picker().setTop(0 - _this.picker().getHeight());
20295                 return;
20296             }
20297             
20298             if(c == 'left'){
20299                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20300                 return;
20301             }
20302             if(c == 'right'){
20303                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20304                 return;
20305             }
20306         });
20307         
20308     },
20309   
20310     onFocus : function()
20311     {
20312         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20313         this.show();
20314     },
20315     
20316     onBlur : function()
20317     {
20318         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20319         this.hide();
20320     },
20321     
20322     show : function()
20323     {
20324         this.picker().show();
20325         this.pop.show();
20326         this.update();
20327         this.place();
20328         
20329         this.fireEvent('show', this, this.date);
20330     },
20331     
20332     hide : function()
20333     {
20334         this.picker().hide();
20335         this.pop.hide();
20336         
20337         this.fireEvent('hide', this, this.date);
20338     },
20339     
20340     setTime : function()
20341     {
20342         this.hide();
20343         this.setValue(this.time.format(this.format));
20344         
20345         this.fireEvent('select', this, this.date);
20346         
20347         
20348     },
20349     
20350     onMousedown: function(e){
20351         e.stopPropagation();
20352         e.preventDefault();
20353     },
20354     
20355     onIncrementHours: function()
20356     {
20357         Roo.log('onIncrementHours');
20358         this.time = this.time.add(Date.HOUR, 1);
20359         this.update();
20360         
20361     },
20362     
20363     onDecrementHours: function()
20364     {
20365         Roo.log('onDecrementHours');
20366         this.time = this.time.add(Date.HOUR, -1);
20367         this.update();
20368     },
20369     
20370     onIncrementMinutes: function()
20371     {
20372         Roo.log('onIncrementMinutes');
20373         this.time = this.time.add(Date.MINUTE, 1);
20374         this.update();
20375     },
20376     
20377     onDecrementMinutes: function()
20378     {
20379         Roo.log('onDecrementMinutes');
20380         this.time = this.time.add(Date.MINUTE, -1);
20381         this.update();
20382     },
20383     
20384     onTogglePeriod: function()
20385     {
20386         Roo.log('onTogglePeriod');
20387         this.time = this.time.add(Date.HOUR, 12);
20388         this.update();
20389     }
20390     
20391    
20392 });
20393
20394 Roo.apply(Roo.bootstrap.TimeField,  {
20395     
20396     content : {
20397         tag: 'tbody',
20398         cn: [
20399             {
20400                 tag: 'tr',
20401                 cn: [
20402                 {
20403                     tag: 'td',
20404                     colspan: '7'
20405                 }
20406                 ]
20407             }
20408         ]
20409     },
20410     
20411     footer : {
20412         tag: 'tfoot',
20413         cn: [
20414             {
20415                 tag: 'tr',
20416                 cn: [
20417                 {
20418                     tag: 'th',
20419                     colspan: '7',
20420                     cls: '',
20421                     cn: [
20422                         {
20423                             tag: 'button',
20424                             cls: 'btn btn-info ok',
20425                             html: 'OK'
20426                         }
20427                     ]
20428                 }
20429
20430                 ]
20431             }
20432         ]
20433     }
20434 });
20435
20436 Roo.apply(Roo.bootstrap.TimeField,  {
20437   
20438     template : {
20439         tag: 'div',
20440         cls: 'datepicker dropdown-menu',
20441         cn: [
20442             {
20443                 tag: 'div',
20444                 cls: 'datepicker-time',
20445                 cn: [
20446                 {
20447                     tag: 'table',
20448                     cls: 'table-condensed',
20449                     cn:[
20450                     Roo.bootstrap.TimeField.content,
20451                     Roo.bootstrap.TimeField.footer
20452                     ]
20453                 }
20454                 ]
20455             }
20456         ]
20457     }
20458 });
20459
20460  
20461
20462  /*
20463  * - LGPL
20464  *
20465  * MonthField
20466  * 
20467  */
20468
20469 /**
20470  * @class Roo.bootstrap.MonthField
20471  * @extends Roo.bootstrap.Input
20472  * Bootstrap MonthField class
20473  * 
20474  * @cfg {String} language default en
20475  * 
20476  * @constructor
20477  * Create a new MonthField
20478  * @param {Object} config The config object
20479  */
20480
20481 Roo.bootstrap.MonthField = function(config){
20482     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20483     
20484     this.addEvents({
20485         /**
20486          * @event show
20487          * Fires when this field show.
20488          * @param {Roo.bootstrap.MonthField} this
20489          * @param {Mixed} date The date value
20490          */
20491         show : true,
20492         /**
20493          * @event show
20494          * Fires when this field hide.
20495          * @param {Roo.bootstrap.MonthField} this
20496          * @param {Mixed} date The date value
20497          */
20498         hide : true,
20499         /**
20500          * @event select
20501          * Fires when select a date.
20502          * @param {Roo.bootstrap.MonthField} this
20503          * @param {String} oldvalue The old value
20504          * @param {String} newvalue The new value
20505          */
20506         select : true
20507     });
20508 };
20509
20510 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20511     
20512     onRender: function(ct, position)
20513     {
20514         
20515         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20516         
20517         this.language = this.language || 'en';
20518         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20519         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20520         
20521         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20522         this.isInline = false;
20523         this.isInput = true;
20524         this.component = this.el.select('.add-on', true).first() || false;
20525         this.component = (this.component && this.component.length === 0) ? false : this.component;
20526         this.hasInput = this.component && this.inputEL().length;
20527         
20528         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20529         
20530         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20531         
20532         this.picker().on('mousedown', this.onMousedown, this);
20533         this.picker().on('click', this.onClick, this);
20534         
20535         this.picker().addClass('datepicker-dropdown');
20536         
20537         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20538             v.setStyle('width', '189px');
20539         });
20540         
20541         this.fillMonths();
20542         
20543         this.update();
20544         
20545         if(this.isInline) {
20546             this.show();
20547         }
20548         
20549     },
20550     
20551     setValue: function(v, suppressEvent)
20552     {   
20553         var o = this.getValue();
20554         
20555         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20556         
20557         this.update();
20558
20559         if(suppressEvent !== true){
20560             this.fireEvent('select', this, o, v);
20561         }
20562         
20563     },
20564     
20565     getValue: function()
20566     {
20567         return this.value;
20568     },
20569     
20570     onClick: function(e) 
20571     {
20572         e.stopPropagation();
20573         e.preventDefault();
20574         
20575         var target = e.getTarget();
20576         
20577         if(target.nodeName.toLowerCase() === 'i'){
20578             target = Roo.get(target).dom.parentNode;
20579         }
20580         
20581         var nodeName = target.nodeName;
20582         var className = target.className;
20583         var html = target.innerHTML;
20584         
20585         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20586             return;
20587         }
20588         
20589         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20590         
20591         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20592         
20593         this.hide();
20594                         
20595     },
20596     
20597     picker : function()
20598     {
20599         return this.pickerEl;
20600     },
20601     
20602     fillMonths: function()
20603     {    
20604         var i = 0;
20605         var months = this.picker().select('>.datepicker-months td', true).first();
20606         
20607         months.dom.innerHTML = '';
20608         
20609         while (i < 12) {
20610             var month = {
20611                 tag: 'span',
20612                 cls: 'month',
20613                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20614             };
20615             
20616             months.createChild(month);
20617         }
20618         
20619     },
20620     
20621     update: function()
20622     {
20623         var _this = this;
20624         
20625         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20626             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20627         }
20628         
20629         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20630             e.removeClass('active');
20631             
20632             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20633                 e.addClass('active');
20634             }
20635         })
20636     },
20637     
20638     place: function()
20639     {
20640         if(this.isInline) {
20641             return;
20642         }
20643         
20644         this.picker().removeClass(['bottom', 'top']);
20645         
20646         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20647             /*
20648              * place to the top of element!
20649              *
20650              */
20651             
20652             this.picker().addClass('top');
20653             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20654             
20655             return;
20656         }
20657         
20658         this.picker().addClass('bottom');
20659         
20660         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20661     },
20662     
20663     onFocus : function()
20664     {
20665         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20666         this.show();
20667     },
20668     
20669     onBlur : function()
20670     {
20671         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20672         
20673         var d = this.inputEl().getValue();
20674         
20675         this.setValue(d);
20676                 
20677         this.hide();
20678     },
20679     
20680     show : function()
20681     {
20682         this.picker().show();
20683         this.picker().select('>.datepicker-months', true).first().show();
20684         this.update();
20685         this.place();
20686         
20687         this.fireEvent('show', this, this.date);
20688     },
20689     
20690     hide : function()
20691     {
20692         if(this.isInline) {
20693             return;
20694         }
20695         this.picker().hide();
20696         this.fireEvent('hide', this, this.date);
20697         
20698     },
20699     
20700     onMousedown: function(e)
20701     {
20702         e.stopPropagation();
20703         e.preventDefault();
20704     },
20705     
20706     keyup: function(e)
20707     {
20708         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20709         this.update();
20710     },
20711
20712     fireKey: function(e)
20713     {
20714         if (!this.picker().isVisible()){
20715             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20716                 this.show();
20717             }
20718             return;
20719         }
20720         
20721         var dir;
20722         
20723         switch(e.keyCode){
20724             case 27: // escape
20725                 this.hide();
20726                 e.preventDefault();
20727                 break;
20728             case 37: // left
20729             case 39: // right
20730                 dir = e.keyCode == 37 ? -1 : 1;
20731                 
20732                 this.vIndex = this.vIndex + dir;
20733                 
20734                 if(this.vIndex < 0){
20735                     this.vIndex = 0;
20736                 }
20737                 
20738                 if(this.vIndex > 11){
20739                     this.vIndex = 11;
20740                 }
20741                 
20742                 if(isNaN(this.vIndex)){
20743                     this.vIndex = 0;
20744                 }
20745                 
20746                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20747                 
20748                 break;
20749             case 38: // up
20750             case 40: // down
20751                 
20752                 dir = e.keyCode == 38 ? -1 : 1;
20753                 
20754                 this.vIndex = this.vIndex + dir * 4;
20755                 
20756                 if(this.vIndex < 0){
20757                     this.vIndex = 0;
20758                 }
20759                 
20760                 if(this.vIndex > 11){
20761                     this.vIndex = 11;
20762                 }
20763                 
20764                 if(isNaN(this.vIndex)){
20765                     this.vIndex = 0;
20766                 }
20767                 
20768                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20769                 break;
20770                 
20771             case 13: // enter
20772                 
20773                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20774                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20775                 }
20776                 
20777                 this.hide();
20778                 e.preventDefault();
20779                 break;
20780             case 9: // tab
20781                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20782                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20783                 }
20784                 this.hide();
20785                 break;
20786             case 16: // shift
20787             case 17: // ctrl
20788             case 18: // alt
20789                 break;
20790             default :
20791                 this.hide();
20792                 
20793         }
20794     },
20795     
20796     remove: function() 
20797     {
20798         this.picker().remove();
20799     }
20800    
20801 });
20802
20803 Roo.apply(Roo.bootstrap.MonthField,  {
20804     
20805     content : {
20806         tag: 'tbody',
20807         cn: [
20808         {
20809             tag: 'tr',
20810             cn: [
20811             {
20812                 tag: 'td',
20813                 colspan: '7'
20814             }
20815             ]
20816         }
20817         ]
20818     },
20819     
20820     dates:{
20821         en: {
20822             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20823             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20824         }
20825     }
20826 });
20827
20828 Roo.apply(Roo.bootstrap.MonthField,  {
20829   
20830     template : {
20831         tag: 'div',
20832         cls: 'datepicker dropdown-menu roo-dynamic',
20833         cn: [
20834             {
20835                 tag: 'div',
20836                 cls: 'datepicker-months',
20837                 cn: [
20838                 {
20839                     tag: 'table',
20840                     cls: 'table-condensed',
20841                     cn:[
20842                         Roo.bootstrap.DateField.content
20843                     ]
20844                 }
20845                 ]
20846             }
20847         ]
20848     }
20849 });
20850
20851  
20852
20853  
20854  /*
20855  * - LGPL
20856  *
20857  * CheckBox
20858  * 
20859  */
20860
20861 /**
20862  * @class Roo.bootstrap.CheckBox
20863  * @extends Roo.bootstrap.Input
20864  * Bootstrap CheckBox class
20865  * 
20866  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20867  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20868  * @cfg {String} boxLabel The text that appears beside the checkbox
20869  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20870  * @cfg {Boolean} checked initnal the element
20871  * @cfg {Boolean} inline inline the element (default false)
20872  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20873  * @cfg {String} tooltip label tooltip
20874  * 
20875  * @constructor
20876  * Create a new CheckBox
20877  * @param {Object} config The config object
20878  */
20879
20880 Roo.bootstrap.CheckBox = function(config){
20881     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20882    
20883     this.addEvents({
20884         /**
20885         * @event check
20886         * Fires when the element is checked or unchecked.
20887         * @param {Roo.bootstrap.CheckBox} this This input
20888         * @param {Boolean} checked The new checked value
20889         */
20890        check : true,
20891        /**
20892         * @event click
20893         * Fires when the element is click.
20894         * @param {Roo.bootstrap.CheckBox} this This input
20895         */
20896        click : true
20897     });
20898     
20899 };
20900
20901 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20902   
20903     inputType: 'checkbox',
20904     inputValue: 1,
20905     valueOff: 0,
20906     boxLabel: false,
20907     checked: false,
20908     weight : false,
20909     inline: false,
20910     tooltip : '',
20911     
20912     getAutoCreate : function()
20913     {
20914         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20915         
20916         var id = Roo.id();
20917         
20918         var cfg = {};
20919         
20920         cfg.cls = 'form-group ' + this.inputType; //input-group
20921         
20922         if(this.inline){
20923             cfg.cls += ' ' + this.inputType + '-inline';
20924         }
20925         
20926         var input =  {
20927             tag: 'input',
20928             id : id,
20929             type : this.inputType,
20930             value : this.inputValue,
20931             cls : 'roo-' + this.inputType, //'form-box',
20932             placeholder : this.placeholder || ''
20933             
20934         };
20935         
20936         if(this.inputType != 'radio'){
20937             var hidden =  {
20938                 tag: 'input',
20939                 type : 'hidden',
20940                 cls : 'roo-hidden-value',
20941                 value : this.checked ? this.inputValue : this.valueOff
20942             };
20943         }
20944         
20945             
20946         if (this.weight) { // Validity check?
20947             cfg.cls += " " + this.inputType + "-" + this.weight;
20948         }
20949         
20950         if (this.disabled) {
20951             input.disabled=true;
20952         }
20953         
20954         if(this.checked){
20955             input.checked = this.checked;
20956         }
20957         
20958         if (this.name) {
20959             
20960             input.name = this.name;
20961             
20962             if(this.inputType != 'radio'){
20963                 hidden.name = this.name;
20964                 input.name = '_hidden_' + this.name;
20965             }
20966         }
20967         
20968         if (this.size) {
20969             input.cls += ' input-' + this.size;
20970         }
20971         
20972         var settings=this;
20973         
20974         ['xs','sm','md','lg'].map(function(size){
20975             if (settings[size]) {
20976                 cfg.cls += ' col-' + size + '-' + settings[size];
20977             }
20978         });
20979         
20980         var inputblock = input;
20981          
20982         if (this.before || this.after) {
20983             
20984             inputblock = {
20985                 cls : 'input-group',
20986                 cn :  [] 
20987             };
20988             
20989             if (this.before) {
20990                 inputblock.cn.push({
20991                     tag :'span',
20992                     cls : 'input-group-addon',
20993                     html : this.before
20994                 });
20995             }
20996             
20997             inputblock.cn.push(input);
20998             
20999             if(this.inputType != 'radio'){
21000                 inputblock.cn.push(hidden);
21001             }
21002             
21003             if (this.after) {
21004                 inputblock.cn.push({
21005                     tag :'span',
21006                     cls : 'input-group-addon',
21007                     html : this.after
21008                 });
21009             }
21010             
21011         }
21012         
21013         if (align ==='left' && this.fieldLabel.length) {
21014 //                Roo.log("left and has label");
21015             cfg.cn = [
21016                 {
21017                     tag: 'label',
21018                     'for' :  id,
21019                     cls : 'control-label',
21020                     html : this.fieldLabel
21021                 },
21022                 {
21023                     cls : "", 
21024                     cn: [
21025                         inputblock
21026                     ]
21027                 }
21028             ];
21029             
21030             if(this.labelWidth > 12){
21031                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21032             }
21033             
21034             if(this.labelWidth < 13 && this.labelmd == 0){
21035                 this.labelmd = this.labelWidth;
21036             }
21037             
21038             if(this.labellg > 0){
21039                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21040                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21041             }
21042             
21043             if(this.labelmd > 0){
21044                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21045                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21046             }
21047             
21048             if(this.labelsm > 0){
21049                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21050                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21051             }
21052             
21053             if(this.labelxs > 0){
21054                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21055                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21056             }
21057             
21058         } else if ( this.fieldLabel.length) {
21059 //                Roo.log(" label");
21060                 cfg.cn = [
21061                    
21062                     {
21063                         tag: this.boxLabel ? 'span' : 'label',
21064                         'for': id,
21065                         cls: 'control-label box-input-label',
21066                         //cls : 'input-group-addon',
21067                         html : this.fieldLabel
21068                     },
21069                     
21070                     inputblock
21071                     
21072                 ];
21073
21074         } else {
21075             
21076 //                Roo.log(" no label && no align");
21077                 cfg.cn = [  inputblock ] ;
21078                 
21079                 
21080         }
21081         
21082         if(this.boxLabel){
21083              var boxLabelCfg = {
21084                 tag: 'label',
21085                 //'for': id, // box label is handled by onclick - so no for...
21086                 cls: 'box-label',
21087                 html: this.boxLabel
21088             };
21089             
21090             if(this.tooltip){
21091                 boxLabelCfg.tooltip = this.tooltip;
21092             }
21093              
21094             cfg.cn.push(boxLabelCfg);
21095         }
21096         
21097         if(this.inputType != 'radio'){
21098             cfg.cn.push(hidden);
21099         }
21100         
21101         return cfg;
21102         
21103     },
21104     
21105     /**
21106      * return the real input element.
21107      */
21108     inputEl: function ()
21109     {
21110         return this.el.select('input.roo-' + this.inputType,true).first();
21111     },
21112     hiddenEl: function ()
21113     {
21114         return this.el.select('input.roo-hidden-value',true).first();
21115     },
21116     
21117     labelEl: function()
21118     {
21119         return this.el.select('label.control-label',true).first();
21120     },
21121     /* depricated... */
21122     
21123     label: function()
21124     {
21125         return this.labelEl();
21126     },
21127     
21128     boxLabelEl: function()
21129     {
21130         return this.el.select('label.box-label',true).first();
21131     },
21132     
21133     initEvents : function()
21134     {
21135 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21136         
21137         this.inputEl().on('click', this.onClick,  this);
21138         
21139         if (this.boxLabel) { 
21140             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21141         }
21142         
21143         this.startValue = this.getValue();
21144         
21145         if(this.groupId){
21146             Roo.bootstrap.CheckBox.register(this);
21147         }
21148     },
21149     
21150     onClick : function(e)
21151     {   
21152         if(this.fireEvent('click', this, e) !== false){
21153             this.setChecked(!this.checked);
21154         }
21155         
21156     },
21157     
21158     setChecked : function(state,suppressEvent)
21159     {
21160         this.startValue = this.getValue();
21161
21162         if(this.inputType == 'radio'){
21163             
21164             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21165                 e.dom.checked = false;
21166             });
21167             
21168             this.inputEl().dom.checked = true;
21169             
21170             this.inputEl().dom.value = this.inputValue;
21171             
21172             if(suppressEvent !== true){
21173                 this.fireEvent('check', this, true);
21174             }
21175             
21176             this.validate();
21177             
21178             return;
21179         }
21180         
21181         this.checked = state;
21182         
21183         this.inputEl().dom.checked = state;
21184         
21185         
21186         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21187         
21188         if(suppressEvent !== true){
21189             this.fireEvent('check', this, state);
21190         }
21191         
21192         this.validate();
21193     },
21194     
21195     getValue : function()
21196     {
21197         if(this.inputType == 'radio'){
21198             return this.getGroupValue();
21199         }
21200         
21201         return this.hiddenEl().dom.value;
21202         
21203     },
21204     
21205     getGroupValue : function()
21206     {
21207         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21208             return '';
21209         }
21210         
21211         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21212     },
21213     
21214     setValue : function(v,suppressEvent)
21215     {
21216         if(this.inputType == 'radio'){
21217             this.setGroupValue(v, suppressEvent);
21218             return;
21219         }
21220         
21221         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21222         
21223         this.validate();
21224     },
21225     
21226     setGroupValue : function(v, suppressEvent)
21227     {
21228         this.startValue = this.getValue();
21229         
21230         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21231             e.dom.checked = false;
21232             
21233             if(e.dom.value == v){
21234                 e.dom.checked = true;
21235             }
21236         });
21237         
21238         if(suppressEvent !== true){
21239             this.fireEvent('check', this, true);
21240         }
21241
21242         this.validate();
21243         
21244         return;
21245     },
21246     
21247     validate : function()
21248     {
21249         if(this.getVisibilityEl().hasClass('hidden')){
21250             return true;
21251         }
21252         
21253         if(
21254                 this.disabled || 
21255                 (this.inputType == 'radio' && this.validateRadio()) ||
21256                 (this.inputType == 'checkbox' && this.validateCheckbox())
21257         ){
21258             this.markValid();
21259             return true;
21260         }
21261         
21262         this.markInvalid();
21263         return false;
21264     },
21265     
21266     validateRadio : function()
21267     {
21268         if(this.getVisibilityEl().hasClass('hidden')){
21269             return true;
21270         }
21271         
21272         if(this.allowBlank){
21273             return true;
21274         }
21275         
21276         var valid = false;
21277         
21278         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21279             if(!e.dom.checked){
21280                 return;
21281             }
21282             
21283             valid = true;
21284             
21285             return false;
21286         });
21287         
21288         return valid;
21289     },
21290     
21291     validateCheckbox : function()
21292     {
21293         if(!this.groupId){
21294             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21295             //return (this.getValue() == this.inputValue) ? true : false;
21296         }
21297         
21298         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21299         
21300         if(!group){
21301             return false;
21302         }
21303         
21304         var r = false;
21305         
21306         for(var i in group){
21307             if(group[i].el.isVisible(true)){
21308                 r = false;
21309                 break;
21310             }
21311             
21312             r = true;
21313         }
21314         
21315         for(var i in group){
21316             if(r){
21317                 break;
21318             }
21319             
21320             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21321         }
21322         
21323         return r;
21324     },
21325     
21326     /**
21327      * Mark this field as valid
21328      */
21329     markValid : function()
21330     {
21331         var _this = this;
21332         
21333         this.fireEvent('valid', this);
21334         
21335         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21336         
21337         if(this.groupId){
21338             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21339         }
21340         
21341         if(label){
21342             label.markValid();
21343         }
21344
21345         if(this.inputType == 'radio'){
21346             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21347                 var fg = e.findParent('.form-group', false, true);
21348                 if (Roo.bootstrap.version == 3) {
21349                     fg.removeClass([_this.invalidClass, _this.validClass]);
21350                     fg.addClass(_this.validClass);
21351                 } else {
21352                     fg.removeClass(['is-valid', 'is-invalid']);
21353                     fg.addClass('is-valid');
21354                 }
21355             });
21356             
21357             return;
21358         }
21359
21360         if(!this.groupId){
21361             var fg = this.el.findParent('.form-group', false, true);
21362             if (Roo.bootstrap.version == 3) {
21363                 fg.removeClass([this.invalidClass, this.validClass]);
21364                 fg.addClass(this.validClass);
21365             } else {
21366                 fg.removeClass(['is-valid', 'is-invalid']);
21367                 fg.addClass('is-valid');
21368             }
21369             return;
21370         }
21371         
21372         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21373         
21374         if(!group){
21375             return;
21376         }
21377         
21378         for(var i in group){
21379             var fg = group[i].el.findParent('.form-group', false, true);
21380             if (Roo.bootstrap.version == 3) {
21381                 fg.removeClass([this.invalidClass, this.validClass]);
21382                 fg.addClass(this.validClass);
21383             } else {
21384                 fg.removeClass(['is-valid', 'is-invalid']);
21385                 fg.addClass('is-valid');
21386             }
21387         }
21388     },
21389     
21390      /**
21391      * Mark this field as invalid
21392      * @param {String} msg The validation message
21393      */
21394     markInvalid : function(msg)
21395     {
21396         if(this.allowBlank){
21397             return;
21398         }
21399         
21400         var _this = this;
21401         
21402         this.fireEvent('invalid', this, msg);
21403         
21404         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21405         
21406         if(this.groupId){
21407             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21408         }
21409         
21410         if(label){
21411             label.markInvalid();
21412         }
21413             
21414         if(this.inputType == 'radio'){
21415             
21416             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21417                 var fg = e.findParent('.form-group', false, true);
21418                 if (Roo.bootstrap.version == 3) {
21419                     fg.removeClass([_this.invalidClass, _this.validClass]);
21420                     fg.addClass(_this.invalidClass);
21421                 } else {
21422                     fg.removeClass(['is-invalid', 'is-valid']);
21423                     fg.addClass('is-invalid');
21424                 }
21425             });
21426             
21427             return;
21428         }
21429         
21430         if(!this.groupId){
21431             var fg = this.el.findParent('.form-group', false, true);
21432             if (Roo.bootstrap.version == 3) {
21433                 fg.removeClass([_this.invalidClass, _this.validClass]);
21434                 fg.addClass(_this.invalidClass);
21435             } else {
21436                 fg.removeClass(['is-invalid', 'is-valid']);
21437                 fg.addClass('is-invalid');
21438             }
21439             return;
21440         }
21441         
21442         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21443         
21444         if(!group){
21445             return;
21446         }
21447         
21448         for(var i in group){
21449             var fg = group[i].el.findParent('.form-group', false, true);
21450             if (Roo.bootstrap.version == 3) {
21451                 fg.removeClass([_this.invalidClass, _this.validClass]);
21452                 fg.addClass(_this.invalidClass);
21453             } else {
21454                 fg.removeClass(['is-invalid', 'is-valid']);
21455                 fg.addClass('is-invalid');
21456             }
21457         }
21458         
21459     },
21460     
21461     clearInvalid : function()
21462     {
21463         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21464         
21465         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21466         
21467         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21468         
21469         if (label && label.iconEl) {
21470             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21471             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21472         }
21473     },
21474     
21475     disable : function()
21476     {
21477         if(this.inputType != 'radio'){
21478             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21479             return;
21480         }
21481         
21482         var _this = this;
21483         
21484         if(this.rendered){
21485             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21486                 _this.getActionEl().addClass(this.disabledClass);
21487                 e.dom.disabled = true;
21488             });
21489         }
21490         
21491         this.disabled = true;
21492         this.fireEvent("disable", this);
21493         return this;
21494     },
21495
21496     enable : function()
21497     {
21498         if(this.inputType != 'radio'){
21499             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21500             return;
21501         }
21502         
21503         var _this = this;
21504         
21505         if(this.rendered){
21506             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21507                 _this.getActionEl().removeClass(this.disabledClass);
21508                 e.dom.disabled = false;
21509             });
21510         }
21511         
21512         this.disabled = false;
21513         this.fireEvent("enable", this);
21514         return this;
21515     },
21516     
21517     setBoxLabel : function(v)
21518     {
21519         this.boxLabel = v;
21520         
21521         if(this.rendered){
21522             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21523         }
21524     }
21525
21526 });
21527
21528 Roo.apply(Roo.bootstrap.CheckBox, {
21529     
21530     groups: {},
21531     
21532      /**
21533     * register a CheckBox Group
21534     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21535     */
21536     register : function(checkbox)
21537     {
21538         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21539             this.groups[checkbox.groupId] = {};
21540         }
21541         
21542         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21543             return;
21544         }
21545         
21546         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21547         
21548     },
21549     /**
21550     * fetch a CheckBox Group based on the group ID
21551     * @param {string} the group ID
21552     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21553     */
21554     get: function(groupId) {
21555         if (typeof(this.groups[groupId]) == 'undefined') {
21556             return false;
21557         }
21558         
21559         return this.groups[groupId] ;
21560     }
21561     
21562     
21563 });
21564 /*
21565  * - LGPL
21566  *
21567  * RadioItem
21568  * 
21569  */
21570
21571 /**
21572  * @class Roo.bootstrap.Radio
21573  * @extends Roo.bootstrap.Component
21574  * Bootstrap Radio class
21575  * @cfg {String} boxLabel - the label associated
21576  * @cfg {String} value - the value of radio
21577  * 
21578  * @constructor
21579  * Create a new Radio
21580  * @param {Object} config The config object
21581  */
21582 Roo.bootstrap.Radio = function(config){
21583     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21584     
21585 };
21586
21587 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21588     
21589     boxLabel : '',
21590     
21591     value : '',
21592     
21593     getAutoCreate : function()
21594     {
21595         var cfg = {
21596             tag : 'div',
21597             cls : 'form-group radio',
21598             cn : [
21599                 {
21600                     tag : 'label',
21601                     cls : 'box-label',
21602                     html : this.boxLabel
21603                 }
21604             ]
21605         };
21606         
21607         return cfg;
21608     },
21609     
21610     initEvents : function() 
21611     {
21612         this.parent().register(this);
21613         
21614         this.el.on('click', this.onClick, this);
21615         
21616     },
21617     
21618     onClick : function(e)
21619     {
21620         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21621             this.setChecked(true);
21622         }
21623     },
21624     
21625     setChecked : function(state, suppressEvent)
21626     {
21627         this.parent().setValue(this.value, suppressEvent);
21628         
21629     },
21630     
21631     setBoxLabel : function(v)
21632     {
21633         this.boxLabel = v;
21634         
21635         if(this.rendered){
21636             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21637         }
21638     }
21639     
21640 });
21641  
21642
21643  /*
21644  * - LGPL
21645  *
21646  * Input
21647  * 
21648  */
21649
21650 /**
21651  * @class Roo.bootstrap.SecurePass
21652  * @extends Roo.bootstrap.Input
21653  * Bootstrap SecurePass class
21654  *
21655  * 
21656  * @constructor
21657  * Create a new SecurePass
21658  * @param {Object} config The config object
21659  */
21660  
21661 Roo.bootstrap.SecurePass = function (config) {
21662     // these go here, so the translation tool can replace them..
21663     this.errors = {
21664         PwdEmpty: "Please type a password, and then retype it to confirm.",
21665         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21666         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21667         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21668         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21669         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21670         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21671         TooWeak: "Your password is Too Weak."
21672     },
21673     this.meterLabel = "Password strength:";
21674     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21675     this.meterClass = [
21676         "roo-password-meter-tooweak", 
21677         "roo-password-meter-weak", 
21678         "roo-password-meter-medium", 
21679         "roo-password-meter-strong", 
21680         "roo-password-meter-grey"
21681     ];
21682     
21683     this.errors = {};
21684     
21685     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21686 }
21687
21688 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21689     /**
21690      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21691      * {
21692      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21693      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21694      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21695      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21696      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21697      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21698      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21699      * })
21700      */
21701     // private
21702     
21703     meterWidth: 300,
21704     errorMsg :'',    
21705     errors: false,
21706     imageRoot: '/',
21707     /**
21708      * @cfg {String/Object} Label for the strength meter (defaults to
21709      * 'Password strength:')
21710      */
21711     // private
21712     meterLabel: '',
21713     /**
21714      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21715      * ['Weak', 'Medium', 'Strong'])
21716      */
21717     // private    
21718     pwdStrengths: false,    
21719     // private
21720     strength: 0,
21721     // private
21722     _lastPwd: null,
21723     // private
21724     kCapitalLetter: 0,
21725     kSmallLetter: 1,
21726     kDigit: 2,
21727     kPunctuation: 3,
21728     
21729     insecure: false,
21730     // private
21731     initEvents: function ()
21732     {
21733         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21734
21735         if (this.el.is('input[type=password]') && Roo.isSafari) {
21736             this.el.on('keydown', this.SafariOnKeyDown, this);
21737         }
21738
21739         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21740     },
21741     // private
21742     onRender: function (ct, position)
21743     {
21744         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21745         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21746         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21747
21748         this.trigger.createChild({
21749                    cn: [
21750                     {
21751                     //id: 'PwdMeter',
21752                     tag: 'div',
21753                     cls: 'roo-password-meter-grey col-xs-12',
21754                     style: {
21755                         //width: 0,
21756                         //width: this.meterWidth + 'px'                                                
21757                         }
21758                     },
21759                     {                            
21760                          cls: 'roo-password-meter-text'                          
21761                     }
21762                 ]            
21763         });
21764
21765          
21766         if (this.hideTrigger) {
21767             this.trigger.setDisplayed(false);
21768         }
21769         this.setSize(this.width || '', this.height || '');
21770     },
21771     // private
21772     onDestroy: function ()
21773     {
21774         if (this.trigger) {
21775             this.trigger.removeAllListeners();
21776             this.trigger.remove();
21777         }
21778         if (this.wrap) {
21779             this.wrap.remove();
21780         }
21781         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21782     },
21783     // private
21784     checkStrength: function ()
21785     {
21786         var pwd = this.inputEl().getValue();
21787         if (pwd == this._lastPwd) {
21788             return;
21789         }
21790
21791         var strength;
21792         if (this.ClientSideStrongPassword(pwd)) {
21793             strength = 3;
21794         } else if (this.ClientSideMediumPassword(pwd)) {
21795             strength = 2;
21796         } else if (this.ClientSideWeakPassword(pwd)) {
21797             strength = 1;
21798         } else {
21799             strength = 0;
21800         }
21801         
21802         Roo.log('strength1: ' + strength);
21803         
21804         //var pm = this.trigger.child('div/div/div').dom;
21805         var pm = this.trigger.child('div/div');
21806         pm.removeClass(this.meterClass);
21807         pm.addClass(this.meterClass[strength]);
21808                 
21809         
21810         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21811                 
21812         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21813         
21814         this._lastPwd = pwd;
21815     },
21816     reset: function ()
21817     {
21818         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21819         
21820         this._lastPwd = '';
21821         
21822         var pm = this.trigger.child('div/div');
21823         pm.removeClass(this.meterClass);
21824         pm.addClass('roo-password-meter-grey');        
21825         
21826         
21827         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21828         
21829         pt.innerHTML = '';
21830         this.inputEl().dom.type='password';
21831     },
21832     // private
21833     validateValue: function (value)
21834     {
21835         
21836         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21837             return false;
21838         }
21839         if (value.length == 0) {
21840             if (this.allowBlank) {
21841                 this.clearInvalid();
21842                 return true;
21843             }
21844
21845             this.markInvalid(this.errors.PwdEmpty);
21846             this.errorMsg = this.errors.PwdEmpty;
21847             return false;
21848         }
21849         
21850         if(this.insecure){
21851             return true;
21852         }
21853         
21854         if ('[\x21-\x7e]*'.match(value)) {
21855             this.markInvalid(this.errors.PwdBadChar);
21856             this.errorMsg = this.errors.PwdBadChar;
21857             return false;
21858         }
21859         if (value.length < 6) {
21860             this.markInvalid(this.errors.PwdShort);
21861             this.errorMsg = this.errors.PwdShort;
21862             return false;
21863         }
21864         if (value.length > 16) {
21865             this.markInvalid(this.errors.PwdLong);
21866             this.errorMsg = this.errors.PwdLong;
21867             return false;
21868         }
21869         var strength;
21870         if (this.ClientSideStrongPassword(value)) {
21871             strength = 3;
21872         } else if (this.ClientSideMediumPassword(value)) {
21873             strength = 2;
21874         } else if (this.ClientSideWeakPassword(value)) {
21875             strength = 1;
21876         } else {
21877             strength = 0;
21878         }
21879
21880         
21881         if (strength < 2) {
21882             //this.markInvalid(this.errors.TooWeak);
21883             this.errorMsg = this.errors.TooWeak;
21884             //return false;
21885         }
21886         
21887         
21888         console.log('strength2: ' + strength);
21889         
21890         //var pm = this.trigger.child('div/div/div').dom;
21891         
21892         var pm = this.trigger.child('div/div');
21893         pm.removeClass(this.meterClass);
21894         pm.addClass(this.meterClass[strength]);
21895                 
21896         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21897                 
21898         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21899         
21900         this.errorMsg = ''; 
21901         return true;
21902     },
21903     // private
21904     CharacterSetChecks: function (type)
21905     {
21906         this.type = type;
21907         this.fResult = false;
21908     },
21909     // private
21910     isctype: function (character, type)
21911     {
21912         switch (type) {  
21913             case this.kCapitalLetter:
21914                 if (character >= 'A' && character <= 'Z') {
21915                     return true;
21916                 }
21917                 break;
21918             
21919             case this.kSmallLetter:
21920                 if (character >= 'a' && character <= 'z') {
21921                     return true;
21922                 }
21923                 break;
21924             
21925             case this.kDigit:
21926                 if (character >= '0' && character <= '9') {
21927                     return true;
21928                 }
21929                 break;
21930             
21931             case this.kPunctuation:
21932                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21933                     return true;
21934                 }
21935                 break;
21936             
21937             default:
21938                 return false;
21939         }
21940
21941     },
21942     // private
21943     IsLongEnough: function (pwd, size)
21944     {
21945         return !(pwd == null || isNaN(size) || pwd.length < size);
21946     },
21947     // private
21948     SpansEnoughCharacterSets: function (word, nb)
21949     {
21950         if (!this.IsLongEnough(word, nb))
21951         {
21952             return false;
21953         }
21954
21955         var characterSetChecks = new Array(
21956             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21957             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21958         );
21959         
21960         for (var index = 0; index < word.length; ++index) {
21961             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21962                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21963                     characterSetChecks[nCharSet].fResult = true;
21964                     break;
21965                 }
21966             }
21967         }
21968
21969         var nCharSets = 0;
21970         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21971             if (characterSetChecks[nCharSet].fResult) {
21972                 ++nCharSets;
21973             }
21974         }
21975
21976         if (nCharSets < nb) {
21977             return false;
21978         }
21979         return true;
21980     },
21981     // private
21982     ClientSideStrongPassword: function (pwd)
21983     {
21984         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21985     },
21986     // private
21987     ClientSideMediumPassword: function (pwd)
21988     {
21989         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21990     },
21991     // private
21992     ClientSideWeakPassword: function (pwd)
21993     {
21994         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21995     }
21996           
21997 })//<script type="text/javascript">
21998
21999 /*
22000  * Based  Ext JS Library 1.1.1
22001  * Copyright(c) 2006-2007, Ext JS, LLC.
22002  * LGPL
22003  *
22004  */
22005  
22006 /**
22007  * @class Roo.HtmlEditorCore
22008  * @extends Roo.Component
22009  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22010  *
22011  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22012  */
22013
22014 Roo.HtmlEditorCore = function(config){
22015     
22016     
22017     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22018     
22019     
22020     this.addEvents({
22021         /**
22022          * @event initialize
22023          * Fires when the editor is fully initialized (including the iframe)
22024          * @param {Roo.HtmlEditorCore} this
22025          */
22026         initialize: true,
22027         /**
22028          * @event activate
22029          * Fires when the editor is first receives the focus. Any insertion must wait
22030          * until after this event.
22031          * @param {Roo.HtmlEditorCore} this
22032          */
22033         activate: true,
22034          /**
22035          * @event beforesync
22036          * Fires before the textarea is updated with content from the editor iframe. Return false
22037          * to cancel the sync.
22038          * @param {Roo.HtmlEditorCore} this
22039          * @param {String} html
22040          */
22041         beforesync: true,
22042          /**
22043          * @event beforepush
22044          * Fires before the iframe editor is updated with content from the textarea. Return false
22045          * to cancel the push.
22046          * @param {Roo.HtmlEditorCore} this
22047          * @param {String} html
22048          */
22049         beforepush: true,
22050          /**
22051          * @event sync
22052          * Fires when the textarea is updated with content from the editor iframe.
22053          * @param {Roo.HtmlEditorCore} this
22054          * @param {String} html
22055          */
22056         sync: true,
22057          /**
22058          * @event push
22059          * Fires when the iframe editor is updated with content from the textarea.
22060          * @param {Roo.HtmlEditorCore} this
22061          * @param {String} html
22062          */
22063         push: true,
22064         
22065         /**
22066          * @event editorevent
22067          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22068          * @param {Roo.HtmlEditorCore} this
22069          */
22070         editorevent: true
22071         
22072     });
22073     
22074     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22075     
22076     // defaults : white / black...
22077     this.applyBlacklists();
22078     
22079     
22080     
22081 };
22082
22083
22084 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22085
22086
22087      /**
22088      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22089      */
22090     
22091     owner : false,
22092     
22093      /**
22094      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22095      *                        Roo.resizable.
22096      */
22097     resizable : false,
22098      /**
22099      * @cfg {Number} height (in pixels)
22100      */   
22101     height: 300,
22102    /**
22103      * @cfg {Number} width (in pixels)
22104      */   
22105     width: 500,
22106     
22107     /**
22108      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22109      * 
22110      */
22111     stylesheets: false,
22112     
22113     // id of frame..
22114     frameId: false,
22115     
22116     // private properties
22117     validationEvent : false,
22118     deferHeight: true,
22119     initialized : false,
22120     activated : false,
22121     sourceEditMode : false,
22122     onFocus : Roo.emptyFn,
22123     iframePad:3,
22124     hideMode:'offsets',
22125     
22126     clearUp: true,
22127     
22128     // blacklist + whitelisted elements..
22129     black: false,
22130     white: false,
22131      
22132     bodyCls : '',
22133
22134     /**
22135      * Protected method that will not generally be called directly. It
22136      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22137      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22138      */
22139     getDocMarkup : function(){
22140         // body styles..
22141         var st = '';
22142         
22143         // inherit styels from page...?? 
22144         if (this.stylesheets === false) {
22145             
22146             Roo.get(document.head).select('style').each(function(node) {
22147                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22148             });
22149             
22150             Roo.get(document.head).select('link').each(function(node) { 
22151                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22152             });
22153             
22154         } else if (!this.stylesheets.length) {
22155                 // simple..
22156                 st = '<style type="text/css">' +
22157                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22158                    '</style>';
22159         } else { 
22160             st = '<style type="text/css">' +
22161                     this.stylesheets +
22162                 '</style>';
22163         }
22164         
22165         st +=  '<style type="text/css">' +
22166             'IMG { cursor: pointer } ' +
22167         '</style>';
22168
22169         var cls = 'roo-htmleditor-body';
22170         
22171         if(this.bodyCls.length){
22172             cls += ' ' + this.bodyCls;
22173         }
22174         
22175         return '<html><head>' + st  +
22176             //<style type="text/css">' +
22177             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22178             //'</style>' +
22179             ' </head><body class="' +  cls + '"></body></html>';
22180     },
22181
22182     // private
22183     onRender : function(ct, position)
22184     {
22185         var _t = this;
22186         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22187         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22188         
22189         
22190         this.el.dom.style.border = '0 none';
22191         this.el.dom.setAttribute('tabIndex', -1);
22192         this.el.addClass('x-hidden hide');
22193         
22194         
22195         
22196         if(Roo.isIE){ // fix IE 1px bogus margin
22197             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22198         }
22199        
22200         
22201         this.frameId = Roo.id();
22202         
22203          
22204         
22205         var iframe = this.owner.wrap.createChild({
22206             tag: 'iframe',
22207             cls: 'form-control', // bootstrap..
22208             id: this.frameId,
22209             name: this.frameId,
22210             frameBorder : 'no',
22211             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22212         }, this.el
22213         );
22214         
22215         
22216         this.iframe = iframe.dom;
22217
22218          this.assignDocWin();
22219         
22220         this.doc.designMode = 'on';
22221        
22222         this.doc.open();
22223         this.doc.write(this.getDocMarkup());
22224         this.doc.close();
22225
22226         
22227         var task = { // must defer to wait for browser to be ready
22228             run : function(){
22229                 //console.log("run task?" + this.doc.readyState);
22230                 this.assignDocWin();
22231                 if(this.doc.body || this.doc.readyState == 'complete'){
22232                     try {
22233                         this.doc.designMode="on";
22234                     } catch (e) {
22235                         return;
22236                     }
22237                     Roo.TaskMgr.stop(task);
22238                     this.initEditor.defer(10, this);
22239                 }
22240             },
22241             interval : 10,
22242             duration: 10000,
22243             scope: this
22244         };
22245         Roo.TaskMgr.start(task);
22246
22247     },
22248
22249     // private
22250     onResize : function(w, h)
22251     {
22252          Roo.log('resize: ' +w + ',' + h );
22253         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22254         if(!this.iframe){
22255             return;
22256         }
22257         if(typeof w == 'number'){
22258             
22259             this.iframe.style.width = w + 'px';
22260         }
22261         if(typeof h == 'number'){
22262             
22263             this.iframe.style.height = h + 'px';
22264             if(this.doc){
22265                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22266             }
22267         }
22268         
22269     },
22270
22271     /**
22272      * Toggles the editor between standard and source edit mode.
22273      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22274      */
22275     toggleSourceEdit : function(sourceEditMode){
22276         
22277         this.sourceEditMode = sourceEditMode === true;
22278         
22279         if(this.sourceEditMode){
22280  
22281             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22282             
22283         }else{
22284             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22285             //this.iframe.className = '';
22286             this.deferFocus();
22287         }
22288         //this.setSize(this.owner.wrap.getSize());
22289         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22290     },
22291
22292     
22293   
22294
22295     /**
22296      * Protected method that will not generally be called directly. If you need/want
22297      * custom HTML cleanup, this is the method you should override.
22298      * @param {String} html The HTML to be cleaned
22299      * return {String} The cleaned HTML
22300      */
22301     cleanHtml : function(html){
22302         html = String(html);
22303         if(html.length > 5){
22304             if(Roo.isSafari){ // strip safari nonsense
22305                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22306             }
22307         }
22308         if(html == '&nbsp;'){
22309             html = '';
22310         }
22311         return html;
22312     },
22313
22314     /**
22315      * HTML Editor -> Textarea
22316      * Protected method that will not generally be called directly. Syncs the contents
22317      * of the editor iframe with the textarea.
22318      */
22319     syncValue : function(){
22320         if(this.initialized){
22321             var bd = (this.doc.body || this.doc.documentElement);
22322             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22323             var html = bd.innerHTML;
22324             if(Roo.isSafari){
22325                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22326                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22327                 if(m && m[1]){
22328                     html = '<div style="'+m[0]+'">' + html + '</div>';
22329                 }
22330             }
22331             html = this.cleanHtml(html);
22332             // fix up the special chars.. normaly like back quotes in word...
22333             // however we do not want to do this with chinese..
22334             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22335                 var cc = b.charCodeAt();
22336                 if (
22337                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22338                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22339                     (cc >= 0xf900 && cc < 0xfb00 )
22340                 ) {
22341                         return b;
22342                 }
22343                 return "&#"+cc+";" 
22344             });
22345             if(this.owner.fireEvent('beforesync', this, html) !== false){
22346                 this.el.dom.value = html;
22347                 this.owner.fireEvent('sync', this, html);
22348             }
22349         }
22350     },
22351
22352     /**
22353      * Protected method that will not generally be called directly. Pushes the value of the textarea
22354      * into the iframe editor.
22355      */
22356     pushValue : function(){
22357         if(this.initialized){
22358             var v = this.el.dom.value.trim();
22359             
22360 //            if(v.length < 1){
22361 //                v = '&#160;';
22362 //            }
22363             
22364             if(this.owner.fireEvent('beforepush', this, v) !== false){
22365                 var d = (this.doc.body || this.doc.documentElement);
22366                 d.innerHTML = v;
22367                 this.cleanUpPaste();
22368                 this.el.dom.value = d.innerHTML;
22369                 this.owner.fireEvent('push', this, v);
22370             }
22371         }
22372     },
22373
22374     // private
22375     deferFocus : function(){
22376         this.focus.defer(10, this);
22377     },
22378
22379     // doc'ed in Field
22380     focus : function(){
22381         if(this.win && !this.sourceEditMode){
22382             this.win.focus();
22383         }else{
22384             this.el.focus();
22385         }
22386     },
22387     
22388     assignDocWin: function()
22389     {
22390         var iframe = this.iframe;
22391         
22392          if(Roo.isIE){
22393             this.doc = iframe.contentWindow.document;
22394             this.win = iframe.contentWindow;
22395         } else {
22396 //            if (!Roo.get(this.frameId)) {
22397 //                return;
22398 //            }
22399 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22400 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22401             
22402             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22403                 return;
22404             }
22405             
22406             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22407             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22408         }
22409     },
22410     
22411     // private
22412     initEditor : function(){
22413         //console.log("INIT EDITOR");
22414         this.assignDocWin();
22415         
22416         
22417         
22418         this.doc.designMode="on";
22419         this.doc.open();
22420         this.doc.write(this.getDocMarkup());
22421         this.doc.close();
22422         
22423         var dbody = (this.doc.body || this.doc.documentElement);
22424         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22425         // this copies styles from the containing element into thsi one..
22426         // not sure why we need all of this..
22427         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22428         
22429         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22430         //ss['background-attachment'] = 'fixed'; // w3c
22431         dbody.bgProperties = 'fixed'; // ie
22432         //Roo.DomHelper.applyStyles(dbody, ss);
22433         Roo.EventManager.on(this.doc, {
22434             //'mousedown': this.onEditorEvent,
22435             'mouseup': this.onEditorEvent,
22436             'dblclick': this.onEditorEvent,
22437             'click': this.onEditorEvent,
22438             'keyup': this.onEditorEvent,
22439             buffer:100,
22440             scope: this
22441         });
22442         if(Roo.isGecko){
22443             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22444         }
22445         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22446             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22447         }
22448         this.initialized = true;
22449
22450         this.owner.fireEvent('initialize', this);
22451         this.pushValue();
22452     },
22453
22454     // private
22455     onDestroy : function(){
22456         
22457         
22458         
22459         if(this.rendered){
22460             
22461             //for (var i =0; i < this.toolbars.length;i++) {
22462             //    // fixme - ask toolbars for heights?
22463             //    this.toolbars[i].onDestroy();
22464            // }
22465             
22466             //this.wrap.dom.innerHTML = '';
22467             //this.wrap.remove();
22468         }
22469     },
22470
22471     // private
22472     onFirstFocus : function(){
22473         
22474         this.assignDocWin();
22475         
22476         
22477         this.activated = true;
22478          
22479     
22480         if(Roo.isGecko){ // prevent silly gecko errors
22481             this.win.focus();
22482             var s = this.win.getSelection();
22483             if(!s.focusNode || s.focusNode.nodeType != 3){
22484                 var r = s.getRangeAt(0);
22485                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22486                 r.collapse(true);
22487                 this.deferFocus();
22488             }
22489             try{
22490                 this.execCmd('useCSS', true);
22491                 this.execCmd('styleWithCSS', false);
22492             }catch(e){}
22493         }
22494         this.owner.fireEvent('activate', this);
22495     },
22496
22497     // private
22498     adjustFont: function(btn){
22499         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22500         //if(Roo.isSafari){ // safari
22501         //    adjust *= 2;
22502        // }
22503         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22504         if(Roo.isSafari){ // safari
22505             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22506             v =  (v < 10) ? 10 : v;
22507             v =  (v > 48) ? 48 : v;
22508             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22509             
22510         }
22511         
22512         
22513         v = Math.max(1, v+adjust);
22514         
22515         this.execCmd('FontSize', v  );
22516     },
22517
22518     onEditorEvent : function(e)
22519     {
22520         this.owner.fireEvent('editorevent', this, e);
22521       //  this.updateToolbar();
22522         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22523     },
22524
22525     insertTag : function(tg)
22526     {
22527         // could be a bit smarter... -> wrap the current selected tRoo..
22528         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22529             
22530             range = this.createRange(this.getSelection());
22531             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22532             wrappingNode.appendChild(range.extractContents());
22533             range.insertNode(wrappingNode);
22534
22535             return;
22536             
22537             
22538             
22539         }
22540         this.execCmd("formatblock",   tg);
22541         
22542     },
22543     
22544     insertText : function(txt)
22545     {
22546         
22547         
22548         var range = this.createRange();
22549         range.deleteContents();
22550                //alert(Sender.getAttribute('label'));
22551                
22552         range.insertNode(this.doc.createTextNode(txt));
22553     } ,
22554     
22555      
22556
22557     /**
22558      * Executes a Midas editor command on the editor document and performs necessary focus and
22559      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22560      * @param {String} cmd The Midas command
22561      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22562      */
22563     relayCmd : function(cmd, value){
22564         this.win.focus();
22565         this.execCmd(cmd, value);
22566         this.owner.fireEvent('editorevent', this);
22567         //this.updateToolbar();
22568         this.owner.deferFocus();
22569     },
22570
22571     /**
22572      * Executes a Midas editor command directly on the editor document.
22573      * For visual commands, you should use {@link #relayCmd} instead.
22574      * <b>This should only be called after the editor is initialized.</b>
22575      * @param {String} cmd The Midas command
22576      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22577      */
22578     execCmd : function(cmd, value){
22579         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22580         this.syncValue();
22581     },
22582  
22583  
22584    
22585     /**
22586      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22587      * to insert tRoo.
22588      * @param {String} text | dom node.. 
22589      */
22590     insertAtCursor : function(text)
22591     {
22592         
22593         if(!this.activated){
22594             return;
22595         }
22596         /*
22597         if(Roo.isIE){
22598             this.win.focus();
22599             var r = this.doc.selection.createRange();
22600             if(r){
22601                 r.collapse(true);
22602                 r.pasteHTML(text);
22603                 this.syncValue();
22604                 this.deferFocus();
22605             
22606             }
22607             return;
22608         }
22609         */
22610         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22611             this.win.focus();
22612             
22613             
22614             // from jquery ui (MIT licenced)
22615             var range, node;
22616             var win = this.win;
22617             
22618             if (win.getSelection && win.getSelection().getRangeAt) {
22619                 range = win.getSelection().getRangeAt(0);
22620                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22621                 range.insertNode(node);
22622             } else if (win.document.selection && win.document.selection.createRange) {
22623                 // no firefox support
22624                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22625                 win.document.selection.createRange().pasteHTML(txt);
22626             } else {
22627                 // no firefox support
22628                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22629                 this.execCmd('InsertHTML', txt);
22630             } 
22631             
22632             this.syncValue();
22633             
22634             this.deferFocus();
22635         }
22636     },
22637  // private
22638     mozKeyPress : function(e){
22639         if(e.ctrlKey){
22640             var c = e.getCharCode(), cmd;
22641           
22642             if(c > 0){
22643                 c = String.fromCharCode(c).toLowerCase();
22644                 switch(c){
22645                     case 'b':
22646                         cmd = 'bold';
22647                         break;
22648                     case 'i':
22649                         cmd = 'italic';
22650                         break;
22651                     
22652                     case 'u':
22653                         cmd = 'underline';
22654                         break;
22655                     
22656                     case 'v':
22657                         this.cleanUpPaste.defer(100, this);
22658                         return;
22659                         
22660                 }
22661                 if(cmd){
22662                     this.win.focus();
22663                     this.execCmd(cmd);
22664                     this.deferFocus();
22665                     e.preventDefault();
22666                 }
22667                 
22668             }
22669         }
22670     },
22671
22672     // private
22673     fixKeys : function(){ // load time branching for fastest keydown performance
22674         if(Roo.isIE){
22675             return function(e){
22676                 var k = e.getKey(), r;
22677                 if(k == e.TAB){
22678                     e.stopEvent();
22679                     r = this.doc.selection.createRange();
22680                     if(r){
22681                         r.collapse(true);
22682                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22683                         this.deferFocus();
22684                     }
22685                     return;
22686                 }
22687                 
22688                 if(k == e.ENTER){
22689                     r = this.doc.selection.createRange();
22690                     if(r){
22691                         var target = r.parentElement();
22692                         if(!target || target.tagName.toLowerCase() != 'li'){
22693                             e.stopEvent();
22694                             r.pasteHTML('<br />');
22695                             r.collapse(false);
22696                             r.select();
22697                         }
22698                     }
22699                 }
22700                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22701                     this.cleanUpPaste.defer(100, this);
22702                     return;
22703                 }
22704                 
22705                 
22706             };
22707         }else if(Roo.isOpera){
22708             return function(e){
22709                 var k = e.getKey();
22710                 if(k == e.TAB){
22711                     e.stopEvent();
22712                     this.win.focus();
22713                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22714                     this.deferFocus();
22715                 }
22716                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22717                     this.cleanUpPaste.defer(100, this);
22718                     return;
22719                 }
22720                 
22721             };
22722         }else if(Roo.isSafari){
22723             return function(e){
22724                 var k = e.getKey();
22725                 
22726                 if(k == e.TAB){
22727                     e.stopEvent();
22728                     this.execCmd('InsertText','\t');
22729                     this.deferFocus();
22730                     return;
22731                 }
22732                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22733                     this.cleanUpPaste.defer(100, this);
22734                     return;
22735                 }
22736                 
22737              };
22738         }
22739     }(),
22740     
22741     getAllAncestors: function()
22742     {
22743         var p = this.getSelectedNode();
22744         var a = [];
22745         if (!p) {
22746             a.push(p); // push blank onto stack..
22747             p = this.getParentElement();
22748         }
22749         
22750         
22751         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22752             a.push(p);
22753             p = p.parentNode;
22754         }
22755         a.push(this.doc.body);
22756         return a;
22757     },
22758     lastSel : false,
22759     lastSelNode : false,
22760     
22761     
22762     getSelection : function() 
22763     {
22764         this.assignDocWin();
22765         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22766     },
22767     
22768     getSelectedNode: function() 
22769     {
22770         // this may only work on Gecko!!!
22771         
22772         // should we cache this!!!!
22773         
22774         
22775         
22776          
22777         var range = this.createRange(this.getSelection()).cloneRange();
22778         
22779         if (Roo.isIE) {
22780             var parent = range.parentElement();
22781             while (true) {
22782                 var testRange = range.duplicate();
22783                 testRange.moveToElementText(parent);
22784                 if (testRange.inRange(range)) {
22785                     break;
22786                 }
22787                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22788                     break;
22789                 }
22790                 parent = parent.parentElement;
22791             }
22792             return parent;
22793         }
22794         
22795         // is ancestor a text element.
22796         var ac =  range.commonAncestorContainer;
22797         if (ac.nodeType == 3) {
22798             ac = ac.parentNode;
22799         }
22800         
22801         var ar = ac.childNodes;
22802          
22803         var nodes = [];
22804         var other_nodes = [];
22805         var has_other_nodes = false;
22806         for (var i=0;i<ar.length;i++) {
22807             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22808                 continue;
22809             }
22810             // fullly contained node.
22811             
22812             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22813                 nodes.push(ar[i]);
22814                 continue;
22815             }
22816             
22817             // probably selected..
22818             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22819                 other_nodes.push(ar[i]);
22820                 continue;
22821             }
22822             // outer..
22823             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22824                 continue;
22825             }
22826             
22827             
22828             has_other_nodes = true;
22829         }
22830         if (!nodes.length && other_nodes.length) {
22831             nodes= other_nodes;
22832         }
22833         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22834             return false;
22835         }
22836         
22837         return nodes[0];
22838     },
22839     createRange: function(sel)
22840     {
22841         // this has strange effects when using with 
22842         // top toolbar - not sure if it's a great idea.
22843         //this.editor.contentWindow.focus();
22844         if (typeof sel != "undefined") {
22845             try {
22846                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22847             } catch(e) {
22848                 return this.doc.createRange();
22849             }
22850         } else {
22851             return this.doc.createRange();
22852         }
22853     },
22854     getParentElement: function()
22855     {
22856         
22857         this.assignDocWin();
22858         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22859         
22860         var range = this.createRange(sel);
22861          
22862         try {
22863             var p = range.commonAncestorContainer;
22864             while (p.nodeType == 3) { // text node
22865                 p = p.parentNode;
22866             }
22867             return p;
22868         } catch (e) {
22869             return null;
22870         }
22871     
22872     },
22873     /***
22874      *
22875      * Range intersection.. the hard stuff...
22876      *  '-1' = before
22877      *  '0' = hits..
22878      *  '1' = after.
22879      *         [ -- selected range --- ]
22880      *   [fail]                        [fail]
22881      *
22882      *    basically..
22883      *      if end is before start or  hits it. fail.
22884      *      if start is after end or hits it fail.
22885      *
22886      *   if either hits (but other is outside. - then it's not 
22887      *   
22888      *    
22889      **/
22890     
22891     
22892     // @see http://www.thismuchiknow.co.uk/?p=64.
22893     rangeIntersectsNode : function(range, node)
22894     {
22895         var nodeRange = node.ownerDocument.createRange();
22896         try {
22897             nodeRange.selectNode(node);
22898         } catch (e) {
22899             nodeRange.selectNodeContents(node);
22900         }
22901     
22902         var rangeStartRange = range.cloneRange();
22903         rangeStartRange.collapse(true);
22904     
22905         var rangeEndRange = range.cloneRange();
22906         rangeEndRange.collapse(false);
22907     
22908         var nodeStartRange = nodeRange.cloneRange();
22909         nodeStartRange.collapse(true);
22910     
22911         var nodeEndRange = nodeRange.cloneRange();
22912         nodeEndRange.collapse(false);
22913     
22914         return rangeStartRange.compareBoundaryPoints(
22915                  Range.START_TO_START, nodeEndRange) == -1 &&
22916                rangeEndRange.compareBoundaryPoints(
22917                  Range.START_TO_START, nodeStartRange) == 1;
22918         
22919          
22920     },
22921     rangeCompareNode : function(range, node)
22922     {
22923         var nodeRange = node.ownerDocument.createRange();
22924         try {
22925             nodeRange.selectNode(node);
22926         } catch (e) {
22927             nodeRange.selectNodeContents(node);
22928         }
22929         
22930         
22931         range.collapse(true);
22932     
22933         nodeRange.collapse(true);
22934      
22935         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22936         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22937          
22938         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22939         
22940         var nodeIsBefore   =  ss == 1;
22941         var nodeIsAfter    = ee == -1;
22942         
22943         if (nodeIsBefore && nodeIsAfter) {
22944             return 0; // outer
22945         }
22946         if (!nodeIsBefore && nodeIsAfter) {
22947             return 1; //right trailed.
22948         }
22949         
22950         if (nodeIsBefore && !nodeIsAfter) {
22951             return 2;  // left trailed.
22952         }
22953         // fully contined.
22954         return 3;
22955     },
22956
22957     // private? - in a new class?
22958     cleanUpPaste :  function()
22959     {
22960         // cleans up the whole document..
22961         Roo.log('cleanuppaste');
22962         
22963         this.cleanUpChildren(this.doc.body);
22964         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22965         if (clean != this.doc.body.innerHTML) {
22966             this.doc.body.innerHTML = clean;
22967         }
22968         
22969     },
22970     
22971     cleanWordChars : function(input) {// change the chars to hex code
22972         var he = Roo.HtmlEditorCore;
22973         
22974         var output = input;
22975         Roo.each(he.swapCodes, function(sw) { 
22976             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22977             
22978             output = output.replace(swapper, sw[1]);
22979         });
22980         
22981         return output;
22982     },
22983     
22984     
22985     cleanUpChildren : function (n)
22986     {
22987         if (!n.childNodes.length) {
22988             return;
22989         }
22990         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22991            this.cleanUpChild(n.childNodes[i]);
22992         }
22993     },
22994     
22995     
22996         
22997     
22998     cleanUpChild : function (node)
22999     {
23000         var ed = this;
23001         //console.log(node);
23002         if (node.nodeName == "#text") {
23003             // clean up silly Windows -- stuff?
23004             return; 
23005         }
23006         if (node.nodeName == "#comment") {
23007             node.parentNode.removeChild(node);
23008             // clean up silly Windows -- stuff?
23009             return; 
23010         }
23011         var lcname = node.tagName.toLowerCase();
23012         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23013         // whitelist of tags..
23014         
23015         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23016             // remove node.
23017             node.parentNode.removeChild(node);
23018             return;
23019             
23020         }
23021         
23022         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23023         
23024         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23025         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23026         
23027         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23028         //    remove_keep_children = true;
23029         //}
23030         
23031         if (remove_keep_children) {
23032             this.cleanUpChildren(node);
23033             // inserts everything just before this node...
23034             while (node.childNodes.length) {
23035                 var cn = node.childNodes[0];
23036                 node.removeChild(cn);
23037                 node.parentNode.insertBefore(cn, node);
23038             }
23039             node.parentNode.removeChild(node);
23040             return;
23041         }
23042         
23043         if (!node.attributes || !node.attributes.length) {
23044             this.cleanUpChildren(node);
23045             return;
23046         }
23047         
23048         function cleanAttr(n,v)
23049         {
23050             
23051             if (v.match(/^\./) || v.match(/^\//)) {
23052                 return;
23053             }
23054             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23055                 return;
23056             }
23057             if (v.match(/^#/)) {
23058                 return;
23059             }
23060 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23061             node.removeAttribute(n);
23062             
23063         }
23064         
23065         var cwhite = this.cwhite;
23066         var cblack = this.cblack;
23067             
23068         function cleanStyle(n,v)
23069         {
23070             if (v.match(/expression/)) { //XSS?? should we even bother..
23071                 node.removeAttribute(n);
23072                 return;
23073             }
23074             
23075             var parts = v.split(/;/);
23076             var clean = [];
23077             
23078             Roo.each(parts, function(p) {
23079                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23080                 if (!p.length) {
23081                     return true;
23082                 }
23083                 var l = p.split(':').shift().replace(/\s+/g,'');
23084                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23085                 
23086                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23087 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23088                     //node.removeAttribute(n);
23089                     return true;
23090                 }
23091                 //Roo.log()
23092                 // only allow 'c whitelisted system attributes'
23093                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23094 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23095                     //node.removeAttribute(n);
23096                     return true;
23097                 }
23098                 
23099                 
23100                  
23101                 
23102                 clean.push(p);
23103                 return true;
23104             });
23105             if (clean.length) { 
23106                 node.setAttribute(n, clean.join(';'));
23107             } else {
23108                 node.removeAttribute(n);
23109             }
23110             
23111         }
23112         
23113         
23114         for (var i = node.attributes.length-1; i > -1 ; i--) {
23115             var a = node.attributes[i];
23116             //console.log(a);
23117             
23118             if (a.name.toLowerCase().substr(0,2)=='on')  {
23119                 node.removeAttribute(a.name);
23120                 continue;
23121             }
23122             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23123                 node.removeAttribute(a.name);
23124                 continue;
23125             }
23126             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23127                 cleanAttr(a.name,a.value); // fixme..
23128                 continue;
23129             }
23130             if (a.name == 'style') {
23131                 cleanStyle(a.name,a.value);
23132                 continue;
23133             }
23134             /// clean up MS crap..
23135             // tecnically this should be a list of valid class'es..
23136             
23137             
23138             if (a.name == 'class') {
23139                 if (a.value.match(/^Mso/)) {
23140                     node.className = '';
23141                 }
23142                 
23143                 if (a.value.match(/^body$/)) {
23144                     node.className = '';
23145                 }
23146                 continue;
23147             }
23148             
23149             // style cleanup!?
23150             // class cleanup?
23151             
23152         }
23153         
23154         
23155         this.cleanUpChildren(node);
23156         
23157         
23158     },
23159     
23160     /**
23161      * Clean up MS wordisms...
23162      */
23163     cleanWord : function(node)
23164     {
23165         
23166         
23167         if (!node) {
23168             this.cleanWord(this.doc.body);
23169             return;
23170         }
23171         if (node.nodeName == "#text") {
23172             // clean up silly Windows -- stuff?
23173             return; 
23174         }
23175         if (node.nodeName == "#comment") {
23176             node.parentNode.removeChild(node);
23177             // clean up silly Windows -- stuff?
23178             return; 
23179         }
23180         
23181         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23182             node.parentNode.removeChild(node);
23183             return;
23184         }
23185         
23186         // remove - but keep children..
23187         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23188             while (node.childNodes.length) {
23189                 var cn = node.childNodes[0];
23190                 node.removeChild(cn);
23191                 node.parentNode.insertBefore(cn, node);
23192             }
23193             node.parentNode.removeChild(node);
23194             this.iterateChildren(node, this.cleanWord);
23195             return;
23196         }
23197         // clean styles
23198         if (node.className.length) {
23199             
23200             var cn = node.className.split(/\W+/);
23201             var cna = [];
23202             Roo.each(cn, function(cls) {
23203                 if (cls.match(/Mso[a-zA-Z]+/)) {
23204                     return;
23205                 }
23206                 cna.push(cls);
23207             });
23208             node.className = cna.length ? cna.join(' ') : '';
23209             if (!cna.length) {
23210                 node.removeAttribute("class");
23211             }
23212         }
23213         
23214         if (node.hasAttribute("lang")) {
23215             node.removeAttribute("lang");
23216         }
23217         
23218         if (node.hasAttribute("style")) {
23219             
23220             var styles = node.getAttribute("style").split(";");
23221             var nstyle = [];
23222             Roo.each(styles, function(s) {
23223                 if (!s.match(/:/)) {
23224                     return;
23225                 }
23226                 var kv = s.split(":");
23227                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23228                     return;
23229                 }
23230                 // what ever is left... we allow.
23231                 nstyle.push(s);
23232             });
23233             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23234             if (!nstyle.length) {
23235                 node.removeAttribute('style');
23236             }
23237         }
23238         this.iterateChildren(node, this.cleanWord);
23239         
23240         
23241         
23242     },
23243     /**
23244      * iterateChildren of a Node, calling fn each time, using this as the scole..
23245      * @param {DomNode} node node to iterate children of.
23246      * @param {Function} fn method of this class to call on each item.
23247      */
23248     iterateChildren : function(node, fn)
23249     {
23250         if (!node.childNodes.length) {
23251                 return;
23252         }
23253         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23254            fn.call(this, node.childNodes[i])
23255         }
23256     },
23257     
23258     
23259     /**
23260      * cleanTableWidths.
23261      *
23262      * Quite often pasting from word etc.. results in tables with column and widths.
23263      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23264      *
23265      */
23266     cleanTableWidths : function(node)
23267     {
23268          
23269          
23270         if (!node) {
23271             this.cleanTableWidths(this.doc.body);
23272             return;
23273         }
23274         
23275         // ignore list...
23276         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23277             return; 
23278         }
23279         Roo.log(node.tagName);
23280         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23281             this.iterateChildren(node, this.cleanTableWidths);
23282             return;
23283         }
23284         if (node.hasAttribute('width')) {
23285             node.removeAttribute('width');
23286         }
23287         
23288          
23289         if (node.hasAttribute("style")) {
23290             // pretty basic...
23291             
23292             var styles = node.getAttribute("style").split(";");
23293             var nstyle = [];
23294             Roo.each(styles, function(s) {
23295                 if (!s.match(/:/)) {
23296                     return;
23297                 }
23298                 var kv = s.split(":");
23299                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23300                     return;
23301                 }
23302                 // what ever is left... we allow.
23303                 nstyle.push(s);
23304             });
23305             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23306             if (!nstyle.length) {
23307                 node.removeAttribute('style');
23308             }
23309         }
23310         
23311         this.iterateChildren(node, this.cleanTableWidths);
23312         
23313         
23314     },
23315     
23316     
23317     
23318     
23319     domToHTML : function(currentElement, depth, nopadtext) {
23320         
23321         depth = depth || 0;
23322         nopadtext = nopadtext || false;
23323     
23324         if (!currentElement) {
23325             return this.domToHTML(this.doc.body);
23326         }
23327         
23328         //Roo.log(currentElement);
23329         var j;
23330         var allText = false;
23331         var nodeName = currentElement.nodeName;
23332         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23333         
23334         if  (nodeName == '#text') {
23335             
23336             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23337         }
23338         
23339         
23340         var ret = '';
23341         if (nodeName != 'BODY') {
23342              
23343             var i = 0;
23344             // Prints the node tagName, such as <A>, <IMG>, etc
23345             if (tagName) {
23346                 var attr = [];
23347                 for(i = 0; i < currentElement.attributes.length;i++) {
23348                     // quoting?
23349                     var aname = currentElement.attributes.item(i).name;
23350                     if (!currentElement.attributes.item(i).value.length) {
23351                         continue;
23352                     }
23353                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23354                 }
23355                 
23356                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23357             } 
23358             else {
23359                 
23360                 // eack
23361             }
23362         } else {
23363             tagName = false;
23364         }
23365         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23366             return ret;
23367         }
23368         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23369             nopadtext = true;
23370         }
23371         
23372         
23373         // Traverse the tree
23374         i = 0;
23375         var currentElementChild = currentElement.childNodes.item(i);
23376         var allText = true;
23377         var innerHTML  = '';
23378         lastnode = '';
23379         while (currentElementChild) {
23380             // Formatting code (indent the tree so it looks nice on the screen)
23381             var nopad = nopadtext;
23382             if (lastnode == 'SPAN') {
23383                 nopad  = true;
23384             }
23385             // text
23386             if  (currentElementChild.nodeName == '#text') {
23387                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23388                 toadd = nopadtext ? toadd : toadd.trim();
23389                 if (!nopad && toadd.length > 80) {
23390                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23391                 }
23392                 innerHTML  += toadd;
23393                 
23394                 i++;
23395                 currentElementChild = currentElement.childNodes.item(i);
23396                 lastNode = '';
23397                 continue;
23398             }
23399             allText = false;
23400             
23401             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23402                 
23403             // Recursively traverse the tree structure of the child node
23404             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23405             lastnode = currentElementChild.nodeName;
23406             i++;
23407             currentElementChild=currentElement.childNodes.item(i);
23408         }
23409         
23410         ret += innerHTML;
23411         
23412         if (!allText) {
23413                 // The remaining code is mostly for formatting the tree
23414             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23415         }
23416         
23417         
23418         if (tagName) {
23419             ret+= "</"+tagName+">";
23420         }
23421         return ret;
23422         
23423     },
23424         
23425     applyBlacklists : function()
23426     {
23427         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23428         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23429         
23430         this.white = [];
23431         this.black = [];
23432         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23433             if (b.indexOf(tag) > -1) {
23434                 return;
23435             }
23436             this.white.push(tag);
23437             
23438         }, this);
23439         
23440         Roo.each(w, function(tag) {
23441             if (b.indexOf(tag) > -1) {
23442                 return;
23443             }
23444             if (this.white.indexOf(tag) > -1) {
23445                 return;
23446             }
23447             this.white.push(tag);
23448             
23449         }, this);
23450         
23451         
23452         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23453             if (w.indexOf(tag) > -1) {
23454                 return;
23455             }
23456             this.black.push(tag);
23457             
23458         }, this);
23459         
23460         Roo.each(b, function(tag) {
23461             if (w.indexOf(tag) > -1) {
23462                 return;
23463             }
23464             if (this.black.indexOf(tag) > -1) {
23465                 return;
23466             }
23467             this.black.push(tag);
23468             
23469         }, this);
23470         
23471         
23472         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23473         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23474         
23475         this.cwhite = [];
23476         this.cblack = [];
23477         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23478             if (b.indexOf(tag) > -1) {
23479                 return;
23480             }
23481             this.cwhite.push(tag);
23482             
23483         }, this);
23484         
23485         Roo.each(w, function(tag) {
23486             if (b.indexOf(tag) > -1) {
23487                 return;
23488             }
23489             if (this.cwhite.indexOf(tag) > -1) {
23490                 return;
23491             }
23492             this.cwhite.push(tag);
23493             
23494         }, this);
23495         
23496         
23497         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23498             if (w.indexOf(tag) > -1) {
23499                 return;
23500             }
23501             this.cblack.push(tag);
23502             
23503         }, this);
23504         
23505         Roo.each(b, function(tag) {
23506             if (w.indexOf(tag) > -1) {
23507                 return;
23508             }
23509             if (this.cblack.indexOf(tag) > -1) {
23510                 return;
23511             }
23512             this.cblack.push(tag);
23513             
23514         }, this);
23515     },
23516     
23517     setStylesheets : function(stylesheets)
23518     {
23519         if(typeof(stylesheets) == 'string'){
23520             Roo.get(this.iframe.contentDocument.head).createChild({
23521                 tag : 'link',
23522                 rel : 'stylesheet',
23523                 type : 'text/css',
23524                 href : stylesheets
23525             });
23526             
23527             return;
23528         }
23529         var _this = this;
23530      
23531         Roo.each(stylesheets, function(s) {
23532             if(!s.length){
23533                 return;
23534             }
23535             
23536             Roo.get(_this.iframe.contentDocument.head).createChild({
23537                 tag : 'link',
23538                 rel : 'stylesheet',
23539                 type : 'text/css',
23540                 href : s
23541             });
23542         });
23543
23544         
23545     },
23546     
23547     removeStylesheets : function()
23548     {
23549         var _this = this;
23550         
23551         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23552             s.remove();
23553         });
23554     },
23555     
23556     setStyle : function(style)
23557     {
23558         Roo.get(this.iframe.contentDocument.head).createChild({
23559             tag : 'style',
23560             type : 'text/css',
23561             html : style
23562         });
23563
23564         return;
23565     }
23566     
23567     // hide stuff that is not compatible
23568     /**
23569      * @event blur
23570      * @hide
23571      */
23572     /**
23573      * @event change
23574      * @hide
23575      */
23576     /**
23577      * @event focus
23578      * @hide
23579      */
23580     /**
23581      * @event specialkey
23582      * @hide
23583      */
23584     /**
23585      * @cfg {String} fieldClass @hide
23586      */
23587     /**
23588      * @cfg {String} focusClass @hide
23589      */
23590     /**
23591      * @cfg {String} autoCreate @hide
23592      */
23593     /**
23594      * @cfg {String} inputType @hide
23595      */
23596     /**
23597      * @cfg {String} invalidClass @hide
23598      */
23599     /**
23600      * @cfg {String} invalidText @hide
23601      */
23602     /**
23603      * @cfg {String} msgFx @hide
23604      */
23605     /**
23606      * @cfg {String} validateOnBlur @hide
23607      */
23608 });
23609
23610 Roo.HtmlEditorCore.white = [
23611         'area', 'br', 'img', 'input', 'hr', 'wbr',
23612         
23613        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23614        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23615        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23616        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23617        'table',   'ul',         'xmp', 
23618        
23619        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23620       'thead',   'tr', 
23621      
23622       'dir', 'menu', 'ol', 'ul', 'dl',
23623        
23624       'embed',  'object'
23625 ];
23626
23627
23628 Roo.HtmlEditorCore.black = [
23629     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23630         'applet', // 
23631         'base',   'basefont', 'bgsound', 'blink',  'body', 
23632         'frame',  'frameset', 'head',    'html',   'ilayer', 
23633         'iframe', 'layer',  'link',     'meta',    'object',   
23634         'script', 'style' ,'title',  'xml' // clean later..
23635 ];
23636 Roo.HtmlEditorCore.clean = [
23637     'script', 'style', 'title', 'xml'
23638 ];
23639 Roo.HtmlEditorCore.remove = [
23640     'font'
23641 ];
23642 // attributes..
23643
23644 Roo.HtmlEditorCore.ablack = [
23645     'on'
23646 ];
23647     
23648 Roo.HtmlEditorCore.aclean = [ 
23649     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23650 ];
23651
23652 // protocols..
23653 Roo.HtmlEditorCore.pwhite= [
23654         'http',  'https',  'mailto'
23655 ];
23656
23657 // white listed style attributes.
23658 Roo.HtmlEditorCore.cwhite= [
23659       //  'text-align', /// default is to allow most things..
23660       
23661          
23662 //        'font-size'//??
23663 ];
23664
23665 // black listed style attributes.
23666 Roo.HtmlEditorCore.cblack= [
23667       //  'font-size' -- this can be set by the project 
23668 ];
23669
23670
23671 Roo.HtmlEditorCore.swapCodes   =[ 
23672     [    8211, "--" ], 
23673     [    8212, "--" ], 
23674     [    8216,  "'" ],  
23675     [    8217, "'" ],  
23676     [    8220, '"' ],  
23677     [    8221, '"' ],  
23678     [    8226, "*" ],  
23679     [    8230, "..." ]
23680 ]; 
23681
23682     /*
23683  * - LGPL
23684  *
23685  * HtmlEditor
23686  * 
23687  */
23688
23689 /**
23690  * @class Roo.bootstrap.HtmlEditor
23691  * @extends Roo.bootstrap.TextArea
23692  * Bootstrap HtmlEditor class
23693
23694  * @constructor
23695  * Create a new HtmlEditor
23696  * @param {Object} config The config object
23697  */
23698
23699 Roo.bootstrap.HtmlEditor = function(config){
23700     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23701     if (!this.toolbars) {
23702         this.toolbars = [];
23703     }
23704     
23705     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23706     this.addEvents({
23707             /**
23708              * @event initialize
23709              * Fires when the editor is fully initialized (including the iframe)
23710              * @param {HtmlEditor} this
23711              */
23712             initialize: true,
23713             /**
23714              * @event activate
23715              * Fires when the editor is first receives the focus. Any insertion must wait
23716              * until after this event.
23717              * @param {HtmlEditor} this
23718              */
23719             activate: true,
23720              /**
23721              * @event beforesync
23722              * Fires before the textarea is updated with content from the editor iframe. Return false
23723              * to cancel the sync.
23724              * @param {HtmlEditor} this
23725              * @param {String} html
23726              */
23727             beforesync: true,
23728              /**
23729              * @event beforepush
23730              * Fires before the iframe editor is updated with content from the textarea. Return false
23731              * to cancel the push.
23732              * @param {HtmlEditor} this
23733              * @param {String} html
23734              */
23735             beforepush: true,
23736              /**
23737              * @event sync
23738              * Fires when the textarea is updated with content from the editor iframe.
23739              * @param {HtmlEditor} this
23740              * @param {String} html
23741              */
23742             sync: true,
23743              /**
23744              * @event push
23745              * Fires when the iframe editor is updated with content from the textarea.
23746              * @param {HtmlEditor} this
23747              * @param {String} html
23748              */
23749             push: true,
23750              /**
23751              * @event editmodechange
23752              * Fires when the editor switches edit modes
23753              * @param {HtmlEditor} this
23754              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23755              */
23756             editmodechange: true,
23757             /**
23758              * @event editorevent
23759              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23760              * @param {HtmlEditor} this
23761              */
23762             editorevent: true,
23763             /**
23764              * @event firstfocus
23765              * Fires when on first focus - needed by toolbars..
23766              * @param {HtmlEditor} this
23767              */
23768             firstfocus: true,
23769             /**
23770              * @event autosave
23771              * Auto save the htmlEditor value as a file into Events
23772              * @param {HtmlEditor} this
23773              */
23774             autosave: true,
23775             /**
23776              * @event savedpreview
23777              * preview the saved version of htmlEditor
23778              * @param {HtmlEditor} this
23779              */
23780             savedpreview: true
23781         });
23782 };
23783
23784
23785 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23786     
23787     
23788       /**
23789      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23790      */
23791     toolbars : false,
23792     
23793      /**
23794     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23795     */
23796     btns : [],
23797    
23798      /**
23799      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23800      *                        Roo.resizable.
23801      */
23802     resizable : false,
23803      /**
23804      * @cfg {Number} height (in pixels)
23805      */   
23806     height: 300,
23807    /**
23808      * @cfg {Number} width (in pixels)
23809      */   
23810     width: false,
23811     
23812     /**
23813      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23814      * 
23815      */
23816     stylesheets: false,
23817     
23818     // id of frame..
23819     frameId: false,
23820     
23821     // private properties
23822     validationEvent : false,
23823     deferHeight: true,
23824     initialized : false,
23825     activated : false,
23826     
23827     onFocus : Roo.emptyFn,
23828     iframePad:3,
23829     hideMode:'offsets',
23830     
23831     tbContainer : false,
23832     
23833     bodyCls : '',
23834     
23835     toolbarContainer :function() {
23836         return this.wrap.select('.x-html-editor-tb',true).first();
23837     },
23838
23839     /**
23840      * Protected method that will not generally be called directly. It
23841      * is called when the editor creates its toolbar. Override this method if you need to
23842      * add custom toolbar buttons.
23843      * @param {HtmlEditor} editor
23844      */
23845     createToolbar : function(){
23846         Roo.log('renewing');
23847         Roo.log("create toolbars");
23848         
23849         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23850         this.toolbars[0].render(this.toolbarContainer());
23851         
23852         return;
23853         
23854 //        if (!editor.toolbars || !editor.toolbars.length) {
23855 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23856 //        }
23857 //        
23858 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23859 //            editor.toolbars[i] = Roo.factory(
23860 //                    typeof(editor.toolbars[i]) == 'string' ?
23861 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23862 //                Roo.bootstrap.HtmlEditor);
23863 //            editor.toolbars[i].init(editor);
23864 //        }
23865     },
23866
23867      
23868     // private
23869     onRender : function(ct, position)
23870     {
23871        // Roo.log("Call onRender: " + this.xtype);
23872         var _t = this;
23873         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23874       
23875         this.wrap = this.inputEl().wrap({
23876             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23877         });
23878         
23879         this.editorcore.onRender(ct, position);
23880          
23881         if (this.resizable) {
23882             this.resizeEl = new Roo.Resizable(this.wrap, {
23883                 pinned : true,
23884                 wrap: true,
23885                 dynamic : true,
23886                 minHeight : this.height,
23887                 height: this.height,
23888                 handles : this.resizable,
23889                 width: this.width,
23890                 listeners : {
23891                     resize : function(r, w, h) {
23892                         _t.onResize(w,h); // -something
23893                     }
23894                 }
23895             });
23896             
23897         }
23898         this.createToolbar(this);
23899        
23900         
23901         if(!this.width && this.resizable){
23902             this.setSize(this.wrap.getSize());
23903         }
23904         if (this.resizeEl) {
23905             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23906             // should trigger onReize..
23907         }
23908         
23909     },
23910
23911     // private
23912     onResize : function(w, h)
23913     {
23914         Roo.log('resize: ' +w + ',' + h );
23915         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23916         var ew = false;
23917         var eh = false;
23918         
23919         if(this.inputEl() ){
23920             if(typeof w == 'number'){
23921                 var aw = w - this.wrap.getFrameWidth('lr');
23922                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23923                 ew = aw;
23924             }
23925             if(typeof h == 'number'){
23926                  var tbh = -11;  // fixme it needs to tool bar size!
23927                 for (var i =0; i < this.toolbars.length;i++) {
23928                     // fixme - ask toolbars for heights?
23929                     tbh += this.toolbars[i].el.getHeight();
23930                     //if (this.toolbars[i].footer) {
23931                     //    tbh += this.toolbars[i].footer.el.getHeight();
23932                     //}
23933                 }
23934               
23935                 
23936                 
23937                 
23938                 
23939                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23940                 ah -= 5; // knock a few pixes off for look..
23941                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23942                 var eh = ah;
23943             }
23944         }
23945         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23946         this.editorcore.onResize(ew,eh);
23947         
23948     },
23949
23950     /**
23951      * Toggles the editor between standard and source edit mode.
23952      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23953      */
23954     toggleSourceEdit : function(sourceEditMode)
23955     {
23956         this.editorcore.toggleSourceEdit(sourceEditMode);
23957         
23958         if(this.editorcore.sourceEditMode){
23959             Roo.log('editor - showing textarea');
23960             
23961 //            Roo.log('in');
23962 //            Roo.log(this.syncValue());
23963             this.syncValue();
23964             this.inputEl().removeClass(['hide', 'x-hidden']);
23965             this.inputEl().dom.removeAttribute('tabIndex');
23966             this.inputEl().focus();
23967         }else{
23968             Roo.log('editor - hiding textarea');
23969 //            Roo.log('out')
23970 //            Roo.log(this.pushValue()); 
23971             this.pushValue();
23972             
23973             this.inputEl().addClass(['hide', 'x-hidden']);
23974             this.inputEl().dom.setAttribute('tabIndex', -1);
23975             //this.deferFocus();
23976         }
23977          
23978         if(this.resizable){
23979             this.setSize(this.wrap.getSize());
23980         }
23981         
23982         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23983     },
23984  
23985     // private (for BoxComponent)
23986     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23987
23988     // private (for BoxComponent)
23989     getResizeEl : function(){
23990         return this.wrap;
23991     },
23992
23993     // private (for BoxComponent)
23994     getPositionEl : function(){
23995         return this.wrap;
23996     },
23997
23998     // private
23999     initEvents : function(){
24000         this.originalValue = this.getValue();
24001     },
24002
24003 //    /**
24004 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24005 //     * @method
24006 //     */
24007 //    markInvalid : Roo.emptyFn,
24008 //    /**
24009 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24010 //     * @method
24011 //     */
24012 //    clearInvalid : Roo.emptyFn,
24013
24014     setValue : function(v){
24015         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24016         this.editorcore.pushValue();
24017     },
24018
24019      
24020     // private
24021     deferFocus : function(){
24022         this.focus.defer(10, this);
24023     },
24024
24025     // doc'ed in Field
24026     focus : function(){
24027         this.editorcore.focus();
24028         
24029     },
24030       
24031
24032     // private
24033     onDestroy : function(){
24034         
24035         
24036         
24037         if(this.rendered){
24038             
24039             for (var i =0; i < this.toolbars.length;i++) {
24040                 // fixme - ask toolbars for heights?
24041                 this.toolbars[i].onDestroy();
24042             }
24043             
24044             this.wrap.dom.innerHTML = '';
24045             this.wrap.remove();
24046         }
24047     },
24048
24049     // private
24050     onFirstFocus : function(){
24051         //Roo.log("onFirstFocus");
24052         this.editorcore.onFirstFocus();
24053          for (var i =0; i < this.toolbars.length;i++) {
24054             this.toolbars[i].onFirstFocus();
24055         }
24056         
24057     },
24058     
24059     // private
24060     syncValue : function()
24061     {   
24062         this.editorcore.syncValue();
24063     },
24064     
24065     pushValue : function()
24066     {   
24067         this.editorcore.pushValue();
24068     }
24069      
24070     
24071     // hide stuff that is not compatible
24072     /**
24073      * @event blur
24074      * @hide
24075      */
24076     /**
24077      * @event change
24078      * @hide
24079      */
24080     /**
24081      * @event focus
24082      * @hide
24083      */
24084     /**
24085      * @event specialkey
24086      * @hide
24087      */
24088     /**
24089      * @cfg {String} fieldClass @hide
24090      */
24091     /**
24092      * @cfg {String} focusClass @hide
24093      */
24094     /**
24095      * @cfg {String} autoCreate @hide
24096      */
24097     /**
24098      * @cfg {String} inputType @hide
24099      */
24100      
24101     /**
24102      * @cfg {String} invalidText @hide
24103      */
24104     /**
24105      * @cfg {String} msgFx @hide
24106      */
24107     /**
24108      * @cfg {String} validateOnBlur @hide
24109      */
24110 });
24111  
24112     
24113    
24114    
24115    
24116       
24117 Roo.namespace('Roo.bootstrap.htmleditor');
24118 /**
24119  * @class Roo.bootstrap.HtmlEditorToolbar1
24120  * Basic Toolbar
24121  * 
24122  * @example
24123  * Usage:
24124  *
24125  new Roo.bootstrap.HtmlEditor({
24126     ....
24127     toolbars : [
24128         new Roo.bootstrap.HtmlEditorToolbar1({
24129             disable : { fonts: 1 , format: 1, ..., ... , ...],
24130             btns : [ .... ]
24131         })
24132     }
24133      
24134  * 
24135  * @cfg {Object} disable List of elements to disable..
24136  * @cfg {Array} btns List of additional buttons.
24137  * 
24138  * 
24139  * NEEDS Extra CSS? 
24140  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24141  */
24142  
24143 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24144 {
24145     
24146     Roo.apply(this, config);
24147     
24148     // default disabled, based on 'good practice'..
24149     this.disable = this.disable || {};
24150     Roo.applyIf(this.disable, {
24151         fontSize : true,
24152         colors : true,
24153         specialElements : true
24154     });
24155     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24156     
24157     this.editor = config.editor;
24158     this.editorcore = config.editor.editorcore;
24159     
24160     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24161     
24162     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24163     // dont call parent... till later.
24164 }
24165 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24166      
24167     bar : true,
24168     
24169     editor : false,
24170     editorcore : false,
24171     
24172     
24173     formats : [
24174         "p" ,  
24175         "h1","h2","h3","h4","h5","h6", 
24176         "pre", "code", 
24177         "abbr", "acronym", "address", "cite", "samp", "var",
24178         'div','span'
24179     ],
24180     
24181     onRender : function(ct, position)
24182     {
24183        // Roo.log("Call onRender: " + this.xtype);
24184         
24185        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24186        Roo.log(this.el);
24187        this.el.dom.style.marginBottom = '0';
24188        var _this = this;
24189        var editorcore = this.editorcore;
24190        var editor= this.editor;
24191        
24192        var children = [];
24193        var btn = function(id,cmd , toggle, handler, html){
24194        
24195             var  event = toggle ? 'toggle' : 'click';
24196        
24197             var a = {
24198                 size : 'sm',
24199                 xtype: 'Button',
24200                 xns: Roo.bootstrap,
24201                 //glyphicon : id,
24202                 fa: id,
24203                 cmd : id || cmd,
24204                 enableToggle:toggle !== false,
24205                 html : html || '',
24206                 pressed : toggle ? false : null,
24207                 listeners : {}
24208             };
24209             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24210                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24211             };
24212             children.push(a);
24213             return a;
24214        }
24215        
24216     //    var cb_box = function...
24217         
24218         var style = {
24219                 xtype: 'Button',
24220                 size : 'sm',
24221                 xns: Roo.bootstrap,
24222                 fa : 'font',
24223                 //html : 'submit'
24224                 menu : {
24225                     xtype: 'Menu',
24226                     xns: Roo.bootstrap,
24227                     items:  []
24228                 }
24229         };
24230         Roo.each(this.formats, function(f) {
24231             style.menu.items.push({
24232                 xtype :'MenuItem',
24233                 xns: Roo.bootstrap,
24234                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24235                 tagname : f,
24236                 listeners : {
24237                     click : function()
24238                     {
24239                         editorcore.insertTag(this.tagname);
24240                         editor.focus();
24241                     }
24242                 }
24243                 
24244             });
24245         });
24246         children.push(style);   
24247         
24248         btn('bold',false,true);
24249         btn('italic',false,true);
24250         btn('align-left', 'justifyleft',true);
24251         btn('align-center', 'justifycenter',true);
24252         btn('align-right' , 'justifyright',true);
24253         btn('link', false, false, function(btn) {
24254             //Roo.log("create link?");
24255             var url = prompt(this.createLinkText, this.defaultLinkValue);
24256             if(url && url != 'http:/'+'/'){
24257                 this.editorcore.relayCmd('createlink', url);
24258             }
24259         }),
24260         btn('list','insertunorderedlist',true);
24261         btn('pencil', false,true, function(btn){
24262                 Roo.log(this);
24263                 this.toggleSourceEdit(btn.pressed);
24264         });
24265         
24266         if (this.editor.btns.length > 0) {
24267             for (var i = 0; i<this.editor.btns.length; i++) {
24268                 children.push(this.editor.btns[i]);
24269             }
24270         }
24271         
24272         /*
24273         var cog = {
24274                 xtype: 'Button',
24275                 size : 'sm',
24276                 xns: Roo.bootstrap,
24277                 glyphicon : 'cog',
24278                 //html : 'submit'
24279                 menu : {
24280                     xtype: 'Menu',
24281                     xns: Roo.bootstrap,
24282                     items:  []
24283                 }
24284         };
24285         
24286         cog.menu.items.push({
24287             xtype :'MenuItem',
24288             xns: Roo.bootstrap,
24289             html : Clean styles,
24290             tagname : f,
24291             listeners : {
24292                 click : function()
24293                 {
24294                     editorcore.insertTag(this.tagname);
24295                     editor.focus();
24296                 }
24297             }
24298             
24299         });
24300        */
24301         
24302          
24303        this.xtype = 'NavSimplebar';
24304         
24305         for(var i=0;i< children.length;i++) {
24306             
24307             this.buttons.add(this.addxtypeChild(children[i]));
24308             
24309         }
24310         
24311         editor.on('editorevent', this.updateToolbar, this);
24312     },
24313     onBtnClick : function(id)
24314     {
24315        this.editorcore.relayCmd(id);
24316        this.editorcore.focus();
24317     },
24318     
24319     /**
24320      * Protected method that will not generally be called directly. It triggers
24321      * a toolbar update by reading the markup state of the current selection in the editor.
24322      */
24323     updateToolbar: function(){
24324
24325         if(!this.editorcore.activated){
24326             this.editor.onFirstFocus(); // is this neeed?
24327             return;
24328         }
24329
24330         var btns = this.buttons; 
24331         var doc = this.editorcore.doc;
24332         btns.get('bold').setActive(doc.queryCommandState('bold'));
24333         btns.get('italic').setActive(doc.queryCommandState('italic'));
24334         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24335         
24336         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24337         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24338         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24339         
24340         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24341         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24342          /*
24343         
24344         var ans = this.editorcore.getAllAncestors();
24345         if (this.formatCombo) {
24346             
24347             
24348             var store = this.formatCombo.store;
24349             this.formatCombo.setValue("");
24350             for (var i =0; i < ans.length;i++) {
24351                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24352                     // select it..
24353                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24354                     break;
24355                 }
24356             }
24357         }
24358         
24359         
24360         
24361         // hides menus... - so this cant be on a menu...
24362         Roo.bootstrap.MenuMgr.hideAll();
24363         */
24364         Roo.bootstrap.MenuMgr.hideAll();
24365         //this.editorsyncValue();
24366     },
24367     onFirstFocus: function() {
24368         this.buttons.each(function(item){
24369            item.enable();
24370         });
24371     },
24372     toggleSourceEdit : function(sourceEditMode){
24373         
24374           
24375         if(sourceEditMode){
24376             Roo.log("disabling buttons");
24377            this.buttons.each( function(item){
24378                 if(item.cmd != 'pencil'){
24379                     item.disable();
24380                 }
24381             });
24382           
24383         }else{
24384             Roo.log("enabling buttons");
24385             if(this.editorcore.initialized){
24386                 this.buttons.each( function(item){
24387                     item.enable();
24388                 });
24389             }
24390             
24391         }
24392         Roo.log("calling toggole on editor");
24393         // tell the editor that it's been pressed..
24394         this.editor.toggleSourceEdit(sourceEditMode);
24395        
24396     }
24397 });
24398
24399
24400
24401
24402
24403 /**
24404  * @class Roo.bootstrap.Table.AbstractSelectionModel
24405  * @extends Roo.util.Observable
24406  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24407  * implemented by descendant classes.  This class should not be directly instantiated.
24408  * @constructor
24409  */
24410 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24411     this.locked = false;
24412     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24413 };
24414
24415
24416 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24417     /** @ignore Called by the grid automatically. Do not call directly. */
24418     init : function(grid){
24419         this.grid = grid;
24420         this.initEvents();
24421     },
24422
24423     /**
24424      * Locks the selections.
24425      */
24426     lock : function(){
24427         this.locked = true;
24428     },
24429
24430     /**
24431      * Unlocks the selections.
24432      */
24433     unlock : function(){
24434         this.locked = false;
24435     },
24436
24437     /**
24438      * Returns true if the selections are locked.
24439      * @return {Boolean}
24440      */
24441     isLocked : function(){
24442         return this.locked;
24443     }
24444 });
24445 /**
24446  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24447  * @class Roo.bootstrap.Table.RowSelectionModel
24448  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24449  * It supports multiple selections and keyboard selection/navigation. 
24450  * @constructor
24451  * @param {Object} config
24452  */
24453
24454 Roo.bootstrap.Table.RowSelectionModel = function(config){
24455     Roo.apply(this, config);
24456     this.selections = new Roo.util.MixedCollection(false, function(o){
24457         return o.id;
24458     });
24459
24460     this.last = false;
24461     this.lastActive = false;
24462
24463     this.addEvents({
24464         /**
24465              * @event selectionchange
24466              * Fires when the selection changes
24467              * @param {SelectionModel} this
24468              */
24469             "selectionchange" : true,
24470         /**
24471              * @event afterselectionchange
24472              * Fires after the selection changes (eg. by key press or clicking)
24473              * @param {SelectionModel} this
24474              */
24475             "afterselectionchange" : true,
24476         /**
24477              * @event beforerowselect
24478              * Fires when a row is selected being selected, return false to cancel.
24479              * @param {SelectionModel} this
24480              * @param {Number} rowIndex The selected index
24481              * @param {Boolean} keepExisting False if other selections will be cleared
24482              */
24483             "beforerowselect" : true,
24484         /**
24485              * @event rowselect
24486              * Fires when a row is selected.
24487              * @param {SelectionModel} this
24488              * @param {Number} rowIndex The selected index
24489              * @param {Roo.data.Record} r The record
24490              */
24491             "rowselect" : true,
24492         /**
24493              * @event rowdeselect
24494              * Fires when a row is deselected.
24495              * @param {SelectionModel} this
24496              * @param {Number} rowIndex The selected index
24497              */
24498         "rowdeselect" : true
24499     });
24500     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24501     this.locked = false;
24502  };
24503
24504 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24505     /**
24506      * @cfg {Boolean} singleSelect
24507      * True to allow selection of only one row at a time (defaults to false)
24508      */
24509     singleSelect : false,
24510
24511     // private
24512     initEvents : function()
24513     {
24514
24515         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24516         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24517         //}else{ // allow click to work like normal
24518          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24519         //}
24520         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24521         this.grid.on("rowclick", this.handleMouseDown, this);
24522         
24523         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24524             "up" : function(e){
24525                 if(!e.shiftKey){
24526                     this.selectPrevious(e.shiftKey);
24527                 }else if(this.last !== false && this.lastActive !== false){
24528                     var last = this.last;
24529                     this.selectRange(this.last,  this.lastActive-1);
24530                     this.grid.getView().focusRow(this.lastActive);
24531                     if(last !== false){
24532                         this.last = last;
24533                     }
24534                 }else{
24535                     this.selectFirstRow();
24536                 }
24537                 this.fireEvent("afterselectionchange", this);
24538             },
24539             "down" : function(e){
24540                 if(!e.shiftKey){
24541                     this.selectNext(e.shiftKey);
24542                 }else if(this.last !== false && this.lastActive !== false){
24543                     var last = this.last;
24544                     this.selectRange(this.last,  this.lastActive+1);
24545                     this.grid.getView().focusRow(this.lastActive);
24546                     if(last !== false){
24547                         this.last = last;
24548                     }
24549                 }else{
24550                     this.selectFirstRow();
24551                 }
24552                 this.fireEvent("afterselectionchange", this);
24553             },
24554             scope: this
24555         });
24556         this.grid.store.on('load', function(){
24557             this.selections.clear();
24558         },this);
24559         /*
24560         var view = this.grid.view;
24561         view.on("refresh", this.onRefresh, this);
24562         view.on("rowupdated", this.onRowUpdated, this);
24563         view.on("rowremoved", this.onRemove, this);
24564         */
24565     },
24566
24567     // private
24568     onRefresh : function()
24569     {
24570         var ds = this.grid.store, i, v = this.grid.view;
24571         var s = this.selections;
24572         s.each(function(r){
24573             if((i = ds.indexOfId(r.id)) != -1){
24574                 v.onRowSelect(i);
24575             }else{
24576                 s.remove(r);
24577             }
24578         });
24579     },
24580
24581     // private
24582     onRemove : function(v, index, r){
24583         this.selections.remove(r);
24584     },
24585
24586     // private
24587     onRowUpdated : function(v, index, r){
24588         if(this.isSelected(r)){
24589             v.onRowSelect(index);
24590         }
24591     },
24592
24593     /**
24594      * Select records.
24595      * @param {Array} records The records to select
24596      * @param {Boolean} keepExisting (optional) True to keep existing selections
24597      */
24598     selectRecords : function(records, keepExisting)
24599     {
24600         if(!keepExisting){
24601             this.clearSelections();
24602         }
24603             var ds = this.grid.store;
24604         for(var i = 0, len = records.length; i < len; i++){
24605             this.selectRow(ds.indexOf(records[i]), true);
24606         }
24607     },
24608
24609     /**
24610      * Gets the number of selected rows.
24611      * @return {Number}
24612      */
24613     getCount : function(){
24614         return this.selections.length;
24615     },
24616
24617     /**
24618      * Selects the first row in the grid.
24619      */
24620     selectFirstRow : function(){
24621         this.selectRow(0);
24622     },
24623
24624     /**
24625      * Select the last row.
24626      * @param {Boolean} keepExisting (optional) True to keep existing selections
24627      */
24628     selectLastRow : function(keepExisting){
24629         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24630         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24631     },
24632
24633     /**
24634      * Selects the row immediately following the last selected row.
24635      * @param {Boolean} keepExisting (optional) True to keep existing selections
24636      */
24637     selectNext : function(keepExisting)
24638     {
24639             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24640             this.selectRow(this.last+1, keepExisting);
24641             this.grid.getView().focusRow(this.last);
24642         }
24643     },
24644
24645     /**
24646      * Selects the row that precedes the last selected row.
24647      * @param {Boolean} keepExisting (optional) True to keep existing selections
24648      */
24649     selectPrevious : function(keepExisting){
24650         if(this.last){
24651             this.selectRow(this.last-1, keepExisting);
24652             this.grid.getView().focusRow(this.last);
24653         }
24654     },
24655
24656     /**
24657      * Returns the selected records
24658      * @return {Array} Array of selected records
24659      */
24660     getSelections : function(){
24661         return [].concat(this.selections.items);
24662     },
24663
24664     /**
24665      * Returns the first selected record.
24666      * @return {Record}
24667      */
24668     getSelected : function(){
24669         return this.selections.itemAt(0);
24670     },
24671
24672
24673     /**
24674      * Clears all selections.
24675      */
24676     clearSelections : function(fast)
24677     {
24678         if(this.locked) {
24679             return;
24680         }
24681         if(fast !== true){
24682                 var ds = this.grid.store;
24683             var s = this.selections;
24684             s.each(function(r){
24685                 this.deselectRow(ds.indexOfId(r.id));
24686             }, this);
24687             s.clear();
24688         }else{
24689             this.selections.clear();
24690         }
24691         this.last = false;
24692     },
24693
24694
24695     /**
24696      * Selects all rows.
24697      */
24698     selectAll : function(){
24699         if(this.locked) {
24700             return;
24701         }
24702         this.selections.clear();
24703         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24704             this.selectRow(i, true);
24705         }
24706     },
24707
24708     /**
24709      * Returns True if there is a selection.
24710      * @return {Boolean}
24711      */
24712     hasSelection : function(){
24713         return this.selections.length > 0;
24714     },
24715
24716     /**
24717      * Returns True if the specified row is selected.
24718      * @param {Number/Record} record The record or index of the record to check
24719      * @return {Boolean}
24720      */
24721     isSelected : function(index){
24722             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24723         return (r && this.selections.key(r.id) ? true : false);
24724     },
24725
24726     /**
24727      * Returns True if the specified record id is selected.
24728      * @param {String} id The id of record to check
24729      * @return {Boolean}
24730      */
24731     isIdSelected : function(id){
24732         return (this.selections.key(id) ? true : false);
24733     },
24734
24735
24736     // private
24737     handleMouseDBClick : function(e, t){
24738         
24739     },
24740     // private
24741     handleMouseDown : function(e, t)
24742     {
24743             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24744         if(this.isLocked() || rowIndex < 0 ){
24745             return;
24746         };
24747         if(e.shiftKey && this.last !== false){
24748             var last = this.last;
24749             this.selectRange(last, rowIndex, e.ctrlKey);
24750             this.last = last; // reset the last
24751             t.focus();
24752     
24753         }else{
24754             var isSelected = this.isSelected(rowIndex);
24755             //Roo.log("select row:" + rowIndex);
24756             if(isSelected){
24757                 this.deselectRow(rowIndex);
24758             } else {
24759                         this.selectRow(rowIndex, true);
24760             }
24761     
24762             /*
24763                 if(e.button !== 0 && isSelected){
24764                 alert('rowIndex 2: ' + rowIndex);
24765                     view.focusRow(rowIndex);
24766                 }else if(e.ctrlKey && isSelected){
24767                     this.deselectRow(rowIndex);
24768                 }else if(!isSelected){
24769                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24770                     view.focusRow(rowIndex);
24771                 }
24772             */
24773         }
24774         this.fireEvent("afterselectionchange", this);
24775     },
24776     // private
24777     handleDragableRowClick :  function(grid, rowIndex, e) 
24778     {
24779         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24780             this.selectRow(rowIndex, false);
24781             grid.view.focusRow(rowIndex);
24782              this.fireEvent("afterselectionchange", this);
24783         }
24784     },
24785     
24786     /**
24787      * Selects multiple rows.
24788      * @param {Array} rows Array of the indexes of the row to select
24789      * @param {Boolean} keepExisting (optional) True to keep existing selections
24790      */
24791     selectRows : function(rows, keepExisting){
24792         if(!keepExisting){
24793             this.clearSelections();
24794         }
24795         for(var i = 0, len = rows.length; i < len; i++){
24796             this.selectRow(rows[i], true);
24797         }
24798     },
24799
24800     /**
24801      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24802      * @param {Number} startRow The index of the first row in the range
24803      * @param {Number} endRow The index of the last row in the range
24804      * @param {Boolean} keepExisting (optional) True to retain existing selections
24805      */
24806     selectRange : function(startRow, endRow, keepExisting){
24807         if(this.locked) {
24808             return;
24809         }
24810         if(!keepExisting){
24811             this.clearSelections();
24812         }
24813         if(startRow <= endRow){
24814             for(var i = startRow; i <= endRow; i++){
24815                 this.selectRow(i, true);
24816             }
24817         }else{
24818             for(var i = startRow; i >= endRow; i--){
24819                 this.selectRow(i, true);
24820             }
24821         }
24822     },
24823
24824     /**
24825      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24826      * @param {Number} startRow The index of the first row in the range
24827      * @param {Number} endRow The index of the last row in the range
24828      */
24829     deselectRange : function(startRow, endRow, preventViewNotify){
24830         if(this.locked) {
24831             return;
24832         }
24833         for(var i = startRow; i <= endRow; i++){
24834             this.deselectRow(i, preventViewNotify);
24835         }
24836     },
24837
24838     /**
24839      * Selects a row.
24840      * @param {Number} row The index of the row to select
24841      * @param {Boolean} keepExisting (optional) True to keep existing selections
24842      */
24843     selectRow : function(index, keepExisting, preventViewNotify)
24844     {
24845             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24846             return;
24847         }
24848         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24849             if(!keepExisting || this.singleSelect){
24850                 this.clearSelections();
24851             }
24852             
24853             var r = this.grid.store.getAt(index);
24854             //console.log('selectRow - record id :' + r.id);
24855             
24856             this.selections.add(r);
24857             this.last = this.lastActive = index;
24858             if(!preventViewNotify){
24859                 var proxy = new Roo.Element(
24860                                 this.grid.getRowDom(index)
24861                 );
24862                 proxy.addClass('bg-info info');
24863             }
24864             this.fireEvent("rowselect", this, index, r);
24865             this.fireEvent("selectionchange", this);
24866         }
24867     },
24868
24869     /**
24870      * Deselects a row.
24871      * @param {Number} row The index of the row to deselect
24872      */
24873     deselectRow : function(index, preventViewNotify)
24874     {
24875         if(this.locked) {
24876             return;
24877         }
24878         if(this.last == index){
24879             this.last = false;
24880         }
24881         if(this.lastActive == index){
24882             this.lastActive = false;
24883         }
24884         
24885         var r = this.grid.store.getAt(index);
24886         if (!r) {
24887             return;
24888         }
24889         
24890         this.selections.remove(r);
24891         //.console.log('deselectRow - record id :' + r.id);
24892         if(!preventViewNotify){
24893         
24894             var proxy = new Roo.Element(
24895                 this.grid.getRowDom(index)
24896             );
24897             proxy.removeClass('bg-info info');
24898         }
24899         this.fireEvent("rowdeselect", this, index);
24900         this.fireEvent("selectionchange", this);
24901     },
24902
24903     // private
24904     restoreLast : function(){
24905         if(this._last){
24906             this.last = this._last;
24907         }
24908     },
24909
24910     // private
24911     acceptsNav : function(row, col, cm){
24912         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24913     },
24914
24915     // private
24916     onEditorKey : function(field, e){
24917         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24918         if(k == e.TAB){
24919             e.stopEvent();
24920             ed.completeEdit();
24921             if(e.shiftKey){
24922                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24923             }else{
24924                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24925             }
24926         }else if(k == e.ENTER && !e.ctrlKey){
24927             e.stopEvent();
24928             ed.completeEdit();
24929             if(e.shiftKey){
24930                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24931             }else{
24932                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24933             }
24934         }else if(k == e.ESC){
24935             ed.cancelEdit();
24936         }
24937         if(newCell){
24938             g.startEditing(newCell[0], newCell[1]);
24939         }
24940     }
24941 });
24942 /*
24943  * Based on:
24944  * Ext JS Library 1.1.1
24945  * Copyright(c) 2006-2007, Ext JS, LLC.
24946  *
24947  * Originally Released Under LGPL - original licence link has changed is not relivant.
24948  *
24949  * Fork - LGPL
24950  * <script type="text/javascript">
24951  */
24952  
24953 /**
24954  * @class Roo.bootstrap.PagingToolbar
24955  * @extends Roo.bootstrap.NavSimplebar
24956  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24957  * @constructor
24958  * Create a new PagingToolbar
24959  * @param {Object} config The config object
24960  * @param {Roo.data.Store} store
24961  */
24962 Roo.bootstrap.PagingToolbar = function(config)
24963 {
24964     // old args format still supported... - xtype is prefered..
24965         // created from xtype...
24966     
24967     this.ds = config.dataSource;
24968     
24969     if (config.store && !this.ds) {
24970         this.store= Roo.factory(config.store, Roo.data);
24971         this.ds = this.store;
24972         this.ds.xmodule = this.xmodule || false;
24973     }
24974     
24975     this.toolbarItems = [];
24976     if (config.items) {
24977         this.toolbarItems = config.items;
24978     }
24979     
24980     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24981     
24982     this.cursor = 0;
24983     
24984     if (this.ds) { 
24985         this.bind(this.ds);
24986     }
24987     
24988     if (Roo.bootstrap.version == 4) {
24989         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24990     } else {
24991         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24992     }
24993     
24994 };
24995
24996 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24997     /**
24998      * @cfg {Roo.data.Store} dataSource
24999      * The underlying data store providing the paged data
25000      */
25001     /**
25002      * @cfg {String/HTMLElement/Element} container
25003      * container The id or element that will contain the toolbar
25004      */
25005     /**
25006      * @cfg {Boolean} displayInfo
25007      * True to display the displayMsg (defaults to false)
25008      */
25009     /**
25010      * @cfg {Number} pageSize
25011      * The number of records to display per page (defaults to 20)
25012      */
25013     pageSize: 20,
25014     /**
25015      * @cfg {String} displayMsg
25016      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25017      */
25018     displayMsg : 'Displaying {0} - {1} of {2}',
25019     /**
25020      * @cfg {String} emptyMsg
25021      * The message to display when no records are found (defaults to "No data to display")
25022      */
25023     emptyMsg : 'No data to display',
25024     /**
25025      * Customizable piece of the default paging text (defaults to "Page")
25026      * @type String
25027      */
25028     beforePageText : "Page",
25029     /**
25030      * Customizable piece of the default paging text (defaults to "of %0")
25031      * @type String
25032      */
25033     afterPageText : "of {0}",
25034     /**
25035      * Customizable piece of the default paging text (defaults to "First Page")
25036      * @type String
25037      */
25038     firstText : "First Page",
25039     /**
25040      * Customizable piece of the default paging text (defaults to "Previous Page")
25041      * @type String
25042      */
25043     prevText : "Previous Page",
25044     /**
25045      * Customizable piece of the default paging text (defaults to "Next Page")
25046      * @type String
25047      */
25048     nextText : "Next Page",
25049     /**
25050      * Customizable piece of the default paging text (defaults to "Last Page")
25051      * @type String
25052      */
25053     lastText : "Last Page",
25054     /**
25055      * Customizable piece of the default paging text (defaults to "Refresh")
25056      * @type String
25057      */
25058     refreshText : "Refresh",
25059
25060     buttons : false,
25061     // private
25062     onRender : function(ct, position) 
25063     {
25064         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25065         this.navgroup.parentId = this.id;
25066         this.navgroup.onRender(this.el, null);
25067         // add the buttons to the navgroup
25068         
25069         if(this.displayInfo){
25070             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25071             this.displayEl = this.el.select('.x-paging-info', true).first();
25072 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25073 //            this.displayEl = navel.el.select('span',true).first();
25074         }
25075         
25076         var _this = this;
25077         
25078         if(this.buttons){
25079             Roo.each(_this.buttons, function(e){ // this might need to use render????
25080                Roo.factory(e).render(_this.el);
25081             });
25082         }
25083             
25084         Roo.each(_this.toolbarItems, function(e) {
25085             _this.navgroup.addItem(e);
25086         });
25087         
25088         
25089         this.first = this.navgroup.addItem({
25090             tooltip: this.firstText,
25091             cls: "prev btn-outline-secondary",
25092             html : ' <i class="fa fa-step-backward"></i>',
25093             disabled: true,
25094             preventDefault: true,
25095             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25096         });
25097         
25098         this.prev =  this.navgroup.addItem({
25099             tooltip: this.prevText,
25100             cls: "prev btn-outline-secondary",
25101             html : ' <i class="fa fa-backward"></i>',
25102             disabled: true,
25103             preventDefault: true,
25104             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25105         });
25106     //this.addSeparator();
25107         
25108         
25109         var field = this.navgroup.addItem( {
25110             tagtype : 'span',
25111             cls : 'x-paging-position  btn-outline-secondary',
25112              disabled: true,
25113             html : this.beforePageText  +
25114                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25115                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25116          } ); //?? escaped?
25117         
25118         this.field = field.el.select('input', true).first();
25119         this.field.on("keydown", this.onPagingKeydown, this);
25120         this.field.on("focus", function(){this.dom.select();});
25121     
25122     
25123         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25124         //this.field.setHeight(18);
25125         //this.addSeparator();
25126         this.next = this.navgroup.addItem({
25127             tooltip: this.nextText,
25128             cls: "next btn-outline-secondary",
25129             html : ' <i class="fa fa-forward"></i>',
25130             disabled: true,
25131             preventDefault: true,
25132             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25133         });
25134         this.last = this.navgroup.addItem({
25135             tooltip: this.lastText,
25136             html : ' <i class="fa fa-step-forward"></i>',
25137             cls: "next btn-outline-secondary",
25138             disabled: true,
25139             preventDefault: true,
25140             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25141         });
25142     //this.addSeparator();
25143         this.loading = this.navgroup.addItem({
25144             tooltip: this.refreshText,
25145             cls: "btn-outline-secondary",
25146             html : ' <i class="fa fa-refresh"></i>',
25147             preventDefault: true,
25148             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25149         });
25150         
25151     },
25152
25153     // private
25154     updateInfo : function(){
25155         if(this.displayEl){
25156             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25157             var msg = count == 0 ?
25158                 this.emptyMsg :
25159                 String.format(
25160                     this.displayMsg,
25161                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25162                 );
25163             this.displayEl.update(msg);
25164         }
25165     },
25166
25167     // private
25168     onLoad : function(ds, r, o)
25169     {
25170         this.cursor = o.params.start ? o.params.start : 0;
25171         
25172         var d = this.getPageData(),
25173             ap = d.activePage,
25174             ps = d.pages;
25175         
25176         
25177         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25178         this.field.dom.value = ap;
25179         this.first.setDisabled(ap == 1);
25180         this.prev.setDisabled(ap == 1);
25181         this.next.setDisabled(ap == ps);
25182         this.last.setDisabled(ap == ps);
25183         this.loading.enable();
25184         this.updateInfo();
25185     },
25186
25187     // private
25188     getPageData : function(){
25189         var total = this.ds.getTotalCount();
25190         return {
25191             total : total,
25192             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25193             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25194         };
25195     },
25196
25197     // private
25198     onLoadError : function(){
25199         this.loading.enable();
25200     },
25201
25202     // private
25203     onPagingKeydown : function(e){
25204         var k = e.getKey();
25205         var d = this.getPageData();
25206         if(k == e.RETURN){
25207             var v = this.field.dom.value, pageNum;
25208             if(!v || isNaN(pageNum = parseInt(v, 10))){
25209                 this.field.dom.value = d.activePage;
25210                 return;
25211             }
25212             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25213             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25214             e.stopEvent();
25215         }
25216         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))
25217         {
25218           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25219           this.field.dom.value = pageNum;
25220           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25221           e.stopEvent();
25222         }
25223         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25224         {
25225           var v = this.field.dom.value, pageNum; 
25226           var increment = (e.shiftKey) ? 10 : 1;
25227           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25228                 increment *= -1;
25229           }
25230           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25231             this.field.dom.value = d.activePage;
25232             return;
25233           }
25234           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25235           {
25236             this.field.dom.value = parseInt(v, 10) + increment;
25237             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25238             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25239           }
25240           e.stopEvent();
25241         }
25242     },
25243
25244     // private
25245     beforeLoad : function(){
25246         if(this.loading){
25247             this.loading.disable();
25248         }
25249     },
25250
25251     // private
25252     onClick : function(which){
25253         
25254         var ds = this.ds;
25255         if (!ds) {
25256             return;
25257         }
25258         
25259         switch(which){
25260             case "first":
25261                 ds.load({params:{start: 0, limit: this.pageSize}});
25262             break;
25263             case "prev":
25264                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25265             break;
25266             case "next":
25267                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25268             break;
25269             case "last":
25270                 var total = ds.getTotalCount();
25271                 var extra = total % this.pageSize;
25272                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25273                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25274             break;
25275             case "refresh":
25276                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25277             break;
25278         }
25279     },
25280
25281     /**
25282      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25283      * @param {Roo.data.Store} store The data store to unbind
25284      */
25285     unbind : function(ds){
25286         ds.un("beforeload", this.beforeLoad, this);
25287         ds.un("load", this.onLoad, this);
25288         ds.un("loadexception", this.onLoadError, this);
25289         ds.un("remove", this.updateInfo, this);
25290         ds.un("add", this.updateInfo, this);
25291         this.ds = undefined;
25292     },
25293
25294     /**
25295      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25296      * @param {Roo.data.Store} store The data store to bind
25297      */
25298     bind : function(ds){
25299         ds.on("beforeload", this.beforeLoad, this);
25300         ds.on("load", this.onLoad, this);
25301         ds.on("loadexception", this.onLoadError, this);
25302         ds.on("remove", this.updateInfo, this);
25303         ds.on("add", this.updateInfo, this);
25304         this.ds = ds;
25305     }
25306 });/*
25307  * - LGPL
25308  *
25309  * element
25310  * 
25311  */
25312
25313 /**
25314  * @class Roo.bootstrap.MessageBar
25315  * @extends Roo.bootstrap.Component
25316  * Bootstrap MessageBar class
25317  * @cfg {String} html contents of the MessageBar
25318  * @cfg {String} weight (info | success | warning | danger) default info
25319  * @cfg {String} beforeClass insert the bar before the given class
25320  * @cfg {Boolean} closable (true | false) default false
25321  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25322  * 
25323  * @constructor
25324  * Create a new Element
25325  * @param {Object} config The config object
25326  */
25327
25328 Roo.bootstrap.MessageBar = function(config){
25329     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25330 };
25331
25332 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25333     
25334     html: '',
25335     weight: 'info',
25336     closable: false,
25337     fixed: false,
25338     beforeClass: 'bootstrap-sticky-wrap',
25339     
25340     getAutoCreate : function(){
25341         
25342         var cfg = {
25343             tag: 'div',
25344             cls: 'alert alert-dismissable alert-' + this.weight,
25345             cn: [
25346                 {
25347                     tag: 'span',
25348                     cls: 'message',
25349                     html: this.html || ''
25350                 }
25351             ]
25352         };
25353         
25354         if(this.fixed){
25355             cfg.cls += ' alert-messages-fixed';
25356         }
25357         
25358         if(this.closable){
25359             cfg.cn.push({
25360                 tag: 'button',
25361                 cls: 'close',
25362                 html: 'x'
25363             });
25364         }
25365         
25366         return cfg;
25367     },
25368     
25369     onRender : function(ct, position)
25370     {
25371         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25372         
25373         if(!this.el){
25374             var cfg = Roo.apply({},  this.getAutoCreate());
25375             cfg.id = Roo.id();
25376             
25377             if (this.cls) {
25378                 cfg.cls += ' ' + this.cls;
25379             }
25380             if (this.style) {
25381                 cfg.style = this.style;
25382             }
25383             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25384             
25385             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25386         }
25387         
25388         this.el.select('>button.close').on('click', this.hide, this);
25389         
25390     },
25391     
25392     show : function()
25393     {
25394         if (!this.rendered) {
25395             this.render();
25396         }
25397         
25398         this.el.show();
25399         
25400         this.fireEvent('show', this);
25401         
25402     },
25403     
25404     hide : function()
25405     {
25406         if (!this.rendered) {
25407             this.render();
25408         }
25409         
25410         this.el.hide();
25411         
25412         this.fireEvent('hide', this);
25413     },
25414     
25415     update : function()
25416     {
25417 //        var e = this.el.dom.firstChild;
25418 //        
25419 //        if(this.closable){
25420 //            e = e.nextSibling;
25421 //        }
25422 //        
25423 //        e.data = this.html || '';
25424
25425         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25426     }
25427    
25428 });
25429
25430  
25431
25432      /*
25433  * - LGPL
25434  *
25435  * Graph
25436  * 
25437  */
25438
25439
25440 /**
25441  * @class Roo.bootstrap.Graph
25442  * @extends Roo.bootstrap.Component
25443  * Bootstrap Graph class
25444 > Prameters
25445  -sm {number} sm 4
25446  -md {number} md 5
25447  @cfg {String} graphtype  bar | vbar | pie
25448  @cfg {number} g_x coodinator | centre x (pie)
25449  @cfg {number} g_y coodinator | centre y (pie)
25450  @cfg {number} g_r radius (pie)
25451  @cfg {number} g_height height of the chart (respected by all elements in the set)
25452  @cfg {number} g_width width of the chart (respected by all elements in the set)
25453  @cfg {Object} title The title of the chart
25454     
25455  -{Array}  values
25456  -opts (object) options for the chart 
25457      o {
25458      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25459      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25460      o vgutter (number)
25461      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.
25462      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25463      o to
25464      o stretch (boolean)
25465      o }
25466  -opts (object) options for the pie
25467      o{
25468      o cut
25469      o startAngle (number)
25470      o endAngle (number)
25471      } 
25472  *
25473  * @constructor
25474  * Create a new Input
25475  * @param {Object} config The config object
25476  */
25477
25478 Roo.bootstrap.Graph = function(config){
25479     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25480     
25481     this.addEvents({
25482         // img events
25483         /**
25484          * @event click
25485          * The img click event for the img.
25486          * @param {Roo.EventObject} e
25487          */
25488         "click" : true
25489     });
25490 };
25491
25492 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25493     
25494     sm: 4,
25495     md: 5,
25496     graphtype: 'bar',
25497     g_height: 250,
25498     g_width: 400,
25499     g_x: 50,
25500     g_y: 50,
25501     g_r: 30,
25502     opts:{
25503         //g_colors: this.colors,
25504         g_type: 'soft',
25505         g_gutter: '20%'
25506
25507     },
25508     title : false,
25509
25510     getAutoCreate : function(){
25511         
25512         var cfg = {
25513             tag: 'div',
25514             html : null
25515         };
25516         
25517         
25518         return  cfg;
25519     },
25520
25521     onRender : function(ct,position){
25522         
25523         
25524         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25525         
25526         if (typeof(Raphael) == 'undefined') {
25527             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25528             return;
25529         }
25530         
25531         this.raphael = Raphael(this.el.dom);
25532         
25533                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25534                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25535                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25536                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25537                 /*
25538                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25539                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25540                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25541                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25542                 
25543                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25544                 r.barchart(330, 10, 300, 220, data1);
25545                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25546                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25547                 */
25548                 
25549                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25550                 // r.barchart(30, 30, 560, 250,  xdata, {
25551                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25552                 //     axis : "0 0 1 1",
25553                 //     axisxlabels :  xdata
25554                 //     //yvalues : cols,
25555                    
25556                 // });
25557 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25558 //        
25559 //        this.load(null,xdata,{
25560 //                axis : "0 0 1 1",
25561 //                axisxlabels :  xdata
25562 //                });
25563
25564     },
25565
25566     load : function(graphtype,xdata,opts)
25567     {
25568         this.raphael.clear();
25569         if(!graphtype) {
25570             graphtype = this.graphtype;
25571         }
25572         if(!opts){
25573             opts = this.opts;
25574         }
25575         var r = this.raphael,
25576             fin = function () {
25577                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25578             },
25579             fout = function () {
25580                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25581             },
25582             pfin = function() {
25583                 this.sector.stop();
25584                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25585
25586                 if (this.label) {
25587                     this.label[0].stop();
25588                     this.label[0].attr({ r: 7.5 });
25589                     this.label[1].attr({ "font-weight": 800 });
25590                 }
25591             },
25592             pfout = function() {
25593                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25594
25595                 if (this.label) {
25596                     this.label[0].animate({ r: 5 }, 500, "bounce");
25597                     this.label[1].attr({ "font-weight": 400 });
25598                 }
25599             };
25600
25601         switch(graphtype){
25602             case 'bar':
25603                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25604                 break;
25605             case 'hbar':
25606                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25607                 break;
25608             case 'pie':
25609 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25610 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25611 //            
25612                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25613                 
25614                 break;
25615
25616         }
25617         
25618         if(this.title){
25619             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25620         }
25621         
25622     },
25623     
25624     setTitle: function(o)
25625     {
25626         this.title = o;
25627     },
25628     
25629     initEvents: function() {
25630         
25631         if(!this.href){
25632             this.el.on('click', this.onClick, this);
25633         }
25634     },
25635     
25636     onClick : function(e)
25637     {
25638         Roo.log('img onclick');
25639         this.fireEvent('click', this, e);
25640     }
25641    
25642 });
25643
25644  
25645 /*
25646  * - LGPL
25647  *
25648  * numberBox
25649  * 
25650  */
25651 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25652
25653 /**
25654  * @class Roo.bootstrap.dash.NumberBox
25655  * @extends Roo.bootstrap.Component
25656  * Bootstrap NumberBox class
25657  * @cfg {String} headline Box headline
25658  * @cfg {String} content Box content
25659  * @cfg {String} icon Box icon
25660  * @cfg {String} footer Footer text
25661  * @cfg {String} fhref Footer href
25662  * 
25663  * @constructor
25664  * Create a new NumberBox
25665  * @param {Object} config The config object
25666  */
25667
25668
25669 Roo.bootstrap.dash.NumberBox = function(config){
25670     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25671     
25672 };
25673
25674 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25675     
25676     headline : '',
25677     content : '',
25678     icon : '',
25679     footer : '',
25680     fhref : '',
25681     ficon : '',
25682     
25683     getAutoCreate : function(){
25684         
25685         var cfg = {
25686             tag : 'div',
25687             cls : 'small-box ',
25688             cn : [
25689                 {
25690                     tag : 'div',
25691                     cls : 'inner',
25692                     cn :[
25693                         {
25694                             tag : 'h3',
25695                             cls : 'roo-headline',
25696                             html : this.headline
25697                         },
25698                         {
25699                             tag : 'p',
25700                             cls : 'roo-content',
25701                             html : this.content
25702                         }
25703                     ]
25704                 }
25705             ]
25706         };
25707         
25708         if(this.icon){
25709             cfg.cn.push({
25710                 tag : 'div',
25711                 cls : 'icon',
25712                 cn :[
25713                     {
25714                         tag : 'i',
25715                         cls : 'ion ' + this.icon
25716                     }
25717                 ]
25718             });
25719         }
25720         
25721         if(this.footer){
25722             var footer = {
25723                 tag : 'a',
25724                 cls : 'small-box-footer',
25725                 href : this.fhref || '#',
25726                 html : this.footer
25727             };
25728             
25729             cfg.cn.push(footer);
25730             
25731         }
25732         
25733         return  cfg;
25734     },
25735
25736     onRender : function(ct,position){
25737         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25738
25739
25740        
25741                 
25742     },
25743
25744     setHeadline: function (value)
25745     {
25746         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25747     },
25748     
25749     setFooter: function (value, href)
25750     {
25751         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25752         
25753         if(href){
25754             this.el.select('a.small-box-footer',true).first().attr('href', href);
25755         }
25756         
25757     },
25758
25759     setContent: function (value)
25760     {
25761         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25762     },
25763
25764     initEvents: function() 
25765     {   
25766         
25767     }
25768     
25769 });
25770
25771  
25772 /*
25773  * - LGPL
25774  *
25775  * TabBox
25776  * 
25777  */
25778 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25779
25780 /**
25781  * @class Roo.bootstrap.dash.TabBox
25782  * @extends Roo.bootstrap.Component
25783  * Bootstrap TabBox class
25784  * @cfg {String} title Title of the TabBox
25785  * @cfg {String} icon Icon of the TabBox
25786  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25787  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25788  * 
25789  * @constructor
25790  * Create a new TabBox
25791  * @param {Object} config The config object
25792  */
25793
25794
25795 Roo.bootstrap.dash.TabBox = function(config){
25796     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25797     this.addEvents({
25798         // raw events
25799         /**
25800          * @event addpane
25801          * When a pane is added
25802          * @param {Roo.bootstrap.dash.TabPane} pane
25803          */
25804         "addpane" : true,
25805         /**
25806          * @event activatepane
25807          * When a pane is activated
25808          * @param {Roo.bootstrap.dash.TabPane} pane
25809          */
25810         "activatepane" : true
25811         
25812          
25813     });
25814     
25815     this.panes = [];
25816 };
25817
25818 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25819
25820     title : '',
25821     icon : false,
25822     showtabs : true,
25823     tabScrollable : false,
25824     
25825     getChildContainer : function()
25826     {
25827         return this.el.select('.tab-content', true).first();
25828     },
25829     
25830     getAutoCreate : function(){
25831         
25832         var header = {
25833             tag: 'li',
25834             cls: 'pull-left header',
25835             html: this.title,
25836             cn : []
25837         };
25838         
25839         if(this.icon){
25840             header.cn.push({
25841                 tag: 'i',
25842                 cls: 'fa ' + this.icon
25843             });
25844         }
25845         
25846         var h = {
25847             tag: 'ul',
25848             cls: 'nav nav-tabs pull-right',
25849             cn: [
25850                 header
25851             ]
25852         };
25853         
25854         if(this.tabScrollable){
25855             h = {
25856                 tag: 'div',
25857                 cls: 'tab-header',
25858                 cn: [
25859                     {
25860                         tag: 'ul',
25861                         cls: 'nav nav-tabs pull-right',
25862                         cn: [
25863                             header
25864                         ]
25865                     }
25866                 ]
25867             };
25868         }
25869         
25870         var cfg = {
25871             tag: 'div',
25872             cls: 'nav-tabs-custom',
25873             cn: [
25874                 h,
25875                 {
25876                     tag: 'div',
25877                     cls: 'tab-content no-padding',
25878                     cn: []
25879                 }
25880             ]
25881         };
25882
25883         return  cfg;
25884     },
25885     initEvents : function()
25886     {
25887         //Roo.log('add add pane handler');
25888         this.on('addpane', this.onAddPane, this);
25889     },
25890      /**
25891      * Updates the box title
25892      * @param {String} html to set the title to.
25893      */
25894     setTitle : function(value)
25895     {
25896         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25897     },
25898     onAddPane : function(pane)
25899     {
25900         this.panes.push(pane);
25901         //Roo.log('addpane');
25902         //Roo.log(pane);
25903         // tabs are rendere left to right..
25904         if(!this.showtabs){
25905             return;
25906         }
25907         
25908         var ctr = this.el.select('.nav-tabs', true).first();
25909          
25910          
25911         var existing = ctr.select('.nav-tab',true);
25912         var qty = existing.getCount();;
25913         
25914         
25915         var tab = ctr.createChild({
25916             tag : 'li',
25917             cls : 'nav-tab' + (qty ? '' : ' active'),
25918             cn : [
25919                 {
25920                     tag : 'a',
25921                     href:'#',
25922                     html : pane.title
25923                 }
25924             ]
25925         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25926         pane.tab = tab;
25927         
25928         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25929         if (!qty) {
25930             pane.el.addClass('active');
25931         }
25932         
25933                 
25934     },
25935     onTabClick : function(ev,un,ob,pane)
25936     {
25937         //Roo.log('tab - prev default');
25938         ev.preventDefault();
25939         
25940         
25941         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25942         pane.tab.addClass('active');
25943         //Roo.log(pane.title);
25944         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25945         // technically we should have a deactivate event.. but maybe add later.
25946         // and it should not de-activate the selected tab...
25947         this.fireEvent('activatepane', pane);
25948         pane.el.addClass('active');
25949         pane.fireEvent('activate');
25950         
25951         
25952     },
25953     
25954     getActivePane : function()
25955     {
25956         var r = false;
25957         Roo.each(this.panes, function(p) {
25958             if(p.el.hasClass('active')){
25959                 r = p;
25960                 return false;
25961             }
25962             
25963             return;
25964         });
25965         
25966         return r;
25967     }
25968     
25969     
25970 });
25971
25972  
25973 /*
25974  * - LGPL
25975  *
25976  * Tab pane
25977  * 
25978  */
25979 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25980 /**
25981  * @class Roo.bootstrap.TabPane
25982  * @extends Roo.bootstrap.Component
25983  * Bootstrap TabPane class
25984  * @cfg {Boolean} active (false | true) Default false
25985  * @cfg {String} title title of panel
25986
25987  * 
25988  * @constructor
25989  * Create a new TabPane
25990  * @param {Object} config The config object
25991  */
25992
25993 Roo.bootstrap.dash.TabPane = function(config){
25994     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25995     
25996     this.addEvents({
25997         // raw events
25998         /**
25999          * @event activate
26000          * When a pane is activated
26001          * @param {Roo.bootstrap.dash.TabPane} pane
26002          */
26003         "activate" : true
26004          
26005     });
26006 };
26007
26008 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26009     
26010     active : false,
26011     title : '',
26012     
26013     // the tabBox that this is attached to.
26014     tab : false,
26015      
26016     getAutoCreate : function() 
26017     {
26018         var cfg = {
26019             tag: 'div',
26020             cls: 'tab-pane'
26021         };
26022         
26023         if(this.active){
26024             cfg.cls += ' active';
26025         }
26026         
26027         return cfg;
26028     },
26029     initEvents  : function()
26030     {
26031         //Roo.log('trigger add pane handler');
26032         this.parent().fireEvent('addpane', this)
26033     },
26034     
26035      /**
26036      * Updates the tab title 
26037      * @param {String} html to set the title to.
26038      */
26039     setTitle: function(str)
26040     {
26041         if (!this.tab) {
26042             return;
26043         }
26044         this.title = str;
26045         this.tab.select('a', true).first().dom.innerHTML = str;
26046         
26047     }
26048     
26049     
26050     
26051 });
26052
26053  
26054
26055
26056  /*
26057  * - LGPL
26058  *
26059  * menu
26060  * 
26061  */
26062 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26063
26064 /**
26065  * @class Roo.bootstrap.menu.Menu
26066  * @extends Roo.bootstrap.Component
26067  * Bootstrap Menu class - container for Menu
26068  * @cfg {String} html Text of the menu
26069  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26070  * @cfg {String} icon Font awesome icon
26071  * @cfg {String} pos Menu align to (top | bottom) default bottom
26072  * 
26073  * 
26074  * @constructor
26075  * Create a new Menu
26076  * @param {Object} config The config object
26077  */
26078
26079
26080 Roo.bootstrap.menu.Menu = function(config){
26081     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26082     
26083     this.addEvents({
26084         /**
26085          * @event beforeshow
26086          * Fires before this menu is displayed
26087          * @param {Roo.bootstrap.menu.Menu} this
26088          */
26089         beforeshow : true,
26090         /**
26091          * @event beforehide
26092          * Fires before this menu is hidden
26093          * @param {Roo.bootstrap.menu.Menu} this
26094          */
26095         beforehide : true,
26096         /**
26097          * @event show
26098          * Fires after this menu is displayed
26099          * @param {Roo.bootstrap.menu.Menu} this
26100          */
26101         show : true,
26102         /**
26103          * @event hide
26104          * Fires after this menu is hidden
26105          * @param {Roo.bootstrap.menu.Menu} this
26106          */
26107         hide : true,
26108         /**
26109          * @event click
26110          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26111          * @param {Roo.bootstrap.menu.Menu} this
26112          * @param {Roo.EventObject} e
26113          */
26114         click : true
26115     });
26116     
26117 };
26118
26119 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26120     
26121     submenu : false,
26122     html : '',
26123     weight : 'default',
26124     icon : false,
26125     pos : 'bottom',
26126     
26127     
26128     getChildContainer : function() {
26129         if(this.isSubMenu){
26130             return this.el;
26131         }
26132         
26133         return this.el.select('ul.dropdown-menu', true).first();  
26134     },
26135     
26136     getAutoCreate : function()
26137     {
26138         var text = [
26139             {
26140                 tag : 'span',
26141                 cls : 'roo-menu-text',
26142                 html : this.html
26143             }
26144         ];
26145         
26146         if(this.icon){
26147             text.unshift({
26148                 tag : 'i',
26149                 cls : 'fa ' + this.icon
26150             })
26151         }
26152         
26153         
26154         var cfg = {
26155             tag : 'div',
26156             cls : 'btn-group',
26157             cn : [
26158                 {
26159                     tag : 'button',
26160                     cls : 'dropdown-button btn btn-' + this.weight,
26161                     cn : text
26162                 },
26163                 {
26164                     tag : 'button',
26165                     cls : 'dropdown-toggle btn btn-' + this.weight,
26166                     cn : [
26167                         {
26168                             tag : 'span',
26169                             cls : 'caret'
26170                         }
26171                     ]
26172                 },
26173                 {
26174                     tag : 'ul',
26175                     cls : 'dropdown-menu'
26176                 }
26177             ]
26178             
26179         };
26180         
26181         if(this.pos == 'top'){
26182             cfg.cls += ' dropup';
26183         }
26184         
26185         if(this.isSubMenu){
26186             cfg = {
26187                 tag : 'ul',
26188                 cls : 'dropdown-menu'
26189             }
26190         }
26191         
26192         return cfg;
26193     },
26194     
26195     onRender : function(ct, position)
26196     {
26197         this.isSubMenu = ct.hasClass('dropdown-submenu');
26198         
26199         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26200     },
26201     
26202     initEvents : function() 
26203     {
26204         if(this.isSubMenu){
26205             return;
26206         }
26207         
26208         this.hidden = true;
26209         
26210         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26211         this.triggerEl.on('click', this.onTriggerPress, this);
26212         
26213         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26214         this.buttonEl.on('click', this.onClick, this);
26215         
26216     },
26217     
26218     list : function()
26219     {
26220         if(this.isSubMenu){
26221             return this.el;
26222         }
26223         
26224         return this.el.select('ul.dropdown-menu', true).first();
26225     },
26226     
26227     onClick : function(e)
26228     {
26229         this.fireEvent("click", this, e);
26230     },
26231     
26232     onTriggerPress  : function(e)
26233     {   
26234         if (this.isVisible()) {
26235             this.hide();
26236         } else {
26237             this.show();
26238         }
26239     },
26240     
26241     isVisible : function(){
26242         return !this.hidden;
26243     },
26244     
26245     show : function()
26246     {
26247         this.fireEvent("beforeshow", this);
26248         
26249         this.hidden = false;
26250         this.el.addClass('open');
26251         
26252         Roo.get(document).on("mouseup", this.onMouseUp, this);
26253         
26254         this.fireEvent("show", this);
26255         
26256         
26257     },
26258     
26259     hide : function()
26260     {
26261         this.fireEvent("beforehide", this);
26262         
26263         this.hidden = true;
26264         this.el.removeClass('open');
26265         
26266         Roo.get(document).un("mouseup", this.onMouseUp);
26267         
26268         this.fireEvent("hide", this);
26269     },
26270     
26271     onMouseUp : function()
26272     {
26273         this.hide();
26274     }
26275     
26276 });
26277
26278  
26279  /*
26280  * - LGPL
26281  *
26282  * menu item
26283  * 
26284  */
26285 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26286
26287 /**
26288  * @class Roo.bootstrap.menu.Item
26289  * @extends Roo.bootstrap.Component
26290  * Bootstrap MenuItem class
26291  * @cfg {Boolean} submenu (true | false) default false
26292  * @cfg {String} html text of the item
26293  * @cfg {String} href the link
26294  * @cfg {Boolean} disable (true | false) default false
26295  * @cfg {Boolean} preventDefault (true | false) default true
26296  * @cfg {String} icon Font awesome icon
26297  * @cfg {String} pos Submenu align to (left | right) default right 
26298  * 
26299  * 
26300  * @constructor
26301  * Create a new Item
26302  * @param {Object} config The config object
26303  */
26304
26305
26306 Roo.bootstrap.menu.Item = function(config){
26307     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26308     this.addEvents({
26309         /**
26310          * @event mouseover
26311          * Fires when the mouse is hovering over this menu
26312          * @param {Roo.bootstrap.menu.Item} this
26313          * @param {Roo.EventObject} e
26314          */
26315         mouseover : true,
26316         /**
26317          * @event mouseout
26318          * Fires when the mouse exits this menu
26319          * @param {Roo.bootstrap.menu.Item} this
26320          * @param {Roo.EventObject} e
26321          */
26322         mouseout : true,
26323         // raw events
26324         /**
26325          * @event click
26326          * The raw click event for the entire grid.
26327          * @param {Roo.EventObject} e
26328          */
26329         click : true
26330     });
26331 };
26332
26333 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26334     
26335     submenu : false,
26336     href : '',
26337     html : '',
26338     preventDefault: true,
26339     disable : false,
26340     icon : false,
26341     pos : 'right',
26342     
26343     getAutoCreate : function()
26344     {
26345         var text = [
26346             {
26347                 tag : 'span',
26348                 cls : 'roo-menu-item-text',
26349                 html : this.html
26350             }
26351         ];
26352         
26353         if(this.icon){
26354             text.unshift({
26355                 tag : 'i',
26356                 cls : 'fa ' + this.icon
26357             })
26358         }
26359         
26360         var cfg = {
26361             tag : 'li',
26362             cn : [
26363                 {
26364                     tag : 'a',
26365                     href : this.href || '#',
26366                     cn : text
26367                 }
26368             ]
26369         };
26370         
26371         if(this.disable){
26372             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26373         }
26374         
26375         if(this.submenu){
26376             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26377             
26378             if(this.pos == 'left'){
26379                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26380             }
26381         }
26382         
26383         return cfg;
26384     },
26385     
26386     initEvents : function() 
26387     {
26388         this.el.on('mouseover', this.onMouseOver, this);
26389         this.el.on('mouseout', this.onMouseOut, this);
26390         
26391         this.el.select('a', true).first().on('click', this.onClick, this);
26392         
26393     },
26394     
26395     onClick : function(e)
26396     {
26397         if(this.preventDefault){
26398             e.preventDefault();
26399         }
26400         
26401         this.fireEvent("click", this, e);
26402     },
26403     
26404     onMouseOver : function(e)
26405     {
26406         if(this.submenu && this.pos == 'left'){
26407             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26408         }
26409         
26410         this.fireEvent("mouseover", this, e);
26411     },
26412     
26413     onMouseOut : function(e)
26414     {
26415         this.fireEvent("mouseout", this, e);
26416     }
26417 });
26418
26419  
26420
26421  /*
26422  * - LGPL
26423  *
26424  * menu separator
26425  * 
26426  */
26427 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26428
26429 /**
26430  * @class Roo.bootstrap.menu.Separator
26431  * @extends Roo.bootstrap.Component
26432  * Bootstrap Separator class
26433  * 
26434  * @constructor
26435  * Create a new Separator
26436  * @param {Object} config The config object
26437  */
26438
26439
26440 Roo.bootstrap.menu.Separator = function(config){
26441     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26442 };
26443
26444 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26445     
26446     getAutoCreate : function(){
26447         var cfg = {
26448             tag : 'li',
26449             cls: 'divider'
26450         };
26451         
26452         return cfg;
26453     }
26454    
26455 });
26456
26457  
26458
26459  /*
26460  * - LGPL
26461  *
26462  * Tooltip
26463  * 
26464  */
26465
26466 /**
26467  * @class Roo.bootstrap.Tooltip
26468  * Bootstrap Tooltip class
26469  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26470  * to determine which dom element triggers the tooltip.
26471  * 
26472  * It needs to add support for additional attributes like tooltip-position
26473  * 
26474  * @constructor
26475  * Create a new Toolti
26476  * @param {Object} config The config object
26477  */
26478
26479 Roo.bootstrap.Tooltip = function(config){
26480     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26481     
26482     this.alignment = Roo.bootstrap.Tooltip.alignment;
26483     
26484     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26485         this.alignment = config.alignment;
26486     }
26487     
26488 };
26489
26490 Roo.apply(Roo.bootstrap.Tooltip, {
26491     /**
26492      * @function init initialize tooltip monitoring.
26493      * @static
26494      */
26495     currentEl : false,
26496     currentTip : false,
26497     currentRegion : false,
26498     
26499     //  init : delay?
26500     
26501     init : function()
26502     {
26503         Roo.get(document).on('mouseover', this.enter ,this);
26504         Roo.get(document).on('mouseout', this.leave, this);
26505          
26506         
26507         this.currentTip = new Roo.bootstrap.Tooltip();
26508     },
26509     
26510     enter : function(ev)
26511     {
26512         var dom = ev.getTarget();
26513         
26514         //Roo.log(['enter',dom]);
26515         var el = Roo.fly(dom);
26516         if (this.currentEl) {
26517             //Roo.log(dom);
26518             //Roo.log(this.currentEl);
26519             //Roo.log(this.currentEl.contains(dom));
26520             if (this.currentEl == el) {
26521                 return;
26522             }
26523             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26524                 return;
26525             }
26526
26527         }
26528         
26529         if (this.currentTip.el) {
26530             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26531         }    
26532         //Roo.log(ev);
26533         
26534         if(!el || el.dom == document){
26535             return;
26536         }
26537         
26538         var bindEl = el;
26539         
26540         // you can not look for children, as if el is the body.. then everythign is the child..
26541         if (!el.attr('tooltip')) { //
26542             if (!el.select("[tooltip]").elements.length) {
26543                 return;
26544             }
26545             // is the mouse over this child...?
26546             bindEl = el.select("[tooltip]").first();
26547             var xy = ev.getXY();
26548             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26549                 //Roo.log("not in region.");
26550                 return;
26551             }
26552             //Roo.log("child element over..");
26553             
26554         }
26555         this.currentEl = bindEl;
26556         this.currentTip.bind(bindEl);
26557         this.currentRegion = Roo.lib.Region.getRegion(dom);
26558         this.currentTip.enter();
26559         
26560     },
26561     leave : function(ev)
26562     {
26563         var dom = ev.getTarget();
26564         //Roo.log(['leave',dom]);
26565         if (!this.currentEl) {
26566             return;
26567         }
26568         
26569         
26570         if (dom != this.currentEl.dom) {
26571             return;
26572         }
26573         var xy = ev.getXY();
26574         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26575             return;
26576         }
26577         // only activate leave if mouse cursor is outside... bounding box..
26578         
26579         
26580         
26581         
26582         if (this.currentTip) {
26583             this.currentTip.leave();
26584         }
26585         //Roo.log('clear currentEl');
26586         this.currentEl = false;
26587         
26588         
26589     },
26590     alignment : {
26591         'left' : ['r-l', [-2,0], 'right'],
26592         'right' : ['l-r', [2,0], 'left'],
26593         'bottom' : ['t-b', [0,2], 'top'],
26594         'top' : [ 'b-t', [0,-2], 'bottom']
26595     }
26596     
26597 });
26598
26599
26600 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26601     
26602     
26603     bindEl : false,
26604     
26605     delay : null, // can be { show : 300 , hide: 500}
26606     
26607     timeout : null,
26608     
26609     hoverState : null, //???
26610     
26611     placement : 'bottom', 
26612     
26613     alignment : false,
26614     
26615     getAutoCreate : function(){
26616     
26617         var cfg = {
26618            cls : 'tooltip',
26619            role : 'tooltip',
26620            cn : [
26621                 {
26622                     cls : 'tooltip-arrow'
26623                 },
26624                 {
26625                     cls : 'tooltip-inner'
26626                 }
26627            ]
26628         };
26629         
26630         return cfg;
26631     },
26632     bind : function(el)
26633     {
26634         this.bindEl = el;
26635     },
26636       
26637     
26638     enter : function () {
26639        
26640         if (this.timeout != null) {
26641             clearTimeout(this.timeout);
26642         }
26643         
26644         this.hoverState = 'in';
26645          //Roo.log("enter - show");
26646         if (!this.delay || !this.delay.show) {
26647             this.show();
26648             return;
26649         }
26650         var _t = this;
26651         this.timeout = setTimeout(function () {
26652             if (_t.hoverState == 'in') {
26653                 _t.show();
26654             }
26655         }, this.delay.show);
26656     },
26657     leave : function()
26658     {
26659         clearTimeout(this.timeout);
26660     
26661         this.hoverState = 'out';
26662          if (!this.delay || !this.delay.hide) {
26663             this.hide();
26664             return;
26665         }
26666        
26667         var _t = this;
26668         this.timeout = setTimeout(function () {
26669             //Roo.log("leave - timeout");
26670             
26671             if (_t.hoverState == 'out') {
26672                 _t.hide();
26673                 Roo.bootstrap.Tooltip.currentEl = false;
26674             }
26675         }, delay);
26676     },
26677     
26678     show : function (msg)
26679     {
26680         if (!this.el) {
26681             this.render(document.body);
26682         }
26683         // set content.
26684         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26685         
26686         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26687         
26688         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26689         
26690         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26691         
26692         var placement = typeof this.placement == 'function' ?
26693             this.placement.call(this, this.el, on_el) :
26694             this.placement;
26695             
26696         var autoToken = /\s?auto?\s?/i;
26697         var autoPlace = autoToken.test(placement);
26698         if (autoPlace) {
26699             placement = placement.replace(autoToken, '') || 'top';
26700         }
26701         
26702         //this.el.detach()
26703         //this.el.setXY([0,0]);
26704         this.el.show();
26705         //this.el.dom.style.display='block';
26706         
26707         //this.el.appendTo(on_el);
26708         
26709         var p = this.getPosition();
26710         var box = this.el.getBox();
26711         
26712         if (autoPlace) {
26713             // fixme..
26714         }
26715         
26716         var align = this.alignment[placement];
26717         
26718         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26719         
26720         if(placement == 'top' || placement == 'bottom'){
26721             if(xy[0] < 0){
26722                 placement = 'right';
26723             }
26724             
26725             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26726                 placement = 'left';
26727             }
26728             
26729             var scroll = Roo.select('body', true).first().getScroll();
26730             
26731             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26732                 placement = 'top';
26733             }
26734             
26735             align = this.alignment[placement];
26736         }
26737         
26738         this.el.alignTo(this.bindEl, align[0],align[1]);
26739         //var arrow = this.el.select('.arrow',true).first();
26740         //arrow.set(align[2], 
26741         
26742         this.el.addClass(placement);
26743         
26744         this.el.addClass('in fade');
26745         
26746         this.hoverState = null;
26747         
26748         if (this.el.hasClass('fade')) {
26749             // fade it?
26750         }
26751         
26752     },
26753     hide : function()
26754     {
26755          
26756         if (!this.el) {
26757             return;
26758         }
26759         //this.el.setXY([0,0]);
26760         this.el.removeClass('in');
26761         //this.el.hide();
26762         
26763     }
26764     
26765 });
26766  
26767
26768  /*
26769  * - LGPL
26770  *
26771  * Location Picker
26772  * 
26773  */
26774
26775 /**
26776  * @class Roo.bootstrap.LocationPicker
26777  * @extends Roo.bootstrap.Component
26778  * Bootstrap LocationPicker class
26779  * @cfg {Number} latitude Position when init default 0
26780  * @cfg {Number} longitude Position when init default 0
26781  * @cfg {Number} zoom default 15
26782  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26783  * @cfg {Boolean} mapTypeControl default false
26784  * @cfg {Boolean} disableDoubleClickZoom default false
26785  * @cfg {Boolean} scrollwheel default true
26786  * @cfg {Boolean} streetViewControl default false
26787  * @cfg {Number} radius default 0
26788  * @cfg {String} locationName
26789  * @cfg {Boolean} draggable default true
26790  * @cfg {Boolean} enableAutocomplete default false
26791  * @cfg {Boolean} enableReverseGeocode default true
26792  * @cfg {String} markerTitle
26793  * 
26794  * @constructor
26795  * Create a new LocationPicker
26796  * @param {Object} config The config object
26797  */
26798
26799
26800 Roo.bootstrap.LocationPicker = function(config){
26801     
26802     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26803     
26804     this.addEvents({
26805         /**
26806          * @event initial
26807          * Fires when the picker initialized.
26808          * @param {Roo.bootstrap.LocationPicker} this
26809          * @param {Google Location} location
26810          */
26811         initial : true,
26812         /**
26813          * @event positionchanged
26814          * Fires when the picker position changed.
26815          * @param {Roo.bootstrap.LocationPicker} this
26816          * @param {Google Location} location
26817          */
26818         positionchanged : true,
26819         /**
26820          * @event resize
26821          * Fires when the map resize.
26822          * @param {Roo.bootstrap.LocationPicker} this
26823          */
26824         resize : true,
26825         /**
26826          * @event show
26827          * Fires when the map show.
26828          * @param {Roo.bootstrap.LocationPicker} this
26829          */
26830         show : true,
26831         /**
26832          * @event hide
26833          * Fires when the map hide.
26834          * @param {Roo.bootstrap.LocationPicker} this
26835          */
26836         hide : true,
26837         /**
26838          * @event mapClick
26839          * Fires when click the map.
26840          * @param {Roo.bootstrap.LocationPicker} this
26841          * @param {Map event} e
26842          */
26843         mapClick : true,
26844         /**
26845          * @event mapRightClick
26846          * Fires when right click the map.
26847          * @param {Roo.bootstrap.LocationPicker} this
26848          * @param {Map event} e
26849          */
26850         mapRightClick : true,
26851         /**
26852          * @event markerClick
26853          * Fires when click the marker.
26854          * @param {Roo.bootstrap.LocationPicker} this
26855          * @param {Map event} e
26856          */
26857         markerClick : true,
26858         /**
26859          * @event markerRightClick
26860          * Fires when right click the marker.
26861          * @param {Roo.bootstrap.LocationPicker} this
26862          * @param {Map event} e
26863          */
26864         markerRightClick : true,
26865         /**
26866          * @event OverlayViewDraw
26867          * Fires when OverlayView Draw
26868          * @param {Roo.bootstrap.LocationPicker} this
26869          */
26870         OverlayViewDraw : true,
26871         /**
26872          * @event OverlayViewOnAdd
26873          * Fires when OverlayView Draw
26874          * @param {Roo.bootstrap.LocationPicker} this
26875          */
26876         OverlayViewOnAdd : true,
26877         /**
26878          * @event OverlayViewOnRemove
26879          * Fires when OverlayView Draw
26880          * @param {Roo.bootstrap.LocationPicker} this
26881          */
26882         OverlayViewOnRemove : true,
26883         /**
26884          * @event OverlayViewShow
26885          * Fires when OverlayView Draw
26886          * @param {Roo.bootstrap.LocationPicker} this
26887          * @param {Pixel} cpx
26888          */
26889         OverlayViewShow : true,
26890         /**
26891          * @event OverlayViewHide
26892          * Fires when OverlayView Draw
26893          * @param {Roo.bootstrap.LocationPicker} this
26894          */
26895         OverlayViewHide : true,
26896         /**
26897          * @event loadexception
26898          * Fires when load google lib failed.
26899          * @param {Roo.bootstrap.LocationPicker} this
26900          */
26901         loadexception : true
26902     });
26903         
26904 };
26905
26906 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26907     
26908     gMapContext: false,
26909     
26910     latitude: 0,
26911     longitude: 0,
26912     zoom: 15,
26913     mapTypeId: false,
26914     mapTypeControl: false,
26915     disableDoubleClickZoom: false,
26916     scrollwheel: true,
26917     streetViewControl: false,
26918     radius: 0,
26919     locationName: '',
26920     draggable: true,
26921     enableAutocomplete: false,
26922     enableReverseGeocode: true,
26923     markerTitle: '',
26924     
26925     getAutoCreate: function()
26926     {
26927
26928         var cfg = {
26929             tag: 'div',
26930             cls: 'roo-location-picker'
26931         };
26932         
26933         return cfg
26934     },
26935     
26936     initEvents: function(ct, position)
26937     {       
26938         if(!this.el.getWidth() || this.isApplied()){
26939             return;
26940         }
26941         
26942         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26943         
26944         this.initial();
26945     },
26946     
26947     initial: function()
26948     {
26949         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26950             this.fireEvent('loadexception', this);
26951             return;
26952         }
26953         
26954         if(!this.mapTypeId){
26955             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26956         }
26957         
26958         this.gMapContext = this.GMapContext();
26959         
26960         this.initOverlayView();
26961         
26962         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26963         
26964         var _this = this;
26965                 
26966         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26967             _this.setPosition(_this.gMapContext.marker.position);
26968         });
26969         
26970         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26971             _this.fireEvent('mapClick', this, event);
26972             
26973         });
26974
26975         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26976             _this.fireEvent('mapRightClick', this, event);
26977             
26978         });
26979         
26980         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26981             _this.fireEvent('markerClick', this, event);
26982             
26983         });
26984
26985         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26986             _this.fireEvent('markerRightClick', this, event);
26987             
26988         });
26989         
26990         this.setPosition(this.gMapContext.location);
26991         
26992         this.fireEvent('initial', this, this.gMapContext.location);
26993     },
26994     
26995     initOverlayView: function()
26996     {
26997         var _this = this;
26998         
26999         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27000             
27001             draw: function()
27002             {
27003                 _this.fireEvent('OverlayViewDraw', _this);
27004             },
27005             
27006             onAdd: function()
27007             {
27008                 _this.fireEvent('OverlayViewOnAdd', _this);
27009             },
27010             
27011             onRemove: function()
27012             {
27013                 _this.fireEvent('OverlayViewOnRemove', _this);
27014             },
27015             
27016             show: function(cpx)
27017             {
27018                 _this.fireEvent('OverlayViewShow', _this, cpx);
27019             },
27020             
27021             hide: function()
27022             {
27023                 _this.fireEvent('OverlayViewHide', _this);
27024             }
27025             
27026         });
27027     },
27028     
27029     fromLatLngToContainerPixel: function(event)
27030     {
27031         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27032     },
27033     
27034     isApplied: function() 
27035     {
27036         return this.getGmapContext() == false ? false : true;
27037     },
27038     
27039     getGmapContext: function() 
27040     {
27041         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27042     },
27043     
27044     GMapContext: function() 
27045     {
27046         var position = new google.maps.LatLng(this.latitude, this.longitude);
27047         
27048         var _map = new google.maps.Map(this.el.dom, {
27049             center: position,
27050             zoom: this.zoom,
27051             mapTypeId: this.mapTypeId,
27052             mapTypeControl: this.mapTypeControl,
27053             disableDoubleClickZoom: this.disableDoubleClickZoom,
27054             scrollwheel: this.scrollwheel,
27055             streetViewControl: this.streetViewControl,
27056             locationName: this.locationName,
27057             draggable: this.draggable,
27058             enableAutocomplete: this.enableAutocomplete,
27059             enableReverseGeocode: this.enableReverseGeocode
27060         });
27061         
27062         var _marker = new google.maps.Marker({
27063             position: position,
27064             map: _map,
27065             title: this.markerTitle,
27066             draggable: this.draggable
27067         });
27068         
27069         return {
27070             map: _map,
27071             marker: _marker,
27072             circle: null,
27073             location: position,
27074             radius: this.radius,
27075             locationName: this.locationName,
27076             addressComponents: {
27077                 formatted_address: null,
27078                 addressLine1: null,
27079                 addressLine2: null,
27080                 streetName: null,
27081                 streetNumber: null,
27082                 city: null,
27083                 district: null,
27084                 state: null,
27085                 stateOrProvince: null
27086             },
27087             settings: this,
27088             domContainer: this.el.dom,
27089             geodecoder: new google.maps.Geocoder()
27090         };
27091     },
27092     
27093     drawCircle: function(center, radius, options) 
27094     {
27095         if (this.gMapContext.circle != null) {
27096             this.gMapContext.circle.setMap(null);
27097         }
27098         if (radius > 0) {
27099             radius *= 1;
27100             options = Roo.apply({}, options, {
27101                 strokeColor: "#0000FF",
27102                 strokeOpacity: .35,
27103                 strokeWeight: 2,
27104                 fillColor: "#0000FF",
27105                 fillOpacity: .2
27106             });
27107             
27108             options.map = this.gMapContext.map;
27109             options.radius = radius;
27110             options.center = center;
27111             this.gMapContext.circle = new google.maps.Circle(options);
27112             return this.gMapContext.circle;
27113         }
27114         
27115         return null;
27116     },
27117     
27118     setPosition: function(location) 
27119     {
27120         this.gMapContext.location = location;
27121         this.gMapContext.marker.setPosition(location);
27122         this.gMapContext.map.panTo(location);
27123         this.drawCircle(location, this.gMapContext.radius, {});
27124         
27125         var _this = this;
27126         
27127         if (this.gMapContext.settings.enableReverseGeocode) {
27128             this.gMapContext.geodecoder.geocode({
27129                 latLng: this.gMapContext.location
27130             }, function(results, status) {
27131                 
27132                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27133                     _this.gMapContext.locationName = results[0].formatted_address;
27134                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27135                     
27136                     _this.fireEvent('positionchanged', this, location);
27137                 }
27138             });
27139             
27140             return;
27141         }
27142         
27143         this.fireEvent('positionchanged', this, location);
27144     },
27145     
27146     resize: function()
27147     {
27148         google.maps.event.trigger(this.gMapContext.map, "resize");
27149         
27150         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27151         
27152         this.fireEvent('resize', this);
27153     },
27154     
27155     setPositionByLatLng: function(latitude, longitude)
27156     {
27157         this.setPosition(new google.maps.LatLng(latitude, longitude));
27158     },
27159     
27160     getCurrentPosition: function() 
27161     {
27162         return {
27163             latitude: this.gMapContext.location.lat(),
27164             longitude: this.gMapContext.location.lng()
27165         };
27166     },
27167     
27168     getAddressName: function() 
27169     {
27170         return this.gMapContext.locationName;
27171     },
27172     
27173     getAddressComponents: function() 
27174     {
27175         return this.gMapContext.addressComponents;
27176     },
27177     
27178     address_component_from_google_geocode: function(address_components) 
27179     {
27180         var result = {};
27181         
27182         for (var i = 0; i < address_components.length; i++) {
27183             var component = address_components[i];
27184             if (component.types.indexOf("postal_code") >= 0) {
27185                 result.postalCode = component.short_name;
27186             } else if (component.types.indexOf("street_number") >= 0) {
27187                 result.streetNumber = component.short_name;
27188             } else if (component.types.indexOf("route") >= 0) {
27189                 result.streetName = component.short_name;
27190             } else if (component.types.indexOf("neighborhood") >= 0) {
27191                 result.city = component.short_name;
27192             } else if (component.types.indexOf("locality") >= 0) {
27193                 result.city = component.short_name;
27194             } else if (component.types.indexOf("sublocality") >= 0) {
27195                 result.district = component.short_name;
27196             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27197                 result.stateOrProvince = component.short_name;
27198             } else if (component.types.indexOf("country") >= 0) {
27199                 result.country = component.short_name;
27200             }
27201         }
27202         
27203         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27204         result.addressLine2 = "";
27205         return result;
27206     },
27207     
27208     setZoomLevel: function(zoom)
27209     {
27210         this.gMapContext.map.setZoom(zoom);
27211     },
27212     
27213     show: function()
27214     {
27215         if(!this.el){
27216             return;
27217         }
27218         
27219         this.el.show();
27220         
27221         this.resize();
27222         
27223         this.fireEvent('show', this);
27224     },
27225     
27226     hide: function()
27227     {
27228         if(!this.el){
27229             return;
27230         }
27231         
27232         this.el.hide();
27233         
27234         this.fireEvent('hide', this);
27235     }
27236     
27237 });
27238
27239 Roo.apply(Roo.bootstrap.LocationPicker, {
27240     
27241     OverlayView : function(map, options)
27242     {
27243         options = options || {};
27244         
27245         this.setMap(map);
27246     }
27247     
27248     
27249 });/**
27250  * @class Roo.bootstrap.Alert
27251  * @extends Roo.bootstrap.Component
27252  * Bootstrap Alert class - shows an alert area box
27253  * eg
27254  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27255   Enter a valid email address
27256 </div>
27257  * @licence LGPL
27258  * @cfg {String} title The title of alert
27259  * @cfg {String} html The content of alert
27260  * @cfg {String} weight (  success | info | warning | danger )
27261  * @cfg {String} faicon font-awesomeicon
27262  * 
27263  * @constructor
27264  * Create a new alert
27265  * @param {Object} config The config object
27266  */
27267
27268
27269 Roo.bootstrap.Alert = function(config){
27270     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27271     
27272 };
27273
27274 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27275     
27276     title: '',
27277     html: '',
27278     weight: false,
27279     faicon: false,
27280     
27281     getAutoCreate : function()
27282     {
27283         
27284         var cfg = {
27285             tag : 'div',
27286             cls : 'alert',
27287             cn : [
27288                 {
27289                     tag : 'i',
27290                     cls : 'roo-alert-icon'
27291                     
27292                 },
27293                 {
27294                     tag : 'b',
27295                     cls : 'roo-alert-title',
27296                     html : this.title
27297                 },
27298                 {
27299                     tag : 'span',
27300                     cls : 'roo-alert-text',
27301                     html : this.html
27302                 }
27303             ]
27304         };
27305         
27306         if(this.faicon){
27307             cfg.cn[0].cls += ' fa ' + this.faicon;
27308         }
27309         
27310         if(this.weight){
27311             cfg.cls += ' alert-' + this.weight;
27312         }
27313         
27314         return cfg;
27315     },
27316     
27317     initEvents: function() 
27318     {
27319         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27320     },
27321     
27322     setTitle : function(str)
27323     {
27324         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27325     },
27326     
27327     setText : function(str)
27328     {
27329         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27330     },
27331     
27332     setWeight : function(weight)
27333     {
27334         if(this.weight){
27335             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27336         }
27337         
27338         this.weight = weight;
27339         
27340         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27341     },
27342     
27343     setIcon : function(icon)
27344     {
27345         if(this.faicon){
27346             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27347         }
27348         
27349         this.faicon = icon;
27350         
27351         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27352     },
27353     
27354     hide: function() 
27355     {
27356         this.el.hide();   
27357     },
27358     
27359     show: function() 
27360     {  
27361         this.el.show();   
27362     }
27363     
27364 });
27365
27366  
27367 /*
27368 * Licence: LGPL
27369 */
27370
27371 /**
27372  * @class Roo.bootstrap.UploadCropbox
27373  * @extends Roo.bootstrap.Component
27374  * Bootstrap UploadCropbox class
27375  * @cfg {String} emptyText show when image has been loaded
27376  * @cfg {String} rotateNotify show when image too small to rotate
27377  * @cfg {Number} errorTimeout default 3000
27378  * @cfg {Number} minWidth default 300
27379  * @cfg {Number} minHeight default 300
27380  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27381  * @cfg {Boolean} isDocument (true|false) default false
27382  * @cfg {String} url action url
27383  * @cfg {String} paramName default 'imageUpload'
27384  * @cfg {String} method default POST
27385  * @cfg {Boolean} loadMask (true|false) default true
27386  * @cfg {Boolean} loadingText default 'Loading...'
27387  * 
27388  * @constructor
27389  * Create a new UploadCropbox
27390  * @param {Object} config The config object
27391  */
27392
27393 Roo.bootstrap.UploadCropbox = function(config){
27394     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27395     
27396     this.addEvents({
27397         /**
27398          * @event beforeselectfile
27399          * Fire before select file
27400          * @param {Roo.bootstrap.UploadCropbox} this
27401          */
27402         "beforeselectfile" : true,
27403         /**
27404          * @event initial
27405          * Fire after initEvent
27406          * @param {Roo.bootstrap.UploadCropbox} this
27407          */
27408         "initial" : true,
27409         /**
27410          * @event crop
27411          * Fire after initEvent
27412          * @param {Roo.bootstrap.UploadCropbox} this
27413          * @param {String} data
27414          */
27415         "crop" : true,
27416         /**
27417          * @event prepare
27418          * Fire when preparing the file data
27419          * @param {Roo.bootstrap.UploadCropbox} this
27420          * @param {Object} file
27421          */
27422         "prepare" : true,
27423         /**
27424          * @event exception
27425          * Fire when get exception
27426          * @param {Roo.bootstrap.UploadCropbox} this
27427          * @param {XMLHttpRequest} xhr
27428          */
27429         "exception" : true,
27430         /**
27431          * @event beforeloadcanvas
27432          * Fire before load the canvas
27433          * @param {Roo.bootstrap.UploadCropbox} this
27434          * @param {String} src
27435          */
27436         "beforeloadcanvas" : true,
27437         /**
27438          * @event trash
27439          * Fire when trash image
27440          * @param {Roo.bootstrap.UploadCropbox} this
27441          */
27442         "trash" : true,
27443         /**
27444          * @event download
27445          * Fire when download the image
27446          * @param {Roo.bootstrap.UploadCropbox} this
27447          */
27448         "download" : true,
27449         /**
27450          * @event footerbuttonclick
27451          * Fire when footerbuttonclick
27452          * @param {Roo.bootstrap.UploadCropbox} this
27453          * @param {String} type
27454          */
27455         "footerbuttonclick" : true,
27456         /**
27457          * @event resize
27458          * Fire when resize
27459          * @param {Roo.bootstrap.UploadCropbox} this
27460          */
27461         "resize" : true,
27462         /**
27463          * @event rotate
27464          * Fire when rotate the image
27465          * @param {Roo.bootstrap.UploadCropbox} this
27466          * @param {String} pos
27467          */
27468         "rotate" : true,
27469         /**
27470          * @event inspect
27471          * Fire when inspect the file
27472          * @param {Roo.bootstrap.UploadCropbox} this
27473          * @param {Object} file
27474          */
27475         "inspect" : true,
27476         /**
27477          * @event upload
27478          * Fire when xhr upload the file
27479          * @param {Roo.bootstrap.UploadCropbox} this
27480          * @param {Object} data
27481          */
27482         "upload" : true,
27483         /**
27484          * @event arrange
27485          * Fire when arrange the file data
27486          * @param {Roo.bootstrap.UploadCropbox} this
27487          * @param {Object} formData
27488          */
27489         "arrange" : true
27490     });
27491     
27492     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27493 };
27494
27495 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27496     
27497     emptyText : 'Click to upload image',
27498     rotateNotify : 'Image is too small to rotate',
27499     errorTimeout : 3000,
27500     scale : 0,
27501     baseScale : 1,
27502     rotate : 0,
27503     dragable : false,
27504     pinching : false,
27505     mouseX : 0,
27506     mouseY : 0,
27507     cropData : false,
27508     minWidth : 300,
27509     minHeight : 300,
27510     file : false,
27511     exif : {},
27512     baseRotate : 1,
27513     cropType : 'image/jpeg',
27514     buttons : false,
27515     canvasLoaded : false,
27516     isDocument : false,
27517     method : 'POST',
27518     paramName : 'imageUpload',
27519     loadMask : true,
27520     loadingText : 'Loading...',
27521     maskEl : false,
27522     
27523     getAutoCreate : function()
27524     {
27525         var cfg = {
27526             tag : 'div',
27527             cls : 'roo-upload-cropbox',
27528             cn : [
27529                 {
27530                     tag : 'input',
27531                     cls : 'roo-upload-cropbox-selector',
27532                     type : 'file'
27533                 },
27534                 {
27535                     tag : 'div',
27536                     cls : 'roo-upload-cropbox-body',
27537                     style : 'cursor:pointer',
27538                     cn : [
27539                         {
27540                             tag : 'div',
27541                             cls : 'roo-upload-cropbox-preview'
27542                         },
27543                         {
27544                             tag : 'div',
27545                             cls : 'roo-upload-cropbox-thumb'
27546                         },
27547                         {
27548                             tag : 'div',
27549                             cls : 'roo-upload-cropbox-empty-notify',
27550                             html : this.emptyText
27551                         },
27552                         {
27553                             tag : 'div',
27554                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27555                             html : this.rotateNotify
27556                         }
27557                     ]
27558                 },
27559                 {
27560                     tag : 'div',
27561                     cls : 'roo-upload-cropbox-footer',
27562                     cn : {
27563                         tag : 'div',
27564                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27565                         cn : []
27566                     }
27567                 }
27568             ]
27569         };
27570         
27571         return cfg;
27572     },
27573     
27574     onRender : function(ct, position)
27575     {
27576         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27577         
27578         if (this.buttons.length) {
27579             
27580             Roo.each(this.buttons, function(bb) {
27581                 
27582                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27583                 
27584                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27585                 
27586             }, this);
27587         }
27588         
27589         if(this.loadMask){
27590             this.maskEl = this.el;
27591         }
27592     },
27593     
27594     initEvents : function()
27595     {
27596         this.urlAPI = (window.createObjectURL && window) || 
27597                                 (window.URL && URL.revokeObjectURL && URL) || 
27598                                 (window.webkitURL && webkitURL);
27599                         
27600         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27601         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27602         
27603         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27604         this.selectorEl.hide();
27605         
27606         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27607         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27608         
27609         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27610         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27611         this.thumbEl.hide();
27612         
27613         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27614         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27615         
27616         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27617         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27618         this.errorEl.hide();
27619         
27620         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27621         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27622         this.footerEl.hide();
27623         
27624         this.setThumbBoxSize();
27625         
27626         this.bind();
27627         
27628         this.resize();
27629         
27630         this.fireEvent('initial', this);
27631     },
27632
27633     bind : function()
27634     {
27635         var _this = this;
27636         
27637         window.addEventListener("resize", function() { _this.resize(); } );
27638         
27639         this.bodyEl.on('click', this.beforeSelectFile, this);
27640         
27641         if(Roo.isTouch){
27642             this.bodyEl.on('touchstart', this.onTouchStart, this);
27643             this.bodyEl.on('touchmove', this.onTouchMove, this);
27644             this.bodyEl.on('touchend', this.onTouchEnd, this);
27645         }
27646         
27647         if(!Roo.isTouch){
27648             this.bodyEl.on('mousedown', this.onMouseDown, this);
27649             this.bodyEl.on('mousemove', this.onMouseMove, this);
27650             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27651             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27652             Roo.get(document).on('mouseup', this.onMouseUp, this);
27653         }
27654         
27655         this.selectorEl.on('change', this.onFileSelected, this);
27656     },
27657     
27658     reset : function()
27659     {    
27660         this.scale = 0;
27661         this.baseScale = 1;
27662         this.rotate = 0;
27663         this.baseRotate = 1;
27664         this.dragable = false;
27665         this.pinching = false;
27666         this.mouseX = 0;
27667         this.mouseY = 0;
27668         this.cropData = false;
27669         this.notifyEl.dom.innerHTML = this.emptyText;
27670         
27671         this.selectorEl.dom.value = '';
27672         
27673     },
27674     
27675     resize : function()
27676     {
27677         if(this.fireEvent('resize', this) != false){
27678             this.setThumbBoxPosition();
27679             this.setCanvasPosition();
27680         }
27681     },
27682     
27683     onFooterButtonClick : function(e, el, o, type)
27684     {
27685         switch (type) {
27686             case 'rotate-left' :
27687                 this.onRotateLeft(e);
27688                 break;
27689             case 'rotate-right' :
27690                 this.onRotateRight(e);
27691                 break;
27692             case 'picture' :
27693                 this.beforeSelectFile(e);
27694                 break;
27695             case 'trash' :
27696                 this.trash(e);
27697                 break;
27698             case 'crop' :
27699                 this.crop(e);
27700                 break;
27701             case 'download' :
27702                 this.download(e);
27703                 break;
27704             default :
27705                 break;
27706         }
27707         
27708         this.fireEvent('footerbuttonclick', this, type);
27709     },
27710     
27711     beforeSelectFile : function(e)
27712     {
27713         e.preventDefault();
27714         
27715         if(this.fireEvent('beforeselectfile', this) != false){
27716             this.selectorEl.dom.click();
27717         }
27718     },
27719     
27720     onFileSelected : function(e)
27721     {
27722         e.preventDefault();
27723         
27724         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27725             return;
27726         }
27727         
27728         var file = this.selectorEl.dom.files[0];
27729         
27730         if(this.fireEvent('inspect', this, file) != false){
27731             this.prepare(file);
27732         }
27733         
27734     },
27735     
27736     trash : function(e)
27737     {
27738         this.fireEvent('trash', this);
27739     },
27740     
27741     download : function(e)
27742     {
27743         this.fireEvent('download', this);
27744     },
27745     
27746     loadCanvas : function(src)
27747     {   
27748         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27749             
27750             this.reset();
27751             
27752             this.imageEl = document.createElement('img');
27753             
27754             var _this = this;
27755             
27756             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27757             
27758             this.imageEl.src = src;
27759         }
27760     },
27761     
27762     onLoadCanvas : function()
27763     {   
27764         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27765         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27766         
27767         this.bodyEl.un('click', this.beforeSelectFile, this);
27768         
27769         this.notifyEl.hide();
27770         this.thumbEl.show();
27771         this.footerEl.show();
27772         
27773         this.baseRotateLevel();
27774         
27775         if(this.isDocument){
27776             this.setThumbBoxSize();
27777         }
27778         
27779         this.setThumbBoxPosition();
27780         
27781         this.baseScaleLevel();
27782         
27783         this.draw();
27784         
27785         this.resize();
27786         
27787         this.canvasLoaded = true;
27788         
27789         if(this.loadMask){
27790             this.maskEl.unmask();
27791         }
27792         
27793     },
27794     
27795     setCanvasPosition : function()
27796     {   
27797         if(!this.canvasEl){
27798             return;
27799         }
27800         
27801         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27802         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27803         
27804         this.previewEl.setLeft(pw);
27805         this.previewEl.setTop(ph);
27806         
27807     },
27808     
27809     onMouseDown : function(e)
27810     {   
27811         e.stopEvent();
27812         
27813         this.dragable = true;
27814         this.pinching = false;
27815         
27816         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27817             this.dragable = false;
27818             return;
27819         }
27820         
27821         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27822         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27823         
27824     },
27825     
27826     onMouseMove : function(e)
27827     {   
27828         e.stopEvent();
27829         
27830         if(!this.canvasLoaded){
27831             return;
27832         }
27833         
27834         if (!this.dragable){
27835             return;
27836         }
27837         
27838         var minX = Math.ceil(this.thumbEl.getLeft(true));
27839         var minY = Math.ceil(this.thumbEl.getTop(true));
27840         
27841         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27842         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27843         
27844         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27845         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27846         
27847         x = x - this.mouseX;
27848         y = y - this.mouseY;
27849         
27850         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27851         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27852         
27853         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27854         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27855         
27856         this.previewEl.setLeft(bgX);
27857         this.previewEl.setTop(bgY);
27858         
27859         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27860         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27861     },
27862     
27863     onMouseUp : function(e)
27864     {   
27865         e.stopEvent();
27866         
27867         this.dragable = false;
27868     },
27869     
27870     onMouseWheel : function(e)
27871     {   
27872         e.stopEvent();
27873         
27874         this.startScale = this.scale;
27875         
27876         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27877         
27878         if(!this.zoomable()){
27879             this.scale = this.startScale;
27880             return;
27881         }
27882         
27883         this.draw();
27884         
27885         return;
27886     },
27887     
27888     zoomable : function()
27889     {
27890         var minScale = this.thumbEl.getWidth() / this.minWidth;
27891         
27892         if(this.minWidth < this.minHeight){
27893             minScale = this.thumbEl.getHeight() / this.minHeight;
27894         }
27895         
27896         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27897         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27898         
27899         if(
27900                 this.isDocument &&
27901                 (this.rotate == 0 || this.rotate == 180) && 
27902                 (
27903                     width > this.imageEl.OriginWidth || 
27904                     height > this.imageEl.OriginHeight ||
27905                     (width < this.minWidth && height < this.minHeight)
27906                 )
27907         ){
27908             return false;
27909         }
27910         
27911         if(
27912                 this.isDocument &&
27913                 (this.rotate == 90 || this.rotate == 270) && 
27914                 (
27915                     width > this.imageEl.OriginWidth || 
27916                     height > this.imageEl.OriginHeight ||
27917                     (width < this.minHeight && height < this.minWidth)
27918                 )
27919         ){
27920             return false;
27921         }
27922         
27923         if(
27924                 !this.isDocument &&
27925                 (this.rotate == 0 || this.rotate == 180) && 
27926                 (
27927                     width < this.minWidth || 
27928                     width > this.imageEl.OriginWidth || 
27929                     height < this.minHeight || 
27930                     height > this.imageEl.OriginHeight
27931                 )
27932         ){
27933             return false;
27934         }
27935         
27936         if(
27937                 !this.isDocument &&
27938                 (this.rotate == 90 || this.rotate == 270) && 
27939                 (
27940                     width < this.minHeight || 
27941                     width > this.imageEl.OriginWidth || 
27942                     height < this.minWidth || 
27943                     height > this.imageEl.OriginHeight
27944                 )
27945         ){
27946             return false;
27947         }
27948         
27949         return true;
27950         
27951     },
27952     
27953     onRotateLeft : function(e)
27954     {   
27955         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27956             
27957             var minScale = this.thumbEl.getWidth() / this.minWidth;
27958             
27959             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27960             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27961             
27962             this.startScale = this.scale;
27963             
27964             while (this.getScaleLevel() < minScale){
27965             
27966                 this.scale = this.scale + 1;
27967                 
27968                 if(!this.zoomable()){
27969                     break;
27970                 }
27971                 
27972                 if(
27973                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27974                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27975                 ){
27976                     continue;
27977                 }
27978                 
27979                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27980
27981                 this.draw();
27982                 
27983                 return;
27984             }
27985             
27986             this.scale = this.startScale;
27987             
27988             this.onRotateFail();
27989             
27990             return false;
27991         }
27992         
27993         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27994
27995         if(this.isDocument){
27996             this.setThumbBoxSize();
27997             this.setThumbBoxPosition();
27998             this.setCanvasPosition();
27999         }
28000         
28001         this.draw();
28002         
28003         this.fireEvent('rotate', this, 'left');
28004         
28005     },
28006     
28007     onRotateRight : function(e)
28008     {
28009         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28010             
28011             var minScale = this.thumbEl.getWidth() / this.minWidth;
28012         
28013             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28014             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28015             
28016             this.startScale = this.scale;
28017             
28018             while (this.getScaleLevel() < minScale){
28019             
28020                 this.scale = this.scale + 1;
28021                 
28022                 if(!this.zoomable()){
28023                     break;
28024                 }
28025                 
28026                 if(
28027                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28028                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28029                 ){
28030                     continue;
28031                 }
28032                 
28033                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28034
28035                 this.draw();
28036                 
28037                 return;
28038             }
28039             
28040             this.scale = this.startScale;
28041             
28042             this.onRotateFail();
28043             
28044             return false;
28045         }
28046         
28047         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28048
28049         if(this.isDocument){
28050             this.setThumbBoxSize();
28051             this.setThumbBoxPosition();
28052             this.setCanvasPosition();
28053         }
28054         
28055         this.draw();
28056         
28057         this.fireEvent('rotate', this, 'right');
28058     },
28059     
28060     onRotateFail : function()
28061     {
28062         this.errorEl.show(true);
28063         
28064         var _this = this;
28065         
28066         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28067     },
28068     
28069     draw : function()
28070     {
28071         this.previewEl.dom.innerHTML = '';
28072         
28073         var canvasEl = document.createElement("canvas");
28074         
28075         var contextEl = canvasEl.getContext("2d");
28076         
28077         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28078         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28079         var center = this.imageEl.OriginWidth / 2;
28080         
28081         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28082             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28083             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28084             center = this.imageEl.OriginHeight / 2;
28085         }
28086         
28087         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28088         
28089         contextEl.translate(center, center);
28090         contextEl.rotate(this.rotate * Math.PI / 180);
28091
28092         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28093         
28094         this.canvasEl = document.createElement("canvas");
28095         
28096         this.contextEl = this.canvasEl.getContext("2d");
28097         
28098         switch (this.rotate) {
28099             case 0 :
28100                 
28101                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28102                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28103                 
28104                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28105                 
28106                 break;
28107             case 90 : 
28108                 
28109                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28110                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28111                 
28112                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28113                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28114                     break;
28115                 }
28116                 
28117                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28118                 
28119                 break;
28120             case 180 :
28121                 
28122                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28123                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28124                 
28125                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28126                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28127                     break;
28128                 }
28129                 
28130                 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);
28131                 
28132                 break;
28133             case 270 :
28134                 
28135                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28136                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28137         
28138                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28139                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28140                     break;
28141                 }
28142                 
28143                 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);
28144                 
28145                 break;
28146             default : 
28147                 break;
28148         }
28149         
28150         this.previewEl.appendChild(this.canvasEl);
28151         
28152         this.setCanvasPosition();
28153     },
28154     
28155     crop : function()
28156     {
28157         if(!this.canvasLoaded){
28158             return;
28159         }
28160         
28161         var imageCanvas = document.createElement("canvas");
28162         
28163         var imageContext = imageCanvas.getContext("2d");
28164         
28165         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28166         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28167         
28168         var center = imageCanvas.width / 2;
28169         
28170         imageContext.translate(center, center);
28171         
28172         imageContext.rotate(this.rotate * Math.PI / 180);
28173         
28174         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28175         
28176         var canvas = document.createElement("canvas");
28177         
28178         var context = canvas.getContext("2d");
28179                 
28180         canvas.width = this.minWidth;
28181         canvas.height = this.minHeight;
28182
28183         switch (this.rotate) {
28184             case 0 :
28185                 
28186                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28187                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28188                 
28189                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28190                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28191                 
28192                 var targetWidth = this.minWidth - 2 * x;
28193                 var targetHeight = this.minHeight - 2 * y;
28194                 
28195                 var scale = 1;
28196                 
28197                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28198                     scale = targetWidth / width;
28199                 }
28200                 
28201                 if(x > 0 && y == 0){
28202                     scale = targetHeight / height;
28203                 }
28204                 
28205                 if(x > 0 && y > 0){
28206                     scale = targetWidth / width;
28207                     
28208                     if(width < height){
28209                         scale = targetHeight / height;
28210                     }
28211                 }
28212                 
28213                 context.scale(scale, scale);
28214                 
28215                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28216                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28217
28218                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28219                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28220
28221                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28222                 
28223                 break;
28224             case 90 : 
28225                 
28226                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28227                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28228                 
28229                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28230                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28231                 
28232                 var targetWidth = this.minWidth - 2 * x;
28233                 var targetHeight = this.minHeight - 2 * y;
28234                 
28235                 var scale = 1;
28236                 
28237                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28238                     scale = targetWidth / width;
28239                 }
28240                 
28241                 if(x > 0 && y == 0){
28242                     scale = targetHeight / height;
28243                 }
28244                 
28245                 if(x > 0 && y > 0){
28246                     scale = targetWidth / width;
28247                     
28248                     if(width < height){
28249                         scale = targetHeight / height;
28250                     }
28251                 }
28252                 
28253                 context.scale(scale, scale);
28254                 
28255                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28256                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28257
28258                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28259                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28260                 
28261                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28262                 
28263                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28264                 
28265                 break;
28266             case 180 :
28267                 
28268                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28269                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28270                 
28271                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28272                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28273                 
28274                 var targetWidth = this.minWidth - 2 * x;
28275                 var targetHeight = this.minHeight - 2 * y;
28276                 
28277                 var scale = 1;
28278                 
28279                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28280                     scale = targetWidth / width;
28281                 }
28282                 
28283                 if(x > 0 && y == 0){
28284                     scale = targetHeight / height;
28285                 }
28286                 
28287                 if(x > 0 && y > 0){
28288                     scale = targetWidth / width;
28289                     
28290                     if(width < height){
28291                         scale = targetHeight / height;
28292                     }
28293                 }
28294                 
28295                 context.scale(scale, scale);
28296                 
28297                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28298                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28299
28300                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28301                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28302
28303                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28304                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28305                 
28306                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28307                 
28308                 break;
28309             case 270 :
28310                 
28311                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28312                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28313                 
28314                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28315                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28316                 
28317                 var targetWidth = this.minWidth - 2 * x;
28318                 var targetHeight = this.minHeight - 2 * y;
28319                 
28320                 var scale = 1;
28321                 
28322                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28323                     scale = targetWidth / width;
28324                 }
28325                 
28326                 if(x > 0 && y == 0){
28327                     scale = targetHeight / height;
28328                 }
28329                 
28330                 if(x > 0 && y > 0){
28331                     scale = targetWidth / width;
28332                     
28333                     if(width < height){
28334                         scale = targetHeight / height;
28335                     }
28336                 }
28337                 
28338                 context.scale(scale, scale);
28339                 
28340                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28341                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28342
28343                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28344                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28345                 
28346                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28347                 
28348                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28349                 
28350                 break;
28351             default : 
28352                 break;
28353         }
28354         
28355         this.cropData = canvas.toDataURL(this.cropType);
28356         
28357         if(this.fireEvent('crop', this, this.cropData) !== false){
28358             this.process(this.file, this.cropData);
28359         }
28360         
28361         return;
28362         
28363     },
28364     
28365     setThumbBoxSize : function()
28366     {
28367         var width, height;
28368         
28369         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28370             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28371             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28372             
28373             this.minWidth = width;
28374             this.minHeight = height;
28375             
28376             if(this.rotate == 90 || this.rotate == 270){
28377                 this.minWidth = height;
28378                 this.minHeight = width;
28379             }
28380         }
28381         
28382         height = 300;
28383         width = Math.ceil(this.minWidth * height / this.minHeight);
28384         
28385         if(this.minWidth > this.minHeight){
28386             width = 300;
28387             height = Math.ceil(this.minHeight * width / this.minWidth);
28388         }
28389         
28390         this.thumbEl.setStyle({
28391             width : width + 'px',
28392             height : height + 'px'
28393         });
28394
28395         return;
28396             
28397     },
28398     
28399     setThumbBoxPosition : function()
28400     {
28401         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28402         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28403         
28404         this.thumbEl.setLeft(x);
28405         this.thumbEl.setTop(y);
28406         
28407     },
28408     
28409     baseRotateLevel : function()
28410     {
28411         this.baseRotate = 1;
28412         
28413         if(
28414                 typeof(this.exif) != 'undefined' &&
28415                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28416                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28417         ){
28418             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28419         }
28420         
28421         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28422         
28423     },
28424     
28425     baseScaleLevel : function()
28426     {
28427         var width, height;
28428         
28429         if(this.isDocument){
28430             
28431             if(this.baseRotate == 6 || this.baseRotate == 8){
28432             
28433                 height = this.thumbEl.getHeight();
28434                 this.baseScale = height / this.imageEl.OriginWidth;
28435
28436                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28437                     width = this.thumbEl.getWidth();
28438                     this.baseScale = width / this.imageEl.OriginHeight;
28439                 }
28440
28441                 return;
28442             }
28443
28444             height = this.thumbEl.getHeight();
28445             this.baseScale = height / this.imageEl.OriginHeight;
28446
28447             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28448                 width = this.thumbEl.getWidth();
28449                 this.baseScale = width / this.imageEl.OriginWidth;
28450             }
28451
28452             return;
28453         }
28454         
28455         if(this.baseRotate == 6 || this.baseRotate == 8){
28456             
28457             width = this.thumbEl.getHeight();
28458             this.baseScale = width / this.imageEl.OriginHeight;
28459             
28460             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28461                 height = this.thumbEl.getWidth();
28462                 this.baseScale = height / this.imageEl.OriginHeight;
28463             }
28464             
28465             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28466                 height = this.thumbEl.getWidth();
28467                 this.baseScale = height / this.imageEl.OriginHeight;
28468                 
28469                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28470                     width = this.thumbEl.getHeight();
28471                     this.baseScale = width / this.imageEl.OriginWidth;
28472                 }
28473             }
28474             
28475             return;
28476         }
28477         
28478         width = this.thumbEl.getWidth();
28479         this.baseScale = width / this.imageEl.OriginWidth;
28480         
28481         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28482             height = this.thumbEl.getHeight();
28483             this.baseScale = height / this.imageEl.OriginHeight;
28484         }
28485         
28486         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28487             
28488             height = this.thumbEl.getHeight();
28489             this.baseScale = height / this.imageEl.OriginHeight;
28490             
28491             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28492                 width = this.thumbEl.getWidth();
28493                 this.baseScale = width / this.imageEl.OriginWidth;
28494             }
28495             
28496         }
28497         
28498         return;
28499     },
28500     
28501     getScaleLevel : function()
28502     {
28503         return this.baseScale * Math.pow(1.1, this.scale);
28504     },
28505     
28506     onTouchStart : function(e)
28507     {
28508         if(!this.canvasLoaded){
28509             this.beforeSelectFile(e);
28510             return;
28511         }
28512         
28513         var touches = e.browserEvent.touches;
28514         
28515         if(!touches){
28516             return;
28517         }
28518         
28519         if(touches.length == 1){
28520             this.onMouseDown(e);
28521             return;
28522         }
28523         
28524         if(touches.length != 2){
28525             return;
28526         }
28527         
28528         var coords = [];
28529         
28530         for(var i = 0, finger; finger = touches[i]; i++){
28531             coords.push(finger.pageX, finger.pageY);
28532         }
28533         
28534         var x = Math.pow(coords[0] - coords[2], 2);
28535         var y = Math.pow(coords[1] - coords[3], 2);
28536         
28537         this.startDistance = Math.sqrt(x + y);
28538         
28539         this.startScale = this.scale;
28540         
28541         this.pinching = true;
28542         this.dragable = false;
28543         
28544     },
28545     
28546     onTouchMove : function(e)
28547     {
28548         if(!this.pinching && !this.dragable){
28549             return;
28550         }
28551         
28552         var touches = e.browserEvent.touches;
28553         
28554         if(!touches){
28555             return;
28556         }
28557         
28558         if(this.dragable){
28559             this.onMouseMove(e);
28560             return;
28561         }
28562         
28563         var coords = [];
28564         
28565         for(var i = 0, finger; finger = touches[i]; i++){
28566             coords.push(finger.pageX, finger.pageY);
28567         }
28568         
28569         var x = Math.pow(coords[0] - coords[2], 2);
28570         var y = Math.pow(coords[1] - coords[3], 2);
28571         
28572         this.endDistance = Math.sqrt(x + y);
28573         
28574         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28575         
28576         if(!this.zoomable()){
28577             this.scale = this.startScale;
28578             return;
28579         }
28580         
28581         this.draw();
28582         
28583     },
28584     
28585     onTouchEnd : function(e)
28586     {
28587         this.pinching = false;
28588         this.dragable = false;
28589         
28590     },
28591     
28592     process : function(file, crop)
28593     {
28594         if(this.loadMask){
28595             this.maskEl.mask(this.loadingText);
28596         }
28597         
28598         this.xhr = new XMLHttpRequest();
28599         
28600         file.xhr = this.xhr;
28601
28602         this.xhr.open(this.method, this.url, true);
28603         
28604         var headers = {
28605             "Accept": "application/json",
28606             "Cache-Control": "no-cache",
28607             "X-Requested-With": "XMLHttpRequest"
28608         };
28609         
28610         for (var headerName in headers) {
28611             var headerValue = headers[headerName];
28612             if (headerValue) {
28613                 this.xhr.setRequestHeader(headerName, headerValue);
28614             }
28615         }
28616         
28617         var _this = this;
28618         
28619         this.xhr.onload = function()
28620         {
28621             _this.xhrOnLoad(_this.xhr);
28622         }
28623         
28624         this.xhr.onerror = function()
28625         {
28626             _this.xhrOnError(_this.xhr);
28627         }
28628         
28629         var formData = new FormData();
28630
28631         formData.append('returnHTML', 'NO');
28632         
28633         if(crop){
28634             formData.append('crop', crop);
28635         }
28636         
28637         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28638             formData.append(this.paramName, file, file.name);
28639         }
28640         
28641         if(typeof(file.filename) != 'undefined'){
28642             formData.append('filename', file.filename);
28643         }
28644         
28645         if(typeof(file.mimetype) != 'undefined'){
28646             formData.append('mimetype', file.mimetype);
28647         }
28648         
28649         if(this.fireEvent('arrange', this, formData) != false){
28650             this.xhr.send(formData);
28651         };
28652     },
28653     
28654     xhrOnLoad : function(xhr)
28655     {
28656         if(this.loadMask){
28657             this.maskEl.unmask();
28658         }
28659         
28660         if (xhr.readyState !== 4) {
28661             this.fireEvent('exception', this, xhr);
28662             return;
28663         }
28664
28665         var response = Roo.decode(xhr.responseText);
28666         
28667         if(!response.success){
28668             this.fireEvent('exception', this, xhr);
28669             return;
28670         }
28671         
28672         var response = Roo.decode(xhr.responseText);
28673         
28674         this.fireEvent('upload', this, response);
28675         
28676     },
28677     
28678     xhrOnError : function()
28679     {
28680         if(this.loadMask){
28681             this.maskEl.unmask();
28682         }
28683         
28684         Roo.log('xhr on error');
28685         
28686         var response = Roo.decode(xhr.responseText);
28687           
28688         Roo.log(response);
28689         
28690     },
28691     
28692     prepare : function(file)
28693     {   
28694         if(this.loadMask){
28695             this.maskEl.mask(this.loadingText);
28696         }
28697         
28698         this.file = false;
28699         this.exif = {};
28700         
28701         if(typeof(file) === 'string'){
28702             this.loadCanvas(file);
28703             return;
28704         }
28705         
28706         if(!file || !this.urlAPI){
28707             return;
28708         }
28709         
28710         this.file = file;
28711         this.cropType = file.type;
28712         
28713         var _this = this;
28714         
28715         if(this.fireEvent('prepare', this, this.file) != false){
28716             
28717             var reader = new FileReader();
28718             
28719             reader.onload = function (e) {
28720                 if (e.target.error) {
28721                     Roo.log(e.target.error);
28722                     return;
28723                 }
28724                 
28725                 var buffer = e.target.result,
28726                     dataView = new DataView(buffer),
28727                     offset = 2,
28728                     maxOffset = dataView.byteLength - 4,
28729                     markerBytes,
28730                     markerLength;
28731                 
28732                 if (dataView.getUint16(0) === 0xffd8) {
28733                     while (offset < maxOffset) {
28734                         markerBytes = dataView.getUint16(offset);
28735                         
28736                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28737                             markerLength = dataView.getUint16(offset + 2) + 2;
28738                             if (offset + markerLength > dataView.byteLength) {
28739                                 Roo.log('Invalid meta data: Invalid segment size.');
28740                                 break;
28741                             }
28742                             
28743                             if(markerBytes == 0xffe1){
28744                                 _this.parseExifData(
28745                                     dataView,
28746                                     offset,
28747                                     markerLength
28748                                 );
28749                             }
28750                             
28751                             offset += markerLength;
28752                             
28753                             continue;
28754                         }
28755                         
28756                         break;
28757                     }
28758                     
28759                 }
28760                 
28761                 var url = _this.urlAPI.createObjectURL(_this.file);
28762                 
28763                 _this.loadCanvas(url);
28764                 
28765                 return;
28766             }
28767             
28768             reader.readAsArrayBuffer(this.file);
28769             
28770         }
28771         
28772     },
28773     
28774     parseExifData : function(dataView, offset, length)
28775     {
28776         var tiffOffset = offset + 10,
28777             littleEndian,
28778             dirOffset;
28779     
28780         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28781             // No Exif data, might be XMP data instead
28782             return;
28783         }
28784         
28785         // Check for the ASCII code for "Exif" (0x45786966):
28786         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28787             // No Exif data, might be XMP data instead
28788             return;
28789         }
28790         if (tiffOffset + 8 > dataView.byteLength) {
28791             Roo.log('Invalid Exif data: Invalid segment size.');
28792             return;
28793         }
28794         // Check for the two null bytes:
28795         if (dataView.getUint16(offset + 8) !== 0x0000) {
28796             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28797             return;
28798         }
28799         // Check the byte alignment:
28800         switch (dataView.getUint16(tiffOffset)) {
28801         case 0x4949:
28802             littleEndian = true;
28803             break;
28804         case 0x4D4D:
28805             littleEndian = false;
28806             break;
28807         default:
28808             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28809             return;
28810         }
28811         // Check for the TIFF tag marker (0x002A):
28812         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28813             Roo.log('Invalid Exif data: Missing TIFF marker.');
28814             return;
28815         }
28816         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28817         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28818         
28819         this.parseExifTags(
28820             dataView,
28821             tiffOffset,
28822             tiffOffset + dirOffset,
28823             littleEndian
28824         );
28825     },
28826     
28827     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28828     {
28829         var tagsNumber,
28830             dirEndOffset,
28831             i;
28832         if (dirOffset + 6 > dataView.byteLength) {
28833             Roo.log('Invalid Exif data: Invalid directory offset.');
28834             return;
28835         }
28836         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28837         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28838         if (dirEndOffset + 4 > dataView.byteLength) {
28839             Roo.log('Invalid Exif data: Invalid directory size.');
28840             return;
28841         }
28842         for (i = 0; i < tagsNumber; i += 1) {
28843             this.parseExifTag(
28844                 dataView,
28845                 tiffOffset,
28846                 dirOffset + 2 + 12 * i, // tag offset
28847                 littleEndian
28848             );
28849         }
28850         // Return the offset to the next directory:
28851         return dataView.getUint32(dirEndOffset, littleEndian);
28852     },
28853     
28854     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28855     {
28856         var tag = dataView.getUint16(offset, littleEndian);
28857         
28858         this.exif[tag] = this.getExifValue(
28859             dataView,
28860             tiffOffset,
28861             offset,
28862             dataView.getUint16(offset + 2, littleEndian), // tag type
28863             dataView.getUint32(offset + 4, littleEndian), // tag length
28864             littleEndian
28865         );
28866     },
28867     
28868     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28869     {
28870         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28871             tagSize,
28872             dataOffset,
28873             values,
28874             i,
28875             str,
28876             c;
28877     
28878         if (!tagType) {
28879             Roo.log('Invalid Exif data: Invalid tag type.');
28880             return;
28881         }
28882         
28883         tagSize = tagType.size * length;
28884         // Determine if the value is contained in the dataOffset bytes,
28885         // or if the value at the dataOffset is a pointer to the actual data:
28886         dataOffset = tagSize > 4 ?
28887                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28888         if (dataOffset + tagSize > dataView.byteLength) {
28889             Roo.log('Invalid Exif data: Invalid data offset.');
28890             return;
28891         }
28892         if (length === 1) {
28893             return tagType.getValue(dataView, dataOffset, littleEndian);
28894         }
28895         values = [];
28896         for (i = 0; i < length; i += 1) {
28897             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28898         }
28899         
28900         if (tagType.ascii) {
28901             str = '';
28902             // Concatenate the chars:
28903             for (i = 0; i < values.length; i += 1) {
28904                 c = values[i];
28905                 // Ignore the terminating NULL byte(s):
28906                 if (c === '\u0000') {
28907                     break;
28908                 }
28909                 str += c;
28910             }
28911             return str;
28912         }
28913         return values;
28914     }
28915     
28916 });
28917
28918 Roo.apply(Roo.bootstrap.UploadCropbox, {
28919     tags : {
28920         'Orientation': 0x0112
28921     },
28922     
28923     Orientation: {
28924             1: 0, //'top-left',
28925 //            2: 'top-right',
28926             3: 180, //'bottom-right',
28927 //            4: 'bottom-left',
28928 //            5: 'left-top',
28929             6: 90, //'right-top',
28930 //            7: 'right-bottom',
28931             8: 270 //'left-bottom'
28932     },
28933     
28934     exifTagTypes : {
28935         // byte, 8-bit unsigned int:
28936         1: {
28937             getValue: function (dataView, dataOffset) {
28938                 return dataView.getUint8(dataOffset);
28939             },
28940             size: 1
28941         },
28942         // ascii, 8-bit byte:
28943         2: {
28944             getValue: function (dataView, dataOffset) {
28945                 return String.fromCharCode(dataView.getUint8(dataOffset));
28946             },
28947             size: 1,
28948             ascii: true
28949         },
28950         // short, 16 bit int:
28951         3: {
28952             getValue: function (dataView, dataOffset, littleEndian) {
28953                 return dataView.getUint16(dataOffset, littleEndian);
28954             },
28955             size: 2
28956         },
28957         // long, 32 bit int:
28958         4: {
28959             getValue: function (dataView, dataOffset, littleEndian) {
28960                 return dataView.getUint32(dataOffset, littleEndian);
28961             },
28962             size: 4
28963         },
28964         // rational = two long values, first is numerator, second is denominator:
28965         5: {
28966             getValue: function (dataView, dataOffset, littleEndian) {
28967                 return dataView.getUint32(dataOffset, littleEndian) /
28968                     dataView.getUint32(dataOffset + 4, littleEndian);
28969             },
28970             size: 8
28971         },
28972         // slong, 32 bit signed int:
28973         9: {
28974             getValue: function (dataView, dataOffset, littleEndian) {
28975                 return dataView.getInt32(dataOffset, littleEndian);
28976             },
28977             size: 4
28978         },
28979         // srational, two slongs, first is numerator, second is denominator:
28980         10: {
28981             getValue: function (dataView, dataOffset, littleEndian) {
28982                 return dataView.getInt32(dataOffset, littleEndian) /
28983                     dataView.getInt32(dataOffset + 4, littleEndian);
28984             },
28985             size: 8
28986         }
28987     },
28988     
28989     footer : {
28990         STANDARD : [
28991             {
28992                 tag : 'div',
28993                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28994                 action : 'rotate-left',
28995                 cn : [
28996                     {
28997                         tag : 'button',
28998                         cls : 'btn btn-default',
28999                         html : '<i class="fa fa-undo"></i>'
29000                     }
29001                 ]
29002             },
29003             {
29004                 tag : 'div',
29005                 cls : 'btn-group roo-upload-cropbox-picture',
29006                 action : 'picture',
29007                 cn : [
29008                     {
29009                         tag : 'button',
29010                         cls : 'btn btn-default',
29011                         html : '<i class="fa fa-picture-o"></i>'
29012                     }
29013                 ]
29014             },
29015             {
29016                 tag : 'div',
29017                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29018                 action : 'rotate-right',
29019                 cn : [
29020                     {
29021                         tag : 'button',
29022                         cls : 'btn btn-default',
29023                         html : '<i class="fa fa-repeat"></i>'
29024                     }
29025                 ]
29026             }
29027         ],
29028         DOCUMENT : [
29029             {
29030                 tag : 'div',
29031                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29032                 action : 'rotate-left',
29033                 cn : [
29034                     {
29035                         tag : 'button',
29036                         cls : 'btn btn-default',
29037                         html : '<i class="fa fa-undo"></i>'
29038                     }
29039                 ]
29040             },
29041             {
29042                 tag : 'div',
29043                 cls : 'btn-group roo-upload-cropbox-download',
29044                 action : 'download',
29045                 cn : [
29046                     {
29047                         tag : 'button',
29048                         cls : 'btn btn-default',
29049                         html : '<i class="fa fa-download"></i>'
29050                     }
29051                 ]
29052             },
29053             {
29054                 tag : 'div',
29055                 cls : 'btn-group roo-upload-cropbox-crop',
29056                 action : 'crop',
29057                 cn : [
29058                     {
29059                         tag : 'button',
29060                         cls : 'btn btn-default',
29061                         html : '<i class="fa fa-crop"></i>'
29062                     }
29063                 ]
29064             },
29065             {
29066                 tag : 'div',
29067                 cls : 'btn-group roo-upload-cropbox-trash',
29068                 action : 'trash',
29069                 cn : [
29070                     {
29071                         tag : 'button',
29072                         cls : 'btn btn-default',
29073                         html : '<i class="fa fa-trash"></i>'
29074                     }
29075                 ]
29076             },
29077             {
29078                 tag : 'div',
29079                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29080                 action : 'rotate-right',
29081                 cn : [
29082                     {
29083                         tag : 'button',
29084                         cls : 'btn btn-default',
29085                         html : '<i class="fa fa-repeat"></i>'
29086                     }
29087                 ]
29088             }
29089         ],
29090         ROTATOR : [
29091             {
29092                 tag : 'div',
29093                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29094                 action : 'rotate-left',
29095                 cn : [
29096                     {
29097                         tag : 'button',
29098                         cls : 'btn btn-default',
29099                         html : '<i class="fa fa-undo"></i>'
29100                     }
29101                 ]
29102             },
29103             {
29104                 tag : 'div',
29105                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29106                 action : 'rotate-right',
29107                 cn : [
29108                     {
29109                         tag : 'button',
29110                         cls : 'btn btn-default',
29111                         html : '<i class="fa fa-repeat"></i>'
29112                     }
29113                 ]
29114             }
29115         ]
29116     }
29117 });
29118
29119 /*
29120 * Licence: LGPL
29121 */
29122
29123 /**
29124  * @class Roo.bootstrap.DocumentManager
29125  * @extends Roo.bootstrap.Component
29126  * Bootstrap DocumentManager class
29127  * @cfg {String} paramName default 'imageUpload'
29128  * @cfg {String} toolTipName default 'filename'
29129  * @cfg {String} method default POST
29130  * @cfg {String} url action url
29131  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29132  * @cfg {Boolean} multiple multiple upload default true
29133  * @cfg {Number} thumbSize default 300
29134  * @cfg {String} fieldLabel
29135  * @cfg {Number} labelWidth default 4
29136  * @cfg {String} labelAlign (left|top) default left
29137  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29138 * @cfg {Number} labellg set the width of label (1-12)
29139  * @cfg {Number} labelmd set the width of label (1-12)
29140  * @cfg {Number} labelsm set the width of label (1-12)
29141  * @cfg {Number} labelxs set the width of label (1-12)
29142  * 
29143  * @constructor
29144  * Create a new DocumentManager
29145  * @param {Object} config The config object
29146  */
29147
29148 Roo.bootstrap.DocumentManager = function(config){
29149     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29150     
29151     this.files = [];
29152     this.delegates = [];
29153     
29154     this.addEvents({
29155         /**
29156          * @event initial
29157          * Fire when initial the DocumentManager
29158          * @param {Roo.bootstrap.DocumentManager} this
29159          */
29160         "initial" : true,
29161         /**
29162          * @event inspect
29163          * inspect selected file
29164          * @param {Roo.bootstrap.DocumentManager} this
29165          * @param {File} file
29166          */
29167         "inspect" : true,
29168         /**
29169          * @event exception
29170          * Fire when xhr load exception
29171          * @param {Roo.bootstrap.DocumentManager} this
29172          * @param {XMLHttpRequest} xhr
29173          */
29174         "exception" : true,
29175         /**
29176          * @event afterupload
29177          * Fire when xhr load exception
29178          * @param {Roo.bootstrap.DocumentManager} this
29179          * @param {XMLHttpRequest} xhr
29180          */
29181         "afterupload" : true,
29182         /**
29183          * @event prepare
29184          * prepare the form data
29185          * @param {Roo.bootstrap.DocumentManager} this
29186          * @param {Object} formData
29187          */
29188         "prepare" : true,
29189         /**
29190          * @event remove
29191          * Fire when remove the file
29192          * @param {Roo.bootstrap.DocumentManager} this
29193          * @param {Object} file
29194          */
29195         "remove" : true,
29196         /**
29197          * @event refresh
29198          * Fire after refresh the file
29199          * @param {Roo.bootstrap.DocumentManager} this
29200          */
29201         "refresh" : true,
29202         /**
29203          * @event click
29204          * Fire after click the image
29205          * @param {Roo.bootstrap.DocumentManager} this
29206          * @param {Object} file
29207          */
29208         "click" : true,
29209         /**
29210          * @event edit
29211          * Fire when upload a image and editable set to true
29212          * @param {Roo.bootstrap.DocumentManager} this
29213          * @param {Object} file
29214          */
29215         "edit" : true,
29216         /**
29217          * @event beforeselectfile
29218          * Fire before select file
29219          * @param {Roo.bootstrap.DocumentManager} this
29220          */
29221         "beforeselectfile" : true,
29222         /**
29223          * @event process
29224          * Fire before process file
29225          * @param {Roo.bootstrap.DocumentManager} this
29226          * @param {Object} file
29227          */
29228         "process" : true,
29229         /**
29230          * @event previewrendered
29231          * Fire when preview rendered
29232          * @param {Roo.bootstrap.DocumentManager} this
29233          * @param {Object} file
29234          */
29235         "previewrendered" : true,
29236         /**
29237          */
29238         "previewResize" : true
29239         
29240     });
29241 };
29242
29243 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29244     
29245     boxes : 0,
29246     inputName : '',
29247     thumbSize : 300,
29248     multiple : true,
29249     files : false,
29250     method : 'POST',
29251     url : '',
29252     paramName : 'imageUpload',
29253     toolTipName : 'filename',
29254     fieldLabel : '',
29255     labelWidth : 4,
29256     labelAlign : 'left',
29257     editable : true,
29258     delegates : false,
29259     xhr : false, 
29260     
29261     labellg : 0,
29262     labelmd : 0,
29263     labelsm : 0,
29264     labelxs : 0,
29265     
29266     getAutoCreate : function()
29267     {   
29268         var managerWidget = {
29269             tag : 'div',
29270             cls : 'roo-document-manager',
29271             cn : [
29272                 {
29273                     tag : 'input',
29274                     cls : 'roo-document-manager-selector',
29275                     type : 'file'
29276                 },
29277                 {
29278                     tag : 'div',
29279                     cls : 'roo-document-manager-uploader',
29280                     cn : [
29281                         {
29282                             tag : 'div',
29283                             cls : 'roo-document-manager-upload-btn',
29284                             html : '<i class="fa fa-plus"></i>'
29285                         }
29286                     ]
29287                     
29288                 }
29289             ]
29290         };
29291         
29292         var content = [
29293             {
29294                 tag : 'div',
29295                 cls : 'column col-md-12',
29296                 cn : managerWidget
29297             }
29298         ];
29299         
29300         if(this.fieldLabel.length){
29301             
29302             content = [
29303                 {
29304                     tag : 'div',
29305                     cls : 'column col-md-12',
29306                     html : this.fieldLabel
29307                 },
29308                 {
29309                     tag : 'div',
29310                     cls : 'column col-md-12',
29311                     cn : managerWidget
29312                 }
29313             ];
29314
29315             if(this.labelAlign == 'left'){
29316                 content = [
29317                     {
29318                         tag : 'div',
29319                         cls : 'column',
29320                         html : this.fieldLabel
29321                     },
29322                     {
29323                         tag : 'div',
29324                         cls : 'column',
29325                         cn : managerWidget
29326                     }
29327                 ];
29328                 
29329                 if(this.labelWidth > 12){
29330                     content[0].style = "width: " + this.labelWidth + 'px';
29331                 }
29332
29333                 if(this.labelWidth < 13 && this.labelmd == 0){
29334                     this.labelmd = this.labelWidth;
29335                 }
29336
29337                 if(this.labellg > 0){
29338                     content[0].cls += ' col-lg-' + this.labellg;
29339                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29340                 }
29341
29342                 if(this.labelmd > 0){
29343                     content[0].cls += ' col-md-' + this.labelmd;
29344                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29345                 }
29346
29347                 if(this.labelsm > 0){
29348                     content[0].cls += ' col-sm-' + this.labelsm;
29349                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29350                 }
29351
29352                 if(this.labelxs > 0){
29353                     content[0].cls += ' col-xs-' + this.labelxs;
29354                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29355                 }
29356                 
29357             }
29358         }
29359         
29360         var cfg = {
29361             tag : 'div',
29362             cls : 'row clearfix',
29363             cn : content
29364         };
29365         
29366         return cfg;
29367         
29368     },
29369     
29370     initEvents : function()
29371     {
29372         this.managerEl = this.el.select('.roo-document-manager', true).first();
29373         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29374         
29375         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29376         this.selectorEl.hide();
29377         
29378         if(this.multiple){
29379             this.selectorEl.attr('multiple', 'multiple');
29380         }
29381         
29382         this.selectorEl.on('change', this.onFileSelected, this);
29383         
29384         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29385         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29386         
29387         this.uploader.on('click', this.onUploaderClick, this);
29388         
29389         this.renderProgressDialog();
29390         
29391         var _this = this;
29392         
29393         window.addEventListener("resize", function() { _this.refresh(); } );
29394         
29395         this.fireEvent('initial', this);
29396     },
29397     
29398     renderProgressDialog : function()
29399     {
29400         var _this = this;
29401         
29402         this.progressDialog = new Roo.bootstrap.Modal({
29403             cls : 'roo-document-manager-progress-dialog',
29404             allow_close : false,
29405             animate : false,
29406             title : '',
29407             buttons : [
29408                 {
29409                     name  :'cancel',
29410                     weight : 'danger',
29411                     html : 'Cancel'
29412                 }
29413             ], 
29414             listeners : { 
29415                 btnclick : function() {
29416                     _this.uploadCancel();
29417                     this.hide();
29418                 }
29419             }
29420         });
29421          
29422         this.progressDialog.render(Roo.get(document.body));
29423          
29424         this.progress = new Roo.bootstrap.Progress({
29425             cls : 'roo-document-manager-progress',
29426             active : true,
29427             striped : true
29428         });
29429         
29430         this.progress.render(this.progressDialog.getChildContainer());
29431         
29432         this.progressBar = new Roo.bootstrap.ProgressBar({
29433             cls : 'roo-document-manager-progress-bar',
29434             aria_valuenow : 0,
29435             aria_valuemin : 0,
29436             aria_valuemax : 12,
29437             panel : 'success'
29438         });
29439         
29440         this.progressBar.render(this.progress.getChildContainer());
29441     },
29442     
29443     onUploaderClick : function(e)
29444     {
29445         e.preventDefault();
29446      
29447         if(this.fireEvent('beforeselectfile', this) != false){
29448             this.selectorEl.dom.click();
29449         }
29450         
29451     },
29452     
29453     onFileSelected : function(e)
29454     {
29455         e.preventDefault();
29456         
29457         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29458             return;
29459         }
29460         
29461         Roo.each(this.selectorEl.dom.files, function(file){
29462             if(this.fireEvent('inspect', this, file) != false){
29463                 this.files.push(file);
29464             }
29465         }, this);
29466         
29467         this.queue();
29468         
29469     },
29470     
29471     queue : function()
29472     {
29473         this.selectorEl.dom.value = '';
29474         
29475         if(!this.files || !this.files.length){
29476             return;
29477         }
29478         
29479         if(this.boxes > 0 && this.files.length > this.boxes){
29480             this.files = this.files.slice(0, this.boxes);
29481         }
29482         
29483         this.uploader.show();
29484         
29485         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29486             this.uploader.hide();
29487         }
29488         
29489         var _this = this;
29490         
29491         var files = [];
29492         
29493         var docs = [];
29494         
29495         Roo.each(this.files, function(file){
29496             
29497             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29498                 var f = this.renderPreview(file);
29499                 files.push(f);
29500                 return;
29501             }
29502             
29503             if(file.type.indexOf('image') != -1){
29504                 this.delegates.push(
29505                     (function(){
29506                         _this.process(file);
29507                     }).createDelegate(this)
29508                 );
29509         
29510                 return;
29511             }
29512             
29513             docs.push(
29514                 (function(){
29515                     _this.process(file);
29516                 }).createDelegate(this)
29517             );
29518             
29519         }, this);
29520         
29521         this.files = files;
29522         
29523         this.delegates = this.delegates.concat(docs);
29524         
29525         if(!this.delegates.length){
29526             this.refresh();
29527             return;
29528         }
29529         
29530         this.progressBar.aria_valuemax = this.delegates.length;
29531         
29532         this.arrange();
29533         
29534         return;
29535     },
29536     
29537     arrange : function()
29538     {
29539         if(!this.delegates.length){
29540             this.progressDialog.hide();
29541             this.refresh();
29542             return;
29543         }
29544         
29545         var delegate = this.delegates.shift();
29546         
29547         this.progressDialog.show();
29548         
29549         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29550         
29551         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29552         
29553         delegate();
29554     },
29555     
29556     refresh : function()
29557     {
29558         this.uploader.show();
29559         
29560         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29561             this.uploader.hide();
29562         }
29563         
29564         Roo.isTouch ? this.closable(false) : this.closable(true);
29565         
29566         this.fireEvent('refresh', this);
29567     },
29568     
29569     onRemove : function(e, el, o)
29570     {
29571         e.preventDefault();
29572         
29573         this.fireEvent('remove', this, o);
29574         
29575     },
29576     
29577     remove : function(o)
29578     {
29579         var files = [];
29580         
29581         Roo.each(this.files, function(file){
29582             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29583                 files.push(file);
29584                 return;
29585             }
29586
29587             o.target.remove();
29588
29589         }, this);
29590         
29591         this.files = files;
29592         
29593         this.refresh();
29594     },
29595     
29596     clear : function()
29597     {
29598         Roo.each(this.files, function(file){
29599             if(!file.target){
29600                 return;
29601             }
29602             
29603             file.target.remove();
29604
29605         }, this);
29606         
29607         this.files = [];
29608         
29609         this.refresh();
29610     },
29611     
29612     onClick : function(e, el, o)
29613     {
29614         e.preventDefault();
29615         
29616         this.fireEvent('click', this, o);
29617         
29618     },
29619     
29620     closable : function(closable)
29621     {
29622         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29623             
29624             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29625             
29626             if(closable){
29627                 el.show();
29628                 return;
29629             }
29630             
29631             el.hide();
29632             
29633         }, this);
29634     },
29635     
29636     xhrOnLoad : function(xhr)
29637     {
29638         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29639             el.remove();
29640         }, this);
29641         
29642         if (xhr.readyState !== 4) {
29643             this.arrange();
29644             this.fireEvent('exception', this, xhr);
29645             return;
29646         }
29647
29648         var response = Roo.decode(xhr.responseText);
29649         
29650         if(!response.success){
29651             this.arrange();
29652             this.fireEvent('exception', this, xhr);
29653             return;
29654         }
29655         
29656         var file = this.renderPreview(response.data);
29657         
29658         this.files.push(file);
29659         
29660         this.arrange();
29661         
29662         this.fireEvent('afterupload', this, xhr);
29663         
29664     },
29665     
29666     xhrOnError : function(xhr)
29667     {
29668         Roo.log('xhr on error');
29669         
29670         var response = Roo.decode(xhr.responseText);
29671           
29672         Roo.log(response);
29673         
29674         this.arrange();
29675     },
29676     
29677     process : function(file)
29678     {
29679         if(this.fireEvent('process', this, file) !== false){
29680             if(this.editable && file.type.indexOf('image') != -1){
29681                 this.fireEvent('edit', this, file);
29682                 return;
29683             }
29684
29685             this.uploadStart(file, false);
29686
29687             return;
29688         }
29689         
29690     },
29691     
29692     uploadStart : function(file, crop)
29693     {
29694         this.xhr = new XMLHttpRequest();
29695         
29696         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29697             this.arrange();
29698             return;
29699         }
29700         
29701         file.xhr = this.xhr;
29702             
29703         this.managerEl.createChild({
29704             tag : 'div',
29705             cls : 'roo-document-manager-loading',
29706             cn : [
29707                 {
29708                     tag : 'div',
29709                     tooltip : file.name,
29710                     cls : 'roo-document-manager-thumb',
29711                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29712                 }
29713             ]
29714
29715         });
29716
29717         this.xhr.open(this.method, this.url, true);
29718         
29719         var headers = {
29720             "Accept": "application/json",
29721             "Cache-Control": "no-cache",
29722             "X-Requested-With": "XMLHttpRequest"
29723         };
29724         
29725         for (var headerName in headers) {
29726             var headerValue = headers[headerName];
29727             if (headerValue) {
29728                 this.xhr.setRequestHeader(headerName, headerValue);
29729             }
29730         }
29731         
29732         var _this = this;
29733         
29734         this.xhr.onload = function()
29735         {
29736             _this.xhrOnLoad(_this.xhr);
29737         }
29738         
29739         this.xhr.onerror = function()
29740         {
29741             _this.xhrOnError(_this.xhr);
29742         }
29743         
29744         var formData = new FormData();
29745
29746         formData.append('returnHTML', 'NO');
29747         
29748         if(crop){
29749             formData.append('crop', crop);
29750         }
29751         
29752         formData.append(this.paramName, file, file.name);
29753         
29754         var options = {
29755             file : file, 
29756             manually : false
29757         };
29758         
29759         if(this.fireEvent('prepare', this, formData, options) != false){
29760             
29761             if(options.manually){
29762                 return;
29763             }
29764             
29765             this.xhr.send(formData);
29766             return;
29767         };
29768         
29769         this.uploadCancel();
29770     },
29771     
29772     uploadCancel : function()
29773     {
29774         if (this.xhr) {
29775             this.xhr.abort();
29776         }
29777         
29778         this.delegates = [];
29779         
29780         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29781             el.remove();
29782         }, this);
29783         
29784         this.arrange();
29785     },
29786     
29787     renderPreview : function(file)
29788     {
29789         if(typeof(file.target) != 'undefined' && file.target){
29790             return file;
29791         }
29792         
29793         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29794         
29795         var previewEl = this.managerEl.createChild({
29796             tag : 'div',
29797             cls : 'roo-document-manager-preview',
29798             cn : [
29799                 {
29800                     tag : 'div',
29801                     tooltip : file[this.toolTipName],
29802                     cls : 'roo-document-manager-thumb',
29803                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29804                 },
29805                 {
29806                     tag : 'button',
29807                     cls : 'close',
29808                     html : '<i class="fa fa-times-circle"></i>'
29809                 }
29810             ]
29811         });
29812
29813         var close = previewEl.select('button.close', true).first();
29814
29815         close.on('click', this.onRemove, this, file);
29816
29817         file.target = previewEl;
29818
29819         var image = previewEl.select('img', true).first();
29820         
29821         var _this = this;
29822         
29823         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29824         
29825         image.on('click', this.onClick, this, file);
29826         
29827         this.fireEvent('previewrendered', this, file);
29828         
29829         return file;
29830         
29831     },
29832     
29833     onPreviewLoad : function(file, image)
29834     {
29835         if(typeof(file.target) == 'undefined' || !file.target){
29836             return;
29837         }
29838         
29839         var width = image.dom.naturalWidth || image.dom.width;
29840         var height = image.dom.naturalHeight || image.dom.height;
29841         
29842         if(!this.previewResize) {
29843             return;
29844         }
29845         
29846         if(width > height){
29847             file.target.addClass('wide');
29848             return;
29849         }
29850         
29851         file.target.addClass('tall');
29852         return;
29853         
29854     },
29855     
29856     uploadFromSource : function(file, crop)
29857     {
29858         this.xhr = new XMLHttpRequest();
29859         
29860         this.managerEl.createChild({
29861             tag : 'div',
29862             cls : 'roo-document-manager-loading',
29863             cn : [
29864                 {
29865                     tag : 'div',
29866                     tooltip : file.name,
29867                     cls : 'roo-document-manager-thumb',
29868                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29869                 }
29870             ]
29871
29872         });
29873
29874         this.xhr.open(this.method, this.url, true);
29875         
29876         var headers = {
29877             "Accept": "application/json",
29878             "Cache-Control": "no-cache",
29879             "X-Requested-With": "XMLHttpRequest"
29880         };
29881         
29882         for (var headerName in headers) {
29883             var headerValue = headers[headerName];
29884             if (headerValue) {
29885                 this.xhr.setRequestHeader(headerName, headerValue);
29886             }
29887         }
29888         
29889         var _this = this;
29890         
29891         this.xhr.onload = function()
29892         {
29893             _this.xhrOnLoad(_this.xhr);
29894         }
29895         
29896         this.xhr.onerror = function()
29897         {
29898             _this.xhrOnError(_this.xhr);
29899         }
29900         
29901         var formData = new FormData();
29902
29903         formData.append('returnHTML', 'NO');
29904         
29905         formData.append('crop', crop);
29906         
29907         if(typeof(file.filename) != 'undefined'){
29908             formData.append('filename', file.filename);
29909         }
29910         
29911         if(typeof(file.mimetype) != 'undefined'){
29912             formData.append('mimetype', file.mimetype);
29913         }
29914         
29915         Roo.log(formData);
29916         
29917         if(this.fireEvent('prepare', this, formData) != false){
29918             this.xhr.send(formData);
29919         };
29920     }
29921 });
29922
29923 /*
29924 * Licence: LGPL
29925 */
29926
29927 /**
29928  * @class Roo.bootstrap.DocumentViewer
29929  * @extends Roo.bootstrap.Component
29930  * Bootstrap DocumentViewer class
29931  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29932  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29933  * 
29934  * @constructor
29935  * Create a new DocumentViewer
29936  * @param {Object} config The config object
29937  */
29938
29939 Roo.bootstrap.DocumentViewer = function(config){
29940     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29941     
29942     this.addEvents({
29943         /**
29944          * @event initial
29945          * Fire after initEvent
29946          * @param {Roo.bootstrap.DocumentViewer} this
29947          */
29948         "initial" : true,
29949         /**
29950          * @event click
29951          * Fire after click
29952          * @param {Roo.bootstrap.DocumentViewer} this
29953          */
29954         "click" : true,
29955         /**
29956          * @event download
29957          * Fire after download button
29958          * @param {Roo.bootstrap.DocumentViewer} this
29959          */
29960         "download" : true,
29961         /**
29962          * @event trash
29963          * Fire after trash button
29964          * @param {Roo.bootstrap.DocumentViewer} this
29965          */
29966         "trash" : true
29967         
29968     });
29969 };
29970
29971 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29972     
29973     showDownload : true,
29974     
29975     showTrash : true,
29976     
29977     getAutoCreate : function()
29978     {
29979         var cfg = {
29980             tag : 'div',
29981             cls : 'roo-document-viewer',
29982             cn : [
29983                 {
29984                     tag : 'div',
29985                     cls : 'roo-document-viewer-body',
29986                     cn : [
29987                         {
29988                             tag : 'div',
29989                             cls : 'roo-document-viewer-thumb',
29990                             cn : [
29991                                 {
29992                                     tag : 'img',
29993                                     cls : 'roo-document-viewer-image'
29994                                 }
29995                             ]
29996                         }
29997                     ]
29998                 },
29999                 {
30000                     tag : 'div',
30001                     cls : 'roo-document-viewer-footer',
30002                     cn : {
30003                         tag : 'div',
30004                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30005                         cn : [
30006                             {
30007                                 tag : 'div',
30008                                 cls : 'btn-group roo-document-viewer-download',
30009                                 cn : [
30010                                     {
30011                                         tag : 'button',
30012                                         cls : 'btn btn-default',
30013                                         html : '<i class="fa fa-download"></i>'
30014                                     }
30015                                 ]
30016                             },
30017                             {
30018                                 tag : 'div',
30019                                 cls : 'btn-group roo-document-viewer-trash',
30020                                 cn : [
30021                                     {
30022                                         tag : 'button',
30023                                         cls : 'btn btn-default',
30024                                         html : '<i class="fa fa-trash"></i>'
30025                                     }
30026                                 ]
30027                             }
30028                         ]
30029                     }
30030                 }
30031             ]
30032         };
30033         
30034         return cfg;
30035     },
30036     
30037     initEvents : function()
30038     {
30039         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30040         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30041         
30042         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30043         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30044         
30045         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30046         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30047         
30048         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30049         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30050         
30051         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30052         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30053         
30054         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30055         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30056         
30057         this.bodyEl.on('click', this.onClick, this);
30058         this.downloadBtn.on('click', this.onDownload, this);
30059         this.trashBtn.on('click', this.onTrash, this);
30060         
30061         this.downloadBtn.hide();
30062         this.trashBtn.hide();
30063         
30064         if(this.showDownload){
30065             this.downloadBtn.show();
30066         }
30067         
30068         if(this.showTrash){
30069             this.trashBtn.show();
30070         }
30071         
30072         if(!this.showDownload && !this.showTrash) {
30073             this.footerEl.hide();
30074         }
30075         
30076     },
30077     
30078     initial : function()
30079     {
30080         this.fireEvent('initial', this);
30081         
30082     },
30083     
30084     onClick : function(e)
30085     {
30086         e.preventDefault();
30087         
30088         this.fireEvent('click', this);
30089     },
30090     
30091     onDownload : function(e)
30092     {
30093         e.preventDefault();
30094         
30095         this.fireEvent('download', this);
30096     },
30097     
30098     onTrash : function(e)
30099     {
30100         e.preventDefault();
30101         
30102         this.fireEvent('trash', this);
30103     }
30104     
30105 });
30106 /*
30107  * - LGPL
30108  *
30109  * nav progress bar
30110  * 
30111  */
30112
30113 /**
30114  * @class Roo.bootstrap.NavProgressBar
30115  * @extends Roo.bootstrap.Component
30116  * Bootstrap NavProgressBar class
30117  * 
30118  * @constructor
30119  * Create a new nav progress bar
30120  * @param {Object} config The config object
30121  */
30122
30123 Roo.bootstrap.NavProgressBar = function(config){
30124     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30125
30126     this.bullets = this.bullets || [];
30127    
30128 //    Roo.bootstrap.NavProgressBar.register(this);
30129      this.addEvents({
30130         /**
30131              * @event changed
30132              * Fires when the active item changes
30133              * @param {Roo.bootstrap.NavProgressBar} this
30134              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30135              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30136          */
30137         'changed': true
30138      });
30139     
30140 };
30141
30142 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30143     
30144     bullets : [],
30145     barItems : [],
30146     
30147     getAutoCreate : function()
30148     {
30149         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30150         
30151         cfg = {
30152             tag : 'div',
30153             cls : 'roo-navigation-bar-group',
30154             cn : [
30155                 {
30156                     tag : 'div',
30157                     cls : 'roo-navigation-top-bar'
30158                 },
30159                 {
30160                     tag : 'div',
30161                     cls : 'roo-navigation-bullets-bar',
30162                     cn : [
30163                         {
30164                             tag : 'ul',
30165                             cls : 'roo-navigation-bar'
30166                         }
30167                     ]
30168                 },
30169                 
30170                 {
30171                     tag : 'div',
30172                     cls : 'roo-navigation-bottom-bar'
30173                 }
30174             ]
30175             
30176         };
30177         
30178         return cfg;
30179         
30180     },
30181     
30182     initEvents: function() 
30183     {
30184         
30185     },
30186     
30187     onRender : function(ct, position) 
30188     {
30189         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30190         
30191         if(this.bullets.length){
30192             Roo.each(this.bullets, function(b){
30193                this.addItem(b);
30194             }, this);
30195         }
30196         
30197         this.format();
30198         
30199     },
30200     
30201     addItem : function(cfg)
30202     {
30203         var item = new Roo.bootstrap.NavProgressItem(cfg);
30204         
30205         item.parentId = this.id;
30206         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30207         
30208         if(cfg.html){
30209             var top = new Roo.bootstrap.Element({
30210                 tag : 'div',
30211                 cls : 'roo-navigation-bar-text'
30212             });
30213             
30214             var bottom = new Roo.bootstrap.Element({
30215                 tag : 'div',
30216                 cls : 'roo-navigation-bar-text'
30217             });
30218             
30219             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30220             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30221             
30222             var topText = new Roo.bootstrap.Element({
30223                 tag : 'span',
30224                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30225             });
30226             
30227             var bottomText = new Roo.bootstrap.Element({
30228                 tag : 'span',
30229                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30230             });
30231             
30232             topText.onRender(top.el, null);
30233             bottomText.onRender(bottom.el, null);
30234             
30235             item.topEl = top;
30236             item.bottomEl = bottom;
30237         }
30238         
30239         this.barItems.push(item);
30240         
30241         return item;
30242     },
30243     
30244     getActive : function()
30245     {
30246         var active = false;
30247         
30248         Roo.each(this.barItems, function(v){
30249             
30250             if (!v.isActive()) {
30251                 return;
30252             }
30253             
30254             active = v;
30255             return false;
30256             
30257         });
30258         
30259         return active;
30260     },
30261     
30262     setActiveItem : function(item)
30263     {
30264         var prev = false;
30265         
30266         Roo.each(this.barItems, function(v){
30267             if (v.rid == item.rid) {
30268                 return ;
30269             }
30270             
30271             if (v.isActive()) {
30272                 v.setActive(false);
30273                 prev = v;
30274             }
30275         });
30276
30277         item.setActive(true);
30278         
30279         this.fireEvent('changed', this, item, prev);
30280     },
30281     
30282     getBarItem: function(rid)
30283     {
30284         var ret = false;
30285         
30286         Roo.each(this.barItems, function(e) {
30287             if (e.rid != rid) {
30288                 return;
30289             }
30290             
30291             ret =  e;
30292             return false;
30293         });
30294         
30295         return ret;
30296     },
30297     
30298     indexOfItem : function(item)
30299     {
30300         var index = false;
30301         
30302         Roo.each(this.barItems, function(v, i){
30303             
30304             if (v.rid != item.rid) {
30305                 return;
30306             }
30307             
30308             index = i;
30309             return false
30310         });
30311         
30312         return index;
30313     },
30314     
30315     setActiveNext : function()
30316     {
30317         var i = this.indexOfItem(this.getActive());
30318         
30319         if (i > this.barItems.length) {
30320             return;
30321         }
30322         
30323         this.setActiveItem(this.barItems[i+1]);
30324     },
30325     
30326     setActivePrev : function()
30327     {
30328         var i = this.indexOfItem(this.getActive());
30329         
30330         if (i  < 1) {
30331             return;
30332         }
30333         
30334         this.setActiveItem(this.barItems[i-1]);
30335     },
30336     
30337     format : function()
30338     {
30339         if(!this.barItems.length){
30340             return;
30341         }
30342      
30343         var width = 100 / this.barItems.length;
30344         
30345         Roo.each(this.barItems, function(i){
30346             i.el.setStyle('width', width + '%');
30347             i.topEl.el.setStyle('width', width + '%');
30348             i.bottomEl.el.setStyle('width', width + '%');
30349         }, this);
30350         
30351     }
30352     
30353 });
30354 /*
30355  * - LGPL
30356  *
30357  * Nav Progress Item
30358  * 
30359  */
30360
30361 /**
30362  * @class Roo.bootstrap.NavProgressItem
30363  * @extends Roo.bootstrap.Component
30364  * Bootstrap NavProgressItem class
30365  * @cfg {String} rid the reference id
30366  * @cfg {Boolean} active (true|false) Is item active default false
30367  * @cfg {Boolean} disabled (true|false) Is item active default false
30368  * @cfg {String} html
30369  * @cfg {String} position (top|bottom) text position default bottom
30370  * @cfg {String} icon show icon instead of number
30371  * 
30372  * @constructor
30373  * Create a new NavProgressItem
30374  * @param {Object} config The config object
30375  */
30376 Roo.bootstrap.NavProgressItem = function(config){
30377     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30378     this.addEvents({
30379         // raw events
30380         /**
30381          * @event click
30382          * The raw click event for the entire grid.
30383          * @param {Roo.bootstrap.NavProgressItem} this
30384          * @param {Roo.EventObject} e
30385          */
30386         "click" : true
30387     });
30388    
30389 };
30390
30391 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30392     
30393     rid : '',
30394     active : false,
30395     disabled : false,
30396     html : '',
30397     position : 'bottom',
30398     icon : false,
30399     
30400     getAutoCreate : function()
30401     {
30402         var iconCls = 'roo-navigation-bar-item-icon';
30403         
30404         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30405         
30406         var cfg = {
30407             tag: 'li',
30408             cls: 'roo-navigation-bar-item',
30409             cn : [
30410                 {
30411                     tag : 'i',
30412                     cls : iconCls
30413                 }
30414             ]
30415         };
30416         
30417         if(this.active){
30418             cfg.cls += ' active';
30419         }
30420         if(this.disabled){
30421             cfg.cls += ' disabled';
30422         }
30423         
30424         return cfg;
30425     },
30426     
30427     disable : function()
30428     {
30429         this.setDisabled(true);
30430     },
30431     
30432     enable : function()
30433     {
30434         this.setDisabled(false);
30435     },
30436     
30437     initEvents: function() 
30438     {
30439         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30440         
30441         this.iconEl.on('click', this.onClick, this);
30442     },
30443     
30444     onClick : function(e)
30445     {
30446         e.preventDefault();
30447         
30448         if(this.disabled){
30449             return;
30450         }
30451         
30452         if(this.fireEvent('click', this, e) === false){
30453             return;
30454         };
30455         
30456         this.parent().setActiveItem(this);
30457     },
30458     
30459     isActive: function () 
30460     {
30461         return this.active;
30462     },
30463     
30464     setActive : function(state)
30465     {
30466         if(this.active == state){
30467             return;
30468         }
30469         
30470         this.active = state;
30471         
30472         if (state) {
30473             this.el.addClass('active');
30474             return;
30475         }
30476         
30477         this.el.removeClass('active');
30478         
30479         return;
30480     },
30481     
30482     setDisabled : function(state)
30483     {
30484         if(this.disabled == state){
30485             return;
30486         }
30487         
30488         this.disabled = state;
30489         
30490         if (state) {
30491             this.el.addClass('disabled');
30492             return;
30493         }
30494         
30495         this.el.removeClass('disabled');
30496     },
30497     
30498     tooltipEl : function()
30499     {
30500         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30501     }
30502 });
30503  
30504
30505  /*
30506  * - LGPL
30507  *
30508  * FieldLabel
30509  * 
30510  */
30511
30512 /**
30513  * @class Roo.bootstrap.FieldLabel
30514  * @extends Roo.bootstrap.Component
30515  * Bootstrap FieldLabel class
30516  * @cfg {String} html contents of the element
30517  * @cfg {String} tag tag of the element default label
30518  * @cfg {String} cls class of the element
30519  * @cfg {String} target label target 
30520  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30521  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30522  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30523  * @cfg {String} iconTooltip default "This field is required"
30524  * @cfg {String} indicatorpos (left|right) default left
30525  * 
30526  * @constructor
30527  * Create a new FieldLabel
30528  * @param {Object} config The config object
30529  */
30530
30531 Roo.bootstrap.FieldLabel = function(config){
30532     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30533     
30534     this.addEvents({
30535             /**
30536              * @event invalid
30537              * Fires after the field has been marked as invalid.
30538              * @param {Roo.form.FieldLabel} this
30539              * @param {String} msg The validation message
30540              */
30541             invalid : true,
30542             /**
30543              * @event valid
30544              * Fires after the field has been validated with no errors.
30545              * @param {Roo.form.FieldLabel} this
30546              */
30547             valid : true
30548         });
30549 };
30550
30551 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30552     
30553     tag: 'label',
30554     cls: '',
30555     html: '',
30556     target: '',
30557     allowBlank : true,
30558     invalidClass : 'has-warning',
30559     validClass : 'has-success',
30560     iconTooltip : 'This field is required',
30561     indicatorpos : 'left',
30562     
30563     getAutoCreate : function(){
30564         
30565         var cls = "";
30566         if (!this.allowBlank) {
30567             cls  = "visible";
30568         }
30569         
30570         var cfg = {
30571             tag : this.tag,
30572             cls : 'roo-bootstrap-field-label ' + this.cls,
30573             for : this.target,
30574             cn : [
30575                 {
30576                     tag : 'i',
30577                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30578                     tooltip : this.iconTooltip
30579                 },
30580                 {
30581                     tag : 'span',
30582                     html : this.html
30583                 }
30584             ] 
30585         };
30586         
30587         if(this.indicatorpos == 'right'){
30588             var cfg = {
30589                 tag : this.tag,
30590                 cls : 'roo-bootstrap-field-label ' + this.cls,
30591                 for : this.target,
30592                 cn : [
30593                     {
30594                         tag : 'span',
30595                         html : this.html
30596                     },
30597                     {
30598                         tag : 'i',
30599                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30600                         tooltip : this.iconTooltip
30601                     }
30602                 ] 
30603             };
30604         }
30605         
30606         return cfg;
30607     },
30608     
30609     initEvents: function() 
30610     {
30611         Roo.bootstrap.Element.superclass.initEvents.call(this);
30612         
30613         this.indicator = this.indicatorEl();
30614         
30615         if(this.indicator){
30616             this.indicator.removeClass('visible');
30617             this.indicator.addClass('invisible');
30618         }
30619         
30620         Roo.bootstrap.FieldLabel.register(this);
30621     },
30622     
30623     indicatorEl : function()
30624     {
30625         var indicator = this.el.select('i.roo-required-indicator',true).first();
30626         
30627         if(!indicator){
30628             return false;
30629         }
30630         
30631         return indicator;
30632         
30633     },
30634     
30635     /**
30636      * Mark this field as valid
30637      */
30638     markValid : function()
30639     {
30640         if(this.indicator){
30641             this.indicator.removeClass('visible');
30642             this.indicator.addClass('invisible');
30643         }
30644         if (Roo.bootstrap.version == 3) {
30645             this.el.removeClass(this.invalidClass);
30646             this.el.addClass(this.validClass);
30647         } else {
30648             this.el.removeClass('is-invalid');
30649             this.el.addClass('is-valid');
30650         }
30651         
30652         
30653         this.fireEvent('valid', this);
30654     },
30655     
30656     /**
30657      * Mark this field as invalid
30658      * @param {String} msg The validation message
30659      */
30660     markInvalid : function(msg)
30661     {
30662         if(this.indicator){
30663             this.indicator.removeClass('invisible');
30664             this.indicator.addClass('visible');
30665         }
30666           if (Roo.bootstrap.version == 3) {
30667             this.el.removeClass(this.validClass);
30668             this.el.addClass(this.invalidClass);
30669         } else {
30670             this.el.removeClass('is-valid');
30671             this.el.addClass('is-invalid');
30672         }
30673         
30674         
30675         this.fireEvent('invalid', this, msg);
30676     }
30677     
30678    
30679 });
30680
30681 Roo.apply(Roo.bootstrap.FieldLabel, {
30682     
30683     groups: {},
30684     
30685      /**
30686     * register a FieldLabel Group
30687     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30688     */
30689     register : function(label)
30690     {
30691         if(this.groups.hasOwnProperty(label.target)){
30692             return;
30693         }
30694      
30695         this.groups[label.target] = label;
30696         
30697     },
30698     /**
30699     * fetch a FieldLabel Group based on the target
30700     * @param {string} target
30701     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30702     */
30703     get: function(target) {
30704         if (typeof(this.groups[target]) == 'undefined') {
30705             return false;
30706         }
30707         
30708         return this.groups[target] ;
30709     }
30710 });
30711
30712  
30713
30714  /*
30715  * - LGPL
30716  *
30717  * page DateSplitField.
30718  * 
30719  */
30720
30721
30722 /**
30723  * @class Roo.bootstrap.DateSplitField
30724  * @extends Roo.bootstrap.Component
30725  * Bootstrap DateSplitField class
30726  * @cfg {string} fieldLabel - the label associated
30727  * @cfg {Number} labelWidth set the width of label (0-12)
30728  * @cfg {String} labelAlign (top|left)
30729  * @cfg {Boolean} dayAllowBlank (true|false) default false
30730  * @cfg {Boolean} monthAllowBlank (true|false) default false
30731  * @cfg {Boolean} yearAllowBlank (true|false) default false
30732  * @cfg {string} dayPlaceholder 
30733  * @cfg {string} monthPlaceholder
30734  * @cfg {string} yearPlaceholder
30735  * @cfg {string} dayFormat default 'd'
30736  * @cfg {string} monthFormat default 'm'
30737  * @cfg {string} yearFormat default 'Y'
30738  * @cfg {Number} labellg set the width of label (1-12)
30739  * @cfg {Number} labelmd set the width of label (1-12)
30740  * @cfg {Number} labelsm set the width of label (1-12)
30741  * @cfg {Number} labelxs set the width of label (1-12)
30742
30743  *     
30744  * @constructor
30745  * Create a new DateSplitField
30746  * @param {Object} config The config object
30747  */
30748
30749 Roo.bootstrap.DateSplitField = function(config){
30750     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30751     
30752     this.addEvents({
30753         // raw events
30754          /**
30755          * @event years
30756          * getting the data of years
30757          * @param {Roo.bootstrap.DateSplitField} this
30758          * @param {Object} years
30759          */
30760         "years" : true,
30761         /**
30762          * @event days
30763          * getting the data of days
30764          * @param {Roo.bootstrap.DateSplitField} this
30765          * @param {Object} days
30766          */
30767         "days" : true,
30768         /**
30769          * @event invalid
30770          * Fires after the field has been marked as invalid.
30771          * @param {Roo.form.Field} this
30772          * @param {String} msg The validation message
30773          */
30774         invalid : true,
30775        /**
30776          * @event valid
30777          * Fires after the field has been validated with no errors.
30778          * @param {Roo.form.Field} this
30779          */
30780         valid : true
30781     });
30782 };
30783
30784 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30785     
30786     fieldLabel : '',
30787     labelAlign : 'top',
30788     labelWidth : 3,
30789     dayAllowBlank : false,
30790     monthAllowBlank : false,
30791     yearAllowBlank : false,
30792     dayPlaceholder : '',
30793     monthPlaceholder : '',
30794     yearPlaceholder : '',
30795     dayFormat : 'd',
30796     monthFormat : 'm',
30797     yearFormat : 'Y',
30798     isFormField : true,
30799     labellg : 0,
30800     labelmd : 0,
30801     labelsm : 0,
30802     labelxs : 0,
30803     
30804     getAutoCreate : function()
30805     {
30806         var cfg = {
30807             tag : 'div',
30808             cls : 'row roo-date-split-field-group',
30809             cn : [
30810                 {
30811                     tag : 'input',
30812                     type : 'hidden',
30813                     cls : 'form-hidden-field roo-date-split-field-group-value',
30814                     name : this.name
30815                 }
30816             ]
30817         };
30818         
30819         var labelCls = 'col-md-12';
30820         var contentCls = 'col-md-4';
30821         
30822         if(this.fieldLabel){
30823             
30824             var label = {
30825                 tag : 'div',
30826                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30827                 cn : [
30828                     {
30829                         tag : 'label',
30830                         html : this.fieldLabel
30831                     }
30832                 ]
30833             };
30834             
30835             if(this.labelAlign == 'left'){
30836             
30837                 if(this.labelWidth > 12){
30838                     label.style = "width: " + this.labelWidth + 'px';
30839                 }
30840
30841                 if(this.labelWidth < 13 && this.labelmd == 0){
30842                     this.labelmd = this.labelWidth;
30843                 }
30844
30845                 if(this.labellg > 0){
30846                     labelCls = ' col-lg-' + this.labellg;
30847                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30848                 }
30849
30850                 if(this.labelmd > 0){
30851                     labelCls = ' col-md-' + this.labelmd;
30852                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30853                 }
30854
30855                 if(this.labelsm > 0){
30856                     labelCls = ' col-sm-' + this.labelsm;
30857                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30858                 }
30859
30860                 if(this.labelxs > 0){
30861                     labelCls = ' col-xs-' + this.labelxs;
30862                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30863                 }
30864             }
30865             
30866             label.cls += ' ' + labelCls;
30867             
30868             cfg.cn.push(label);
30869         }
30870         
30871         Roo.each(['day', 'month', 'year'], function(t){
30872             cfg.cn.push({
30873                 tag : 'div',
30874                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30875             });
30876         }, this);
30877         
30878         return cfg;
30879     },
30880     
30881     inputEl: function ()
30882     {
30883         return this.el.select('.roo-date-split-field-group-value', true).first();
30884     },
30885     
30886     onRender : function(ct, position) 
30887     {
30888         var _this = this;
30889         
30890         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30891         
30892         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30893         
30894         this.dayField = new Roo.bootstrap.ComboBox({
30895             allowBlank : this.dayAllowBlank,
30896             alwaysQuery : true,
30897             displayField : 'value',
30898             editable : false,
30899             fieldLabel : '',
30900             forceSelection : true,
30901             mode : 'local',
30902             placeholder : this.dayPlaceholder,
30903             selectOnFocus : true,
30904             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30905             triggerAction : 'all',
30906             typeAhead : true,
30907             valueField : 'value',
30908             store : new Roo.data.SimpleStore({
30909                 data : (function() {    
30910                     var days = [];
30911                     _this.fireEvent('days', _this, days);
30912                     return days;
30913                 })(),
30914                 fields : [ 'value' ]
30915             }),
30916             listeners : {
30917                 select : function (_self, record, index)
30918                 {
30919                     _this.setValue(_this.getValue());
30920                 }
30921             }
30922         });
30923
30924         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30925         
30926         this.monthField = new Roo.bootstrap.MonthField({
30927             after : '<i class=\"fa fa-calendar\"></i>',
30928             allowBlank : this.monthAllowBlank,
30929             placeholder : this.monthPlaceholder,
30930             readOnly : true,
30931             listeners : {
30932                 render : function (_self)
30933                 {
30934                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30935                         e.preventDefault();
30936                         _self.focus();
30937                     });
30938                 },
30939                 select : function (_self, oldvalue, newvalue)
30940                 {
30941                     _this.setValue(_this.getValue());
30942                 }
30943             }
30944         });
30945         
30946         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30947         
30948         this.yearField = new Roo.bootstrap.ComboBox({
30949             allowBlank : this.yearAllowBlank,
30950             alwaysQuery : true,
30951             displayField : 'value',
30952             editable : false,
30953             fieldLabel : '',
30954             forceSelection : true,
30955             mode : 'local',
30956             placeholder : this.yearPlaceholder,
30957             selectOnFocus : true,
30958             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30959             triggerAction : 'all',
30960             typeAhead : true,
30961             valueField : 'value',
30962             store : new Roo.data.SimpleStore({
30963                 data : (function() {
30964                     var years = [];
30965                     _this.fireEvent('years', _this, years);
30966                     return years;
30967                 })(),
30968                 fields : [ 'value' ]
30969             }),
30970             listeners : {
30971                 select : function (_self, record, index)
30972                 {
30973                     _this.setValue(_this.getValue());
30974                 }
30975             }
30976         });
30977
30978         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30979     },
30980     
30981     setValue : function(v, format)
30982     {
30983         this.inputEl.dom.value = v;
30984         
30985         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30986         
30987         var d = Date.parseDate(v, f);
30988         
30989         if(!d){
30990             this.validate();
30991             return;
30992         }
30993         
30994         this.setDay(d.format(this.dayFormat));
30995         this.setMonth(d.format(this.monthFormat));
30996         this.setYear(d.format(this.yearFormat));
30997         
30998         this.validate();
30999         
31000         return;
31001     },
31002     
31003     setDay : function(v)
31004     {
31005         this.dayField.setValue(v);
31006         this.inputEl.dom.value = this.getValue();
31007         this.validate();
31008         return;
31009     },
31010     
31011     setMonth : function(v)
31012     {
31013         this.monthField.setValue(v, true);
31014         this.inputEl.dom.value = this.getValue();
31015         this.validate();
31016         return;
31017     },
31018     
31019     setYear : function(v)
31020     {
31021         this.yearField.setValue(v);
31022         this.inputEl.dom.value = this.getValue();
31023         this.validate();
31024         return;
31025     },
31026     
31027     getDay : function()
31028     {
31029         return this.dayField.getValue();
31030     },
31031     
31032     getMonth : function()
31033     {
31034         return this.monthField.getValue();
31035     },
31036     
31037     getYear : function()
31038     {
31039         return this.yearField.getValue();
31040     },
31041     
31042     getValue : function()
31043     {
31044         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31045         
31046         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31047         
31048         return date;
31049     },
31050     
31051     reset : function()
31052     {
31053         this.setDay('');
31054         this.setMonth('');
31055         this.setYear('');
31056         this.inputEl.dom.value = '';
31057         this.validate();
31058         return;
31059     },
31060     
31061     validate : function()
31062     {
31063         var d = this.dayField.validate();
31064         var m = this.monthField.validate();
31065         var y = this.yearField.validate();
31066         
31067         var valid = true;
31068         
31069         if(
31070                 (!this.dayAllowBlank && !d) ||
31071                 (!this.monthAllowBlank && !m) ||
31072                 (!this.yearAllowBlank && !y)
31073         ){
31074             valid = false;
31075         }
31076         
31077         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31078             return valid;
31079         }
31080         
31081         if(valid){
31082             this.markValid();
31083             return valid;
31084         }
31085         
31086         this.markInvalid();
31087         
31088         return valid;
31089     },
31090     
31091     markValid : function()
31092     {
31093         
31094         var label = this.el.select('label', true).first();
31095         var icon = this.el.select('i.fa-star', true).first();
31096
31097         if(label && icon){
31098             icon.remove();
31099         }
31100         
31101         this.fireEvent('valid', this);
31102     },
31103     
31104      /**
31105      * Mark this field as invalid
31106      * @param {String} msg The validation message
31107      */
31108     markInvalid : function(msg)
31109     {
31110         
31111         var label = this.el.select('label', true).first();
31112         var icon = this.el.select('i.fa-star', true).first();
31113
31114         if(label && !icon){
31115             this.el.select('.roo-date-split-field-label', true).createChild({
31116                 tag : 'i',
31117                 cls : 'text-danger fa fa-lg fa-star',
31118                 tooltip : 'This field is required',
31119                 style : 'margin-right:5px;'
31120             }, label, true);
31121         }
31122         
31123         this.fireEvent('invalid', this, msg);
31124     },
31125     
31126     clearInvalid : function()
31127     {
31128         var label = this.el.select('label', true).first();
31129         var icon = this.el.select('i.fa-star', true).first();
31130
31131         if(label && icon){
31132             icon.remove();
31133         }
31134         
31135         this.fireEvent('valid', this);
31136     },
31137     
31138     getName: function()
31139     {
31140         return this.name;
31141     }
31142     
31143 });
31144
31145  /**
31146  *
31147  * This is based on 
31148  * http://masonry.desandro.com
31149  *
31150  * The idea is to render all the bricks based on vertical width...
31151  *
31152  * The original code extends 'outlayer' - we might need to use that....
31153  * 
31154  */
31155
31156
31157 /**
31158  * @class Roo.bootstrap.LayoutMasonry
31159  * @extends Roo.bootstrap.Component
31160  * Bootstrap Layout Masonry class
31161  * 
31162  * @constructor
31163  * Create a new Element
31164  * @param {Object} config The config object
31165  */
31166
31167 Roo.bootstrap.LayoutMasonry = function(config){
31168     
31169     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31170     
31171     this.bricks = [];
31172     
31173     Roo.bootstrap.LayoutMasonry.register(this);
31174     
31175     this.addEvents({
31176         // raw events
31177         /**
31178          * @event layout
31179          * Fire after layout the items
31180          * @param {Roo.bootstrap.LayoutMasonry} this
31181          * @param {Roo.EventObject} e
31182          */
31183         "layout" : true
31184     });
31185     
31186 };
31187
31188 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31189     
31190     /**
31191      * @cfg {Boolean} isLayoutInstant = no animation?
31192      */   
31193     isLayoutInstant : false, // needed?
31194    
31195     /**
31196      * @cfg {Number} boxWidth  width of the columns
31197      */   
31198     boxWidth : 450,
31199     
31200       /**
31201      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31202      */   
31203     boxHeight : 0,
31204     
31205     /**
31206      * @cfg {Number} padWidth padding below box..
31207      */   
31208     padWidth : 10, 
31209     
31210     /**
31211      * @cfg {Number} gutter gutter width..
31212      */   
31213     gutter : 10,
31214     
31215      /**
31216      * @cfg {Number} maxCols maximum number of columns
31217      */   
31218     
31219     maxCols: 0,
31220     
31221     /**
31222      * @cfg {Boolean} isAutoInitial defalut true
31223      */   
31224     isAutoInitial : true, 
31225     
31226     containerWidth: 0,
31227     
31228     /**
31229      * @cfg {Boolean} isHorizontal defalut false
31230      */   
31231     isHorizontal : false, 
31232
31233     currentSize : null,
31234     
31235     tag: 'div',
31236     
31237     cls: '',
31238     
31239     bricks: null, //CompositeElement
31240     
31241     cols : 1,
31242     
31243     _isLayoutInited : false,
31244     
31245 //    isAlternative : false, // only use for vertical layout...
31246     
31247     /**
31248      * @cfg {Number} alternativePadWidth padding below box..
31249      */   
31250     alternativePadWidth : 50,
31251     
31252     selectedBrick : [],
31253     
31254     getAutoCreate : function(){
31255         
31256         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31257         
31258         var cfg = {
31259             tag: this.tag,
31260             cls: 'blog-masonary-wrapper ' + this.cls,
31261             cn : {
31262                 cls : 'mas-boxes masonary'
31263             }
31264         };
31265         
31266         return cfg;
31267     },
31268     
31269     getChildContainer: function( )
31270     {
31271         if (this.boxesEl) {
31272             return this.boxesEl;
31273         }
31274         
31275         this.boxesEl = this.el.select('.mas-boxes').first();
31276         
31277         return this.boxesEl;
31278     },
31279     
31280     
31281     initEvents : function()
31282     {
31283         var _this = this;
31284         
31285         if(this.isAutoInitial){
31286             Roo.log('hook children rendered');
31287             this.on('childrenrendered', function() {
31288                 Roo.log('children rendered');
31289                 _this.initial();
31290             } ,this);
31291         }
31292     },
31293     
31294     initial : function()
31295     {
31296         this.selectedBrick = [];
31297         
31298         this.currentSize = this.el.getBox(true);
31299         
31300         Roo.EventManager.onWindowResize(this.resize, this); 
31301
31302         if(!this.isAutoInitial){
31303             this.layout();
31304             return;
31305         }
31306         
31307         this.layout();
31308         
31309         return;
31310         //this.layout.defer(500,this);
31311         
31312     },
31313     
31314     resize : function()
31315     {
31316         var cs = this.el.getBox(true);
31317         
31318         if (
31319                 this.currentSize.width == cs.width && 
31320                 this.currentSize.x == cs.x && 
31321                 this.currentSize.height == cs.height && 
31322                 this.currentSize.y == cs.y 
31323         ) {
31324             Roo.log("no change in with or X or Y");
31325             return;
31326         }
31327         
31328         this.currentSize = cs;
31329         
31330         this.layout();
31331         
31332     },
31333     
31334     layout : function()
31335     {   
31336         this._resetLayout();
31337         
31338         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31339         
31340         this.layoutItems( isInstant );
31341       
31342         this._isLayoutInited = true;
31343         
31344         this.fireEvent('layout', this);
31345         
31346     },
31347     
31348     _resetLayout : function()
31349     {
31350         if(this.isHorizontal){
31351             this.horizontalMeasureColumns();
31352             return;
31353         }
31354         
31355         this.verticalMeasureColumns();
31356         
31357     },
31358     
31359     verticalMeasureColumns : function()
31360     {
31361         this.getContainerWidth();
31362         
31363 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31364 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31365 //            return;
31366 //        }
31367         
31368         var boxWidth = this.boxWidth + this.padWidth;
31369         
31370         if(this.containerWidth < this.boxWidth){
31371             boxWidth = this.containerWidth
31372         }
31373         
31374         var containerWidth = this.containerWidth;
31375         
31376         var cols = Math.floor(containerWidth / boxWidth);
31377         
31378         this.cols = Math.max( cols, 1 );
31379         
31380         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31381         
31382         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31383         
31384         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31385         
31386         this.colWidth = boxWidth + avail - this.padWidth;
31387         
31388         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31389         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31390     },
31391     
31392     horizontalMeasureColumns : function()
31393     {
31394         this.getContainerWidth();
31395         
31396         var boxWidth = this.boxWidth;
31397         
31398         if(this.containerWidth < boxWidth){
31399             boxWidth = this.containerWidth;
31400         }
31401         
31402         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31403         
31404         this.el.setHeight(boxWidth);
31405         
31406     },
31407     
31408     getContainerWidth : function()
31409     {
31410         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31411     },
31412     
31413     layoutItems : function( isInstant )
31414     {
31415         Roo.log(this.bricks);
31416         
31417         var items = Roo.apply([], this.bricks);
31418         
31419         if(this.isHorizontal){
31420             this._horizontalLayoutItems( items , isInstant );
31421             return;
31422         }
31423         
31424 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31425 //            this._verticalAlternativeLayoutItems( items , isInstant );
31426 //            return;
31427 //        }
31428         
31429         this._verticalLayoutItems( items , isInstant );
31430         
31431     },
31432     
31433     _verticalLayoutItems : function ( items , isInstant)
31434     {
31435         if ( !items || !items.length ) {
31436             return;
31437         }
31438         
31439         var standard = [
31440             ['xs', 'xs', 'xs', 'tall'],
31441             ['xs', 'xs', 'tall'],
31442             ['xs', 'xs', 'sm'],
31443             ['xs', 'xs', 'xs'],
31444             ['xs', 'tall'],
31445             ['xs', 'sm'],
31446             ['xs', 'xs'],
31447             ['xs'],
31448             
31449             ['sm', 'xs', 'xs'],
31450             ['sm', 'xs'],
31451             ['sm'],
31452             
31453             ['tall', 'xs', 'xs', 'xs'],
31454             ['tall', 'xs', 'xs'],
31455             ['tall', 'xs'],
31456             ['tall']
31457             
31458         ];
31459         
31460         var queue = [];
31461         
31462         var boxes = [];
31463         
31464         var box = [];
31465         
31466         Roo.each(items, function(item, k){
31467             
31468             switch (item.size) {
31469                 // these layouts take up a full box,
31470                 case 'md' :
31471                 case 'md-left' :
31472                 case 'md-right' :
31473                 case 'wide' :
31474                     
31475                     if(box.length){
31476                         boxes.push(box);
31477                         box = [];
31478                     }
31479                     
31480                     boxes.push([item]);
31481                     
31482                     break;
31483                     
31484                 case 'xs' :
31485                 case 'sm' :
31486                 case 'tall' :
31487                     
31488                     box.push(item);
31489                     
31490                     break;
31491                 default :
31492                     break;
31493                     
31494             }
31495             
31496         }, this);
31497         
31498         if(box.length){
31499             boxes.push(box);
31500             box = [];
31501         }
31502         
31503         var filterPattern = function(box, length)
31504         {
31505             if(!box.length){
31506                 return;
31507             }
31508             
31509             var match = false;
31510             
31511             var pattern = box.slice(0, length);
31512             
31513             var format = [];
31514             
31515             Roo.each(pattern, function(i){
31516                 format.push(i.size);
31517             }, this);
31518             
31519             Roo.each(standard, function(s){
31520                 
31521                 if(String(s) != String(format)){
31522                     return;
31523                 }
31524                 
31525                 match = true;
31526                 return false;
31527                 
31528             }, this);
31529             
31530             if(!match && length == 1){
31531                 return;
31532             }
31533             
31534             if(!match){
31535                 filterPattern(box, length - 1);
31536                 return;
31537             }
31538                 
31539             queue.push(pattern);
31540
31541             box = box.slice(length, box.length);
31542
31543             filterPattern(box, 4);
31544
31545             return;
31546             
31547         }
31548         
31549         Roo.each(boxes, function(box, k){
31550             
31551             if(!box.length){
31552                 return;
31553             }
31554             
31555             if(box.length == 1){
31556                 queue.push(box);
31557                 return;
31558             }
31559             
31560             filterPattern(box, 4);
31561             
31562         }, this);
31563         
31564         this._processVerticalLayoutQueue( queue, isInstant );
31565         
31566     },
31567     
31568 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31569 //    {
31570 //        if ( !items || !items.length ) {
31571 //            return;
31572 //        }
31573 //
31574 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31575 //        
31576 //    },
31577     
31578     _horizontalLayoutItems : function ( items , isInstant)
31579     {
31580         if ( !items || !items.length || items.length < 3) {
31581             return;
31582         }
31583         
31584         items.reverse();
31585         
31586         var eItems = items.slice(0, 3);
31587         
31588         items = items.slice(3, items.length);
31589         
31590         var standard = [
31591             ['xs', 'xs', 'xs', 'wide'],
31592             ['xs', 'xs', 'wide'],
31593             ['xs', 'xs', 'sm'],
31594             ['xs', 'xs', 'xs'],
31595             ['xs', 'wide'],
31596             ['xs', 'sm'],
31597             ['xs', 'xs'],
31598             ['xs'],
31599             
31600             ['sm', 'xs', 'xs'],
31601             ['sm', 'xs'],
31602             ['sm'],
31603             
31604             ['wide', 'xs', 'xs', 'xs'],
31605             ['wide', 'xs', 'xs'],
31606             ['wide', 'xs'],
31607             ['wide'],
31608             
31609             ['wide-thin']
31610         ];
31611         
31612         var queue = [];
31613         
31614         var boxes = [];
31615         
31616         var box = [];
31617         
31618         Roo.each(items, function(item, k){
31619             
31620             switch (item.size) {
31621                 case 'md' :
31622                 case 'md-left' :
31623                 case 'md-right' :
31624                 case 'tall' :
31625                     
31626                     if(box.length){
31627                         boxes.push(box);
31628                         box = [];
31629                     }
31630                     
31631                     boxes.push([item]);
31632                     
31633                     break;
31634                     
31635                 case 'xs' :
31636                 case 'sm' :
31637                 case 'wide' :
31638                 case 'wide-thin' :
31639                     
31640                     box.push(item);
31641                     
31642                     break;
31643                 default :
31644                     break;
31645                     
31646             }
31647             
31648         }, this);
31649         
31650         if(box.length){
31651             boxes.push(box);
31652             box = [];
31653         }
31654         
31655         var filterPattern = function(box, length)
31656         {
31657             if(!box.length){
31658                 return;
31659             }
31660             
31661             var match = false;
31662             
31663             var pattern = box.slice(0, length);
31664             
31665             var format = [];
31666             
31667             Roo.each(pattern, function(i){
31668                 format.push(i.size);
31669             }, this);
31670             
31671             Roo.each(standard, function(s){
31672                 
31673                 if(String(s) != String(format)){
31674                     return;
31675                 }
31676                 
31677                 match = true;
31678                 return false;
31679                 
31680             }, this);
31681             
31682             if(!match && length == 1){
31683                 return;
31684             }
31685             
31686             if(!match){
31687                 filterPattern(box, length - 1);
31688                 return;
31689             }
31690                 
31691             queue.push(pattern);
31692
31693             box = box.slice(length, box.length);
31694
31695             filterPattern(box, 4);
31696
31697             return;
31698             
31699         }
31700         
31701         Roo.each(boxes, function(box, k){
31702             
31703             if(!box.length){
31704                 return;
31705             }
31706             
31707             if(box.length == 1){
31708                 queue.push(box);
31709                 return;
31710             }
31711             
31712             filterPattern(box, 4);
31713             
31714         }, this);
31715         
31716         
31717         var prune = [];
31718         
31719         var pos = this.el.getBox(true);
31720         
31721         var minX = pos.x;
31722         
31723         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31724         
31725         var hit_end = false;
31726         
31727         Roo.each(queue, function(box){
31728             
31729             if(hit_end){
31730                 
31731                 Roo.each(box, function(b){
31732                 
31733                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31734                     b.el.hide();
31735
31736                 }, this);
31737
31738                 return;
31739             }
31740             
31741             var mx = 0;
31742             
31743             Roo.each(box, function(b){
31744                 
31745                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31746                 b.el.show();
31747
31748                 mx = Math.max(mx, b.x);
31749                 
31750             }, this);
31751             
31752             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31753             
31754             if(maxX < minX){
31755                 
31756                 Roo.each(box, function(b){
31757                 
31758                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31759                     b.el.hide();
31760                     
31761                 }, this);
31762                 
31763                 hit_end = true;
31764                 
31765                 return;
31766             }
31767             
31768             prune.push(box);
31769             
31770         }, this);
31771         
31772         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31773     },
31774     
31775     /** Sets position of item in DOM
31776     * @param {Element} item
31777     * @param {Number} x - horizontal position
31778     * @param {Number} y - vertical position
31779     * @param {Boolean} isInstant - disables transitions
31780     */
31781     _processVerticalLayoutQueue : function( queue, isInstant )
31782     {
31783         var pos = this.el.getBox(true);
31784         var x = pos.x;
31785         var y = pos.y;
31786         var maxY = [];
31787         
31788         for (var i = 0; i < this.cols; i++){
31789             maxY[i] = pos.y;
31790         }
31791         
31792         Roo.each(queue, function(box, k){
31793             
31794             var col = k % this.cols;
31795             
31796             Roo.each(box, function(b,kk){
31797                 
31798                 b.el.position('absolute');
31799                 
31800                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31801                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31802                 
31803                 if(b.size == 'md-left' || b.size == 'md-right'){
31804                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31805                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31806                 }
31807                 
31808                 b.el.setWidth(width);
31809                 b.el.setHeight(height);
31810                 // iframe?
31811                 b.el.select('iframe',true).setSize(width,height);
31812                 
31813             }, this);
31814             
31815             for (var i = 0; i < this.cols; i++){
31816                 
31817                 if(maxY[i] < maxY[col]){
31818                     col = i;
31819                     continue;
31820                 }
31821                 
31822                 col = Math.min(col, i);
31823                 
31824             }
31825             
31826             x = pos.x + col * (this.colWidth + this.padWidth);
31827             
31828             y = maxY[col];
31829             
31830             var positions = [];
31831             
31832             switch (box.length){
31833                 case 1 :
31834                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31835                     break;
31836                 case 2 :
31837                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31838                     break;
31839                 case 3 :
31840                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31841                     break;
31842                 case 4 :
31843                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31844                     break;
31845                 default :
31846                     break;
31847             }
31848             
31849             Roo.each(box, function(b,kk){
31850                 
31851                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31852                 
31853                 var sz = b.el.getSize();
31854                 
31855                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31856                 
31857             }, this);
31858             
31859         }, this);
31860         
31861         var mY = 0;
31862         
31863         for (var i = 0; i < this.cols; i++){
31864             mY = Math.max(mY, maxY[i]);
31865         }
31866         
31867         this.el.setHeight(mY - pos.y);
31868         
31869     },
31870     
31871 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31872 //    {
31873 //        var pos = this.el.getBox(true);
31874 //        var x = pos.x;
31875 //        var y = pos.y;
31876 //        var maxX = pos.right;
31877 //        
31878 //        var maxHeight = 0;
31879 //        
31880 //        Roo.each(items, function(item, k){
31881 //            
31882 //            var c = k % 2;
31883 //            
31884 //            item.el.position('absolute');
31885 //                
31886 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31887 //
31888 //            item.el.setWidth(width);
31889 //
31890 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31891 //
31892 //            item.el.setHeight(height);
31893 //            
31894 //            if(c == 0){
31895 //                item.el.setXY([x, y], isInstant ? false : true);
31896 //            } else {
31897 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31898 //            }
31899 //            
31900 //            y = y + height + this.alternativePadWidth;
31901 //            
31902 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31903 //            
31904 //        }, this);
31905 //        
31906 //        this.el.setHeight(maxHeight);
31907 //        
31908 //    },
31909     
31910     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31911     {
31912         var pos = this.el.getBox(true);
31913         
31914         var minX = pos.x;
31915         var minY = pos.y;
31916         
31917         var maxX = pos.right;
31918         
31919         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31920         
31921         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31922         
31923         Roo.each(queue, function(box, k){
31924             
31925             Roo.each(box, function(b, kk){
31926                 
31927                 b.el.position('absolute');
31928                 
31929                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31930                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31931                 
31932                 if(b.size == 'md-left' || b.size == 'md-right'){
31933                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31934                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31935                 }
31936                 
31937                 b.el.setWidth(width);
31938                 b.el.setHeight(height);
31939                 
31940             }, this);
31941             
31942             if(!box.length){
31943                 return;
31944             }
31945             
31946             var positions = [];
31947             
31948             switch (box.length){
31949                 case 1 :
31950                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31951                     break;
31952                 case 2 :
31953                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31954                     break;
31955                 case 3 :
31956                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31957                     break;
31958                 case 4 :
31959                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31960                     break;
31961                 default :
31962                     break;
31963             }
31964             
31965             Roo.each(box, function(b,kk){
31966                 
31967                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31968                 
31969                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31970                 
31971             }, this);
31972             
31973         }, this);
31974         
31975     },
31976     
31977     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31978     {
31979         Roo.each(eItems, function(b,k){
31980             
31981             b.size = (k == 0) ? 'sm' : 'xs';
31982             b.x = (k == 0) ? 2 : 1;
31983             b.y = (k == 0) ? 2 : 1;
31984             
31985             b.el.position('absolute');
31986             
31987             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31988                 
31989             b.el.setWidth(width);
31990             
31991             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31992             
31993             b.el.setHeight(height);
31994             
31995         }, this);
31996
31997         var positions = [];
31998         
31999         positions.push({
32000             x : maxX - this.unitWidth * 2 - this.gutter,
32001             y : minY
32002         });
32003         
32004         positions.push({
32005             x : maxX - this.unitWidth,
32006             y : minY + (this.unitWidth + this.gutter) * 2
32007         });
32008         
32009         positions.push({
32010             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32011             y : minY
32012         });
32013         
32014         Roo.each(eItems, function(b,k){
32015             
32016             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32017
32018         }, this);
32019         
32020     },
32021     
32022     getVerticalOneBoxColPositions : function(x, y, box)
32023     {
32024         var pos = [];
32025         
32026         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32027         
32028         if(box[0].size == 'md-left'){
32029             rand = 0;
32030         }
32031         
32032         if(box[0].size == 'md-right'){
32033             rand = 1;
32034         }
32035         
32036         pos.push({
32037             x : x + (this.unitWidth + this.gutter) * rand,
32038             y : y
32039         });
32040         
32041         return pos;
32042     },
32043     
32044     getVerticalTwoBoxColPositions : function(x, y, box)
32045     {
32046         var pos = [];
32047         
32048         if(box[0].size == 'xs'){
32049             
32050             pos.push({
32051                 x : x,
32052                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32053             });
32054
32055             pos.push({
32056                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32057                 y : y
32058             });
32059             
32060             return pos;
32061             
32062         }
32063         
32064         pos.push({
32065             x : x,
32066             y : y
32067         });
32068
32069         pos.push({
32070             x : x + (this.unitWidth + this.gutter) * 2,
32071             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32072         });
32073         
32074         return pos;
32075         
32076     },
32077     
32078     getVerticalThreeBoxColPositions : function(x, y, box)
32079     {
32080         var pos = [];
32081         
32082         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32083             
32084             pos.push({
32085                 x : x,
32086                 y : y
32087             });
32088
32089             pos.push({
32090                 x : x + (this.unitWidth + this.gutter) * 1,
32091                 y : y
32092             });
32093             
32094             pos.push({
32095                 x : x + (this.unitWidth + this.gutter) * 2,
32096                 y : y
32097             });
32098             
32099             return pos;
32100             
32101         }
32102         
32103         if(box[0].size == 'xs' && box[1].size == 'xs'){
32104             
32105             pos.push({
32106                 x : x,
32107                 y : y
32108             });
32109
32110             pos.push({
32111                 x : x,
32112                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32113             });
32114             
32115             pos.push({
32116                 x : x + (this.unitWidth + this.gutter) * 1,
32117                 y : y
32118             });
32119             
32120             return pos;
32121             
32122         }
32123         
32124         pos.push({
32125             x : x,
32126             y : y
32127         });
32128
32129         pos.push({
32130             x : x + (this.unitWidth + this.gutter) * 2,
32131             y : y
32132         });
32133
32134         pos.push({
32135             x : x + (this.unitWidth + this.gutter) * 2,
32136             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32137         });
32138             
32139         return pos;
32140         
32141     },
32142     
32143     getVerticalFourBoxColPositions : function(x, y, box)
32144     {
32145         var pos = [];
32146         
32147         if(box[0].size == 'xs'){
32148             
32149             pos.push({
32150                 x : x,
32151                 y : y
32152             });
32153
32154             pos.push({
32155                 x : x,
32156                 y : y + (this.unitHeight + this.gutter) * 1
32157             });
32158             
32159             pos.push({
32160                 x : x,
32161                 y : y + (this.unitHeight + this.gutter) * 2
32162             });
32163             
32164             pos.push({
32165                 x : x + (this.unitWidth + this.gutter) * 1,
32166                 y : y
32167             });
32168             
32169             return pos;
32170             
32171         }
32172         
32173         pos.push({
32174             x : x,
32175             y : y
32176         });
32177
32178         pos.push({
32179             x : x + (this.unitWidth + this.gutter) * 2,
32180             y : y
32181         });
32182
32183         pos.push({
32184             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32185             y : y + (this.unitHeight + this.gutter) * 1
32186         });
32187
32188         pos.push({
32189             x : x + (this.unitWidth + this.gutter) * 2,
32190             y : y + (this.unitWidth + this.gutter) * 2
32191         });
32192
32193         return pos;
32194         
32195     },
32196     
32197     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32198     {
32199         var pos = [];
32200         
32201         if(box[0].size == 'md-left'){
32202             pos.push({
32203                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32204                 y : minY
32205             });
32206             
32207             return pos;
32208         }
32209         
32210         if(box[0].size == 'md-right'){
32211             pos.push({
32212                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32213                 y : minY + (this.unitWidth + this.gutter) * 1
32214             });
32215             
32216             return pos;
32217         }
32218         
32219         var rand = Math.floor(Math.random() * (4 - box[0].y));
32220         
32221         pos.push({
32222             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32223             y : minY + (this.unitWidth + this.gutter) * rand
32224         });
32225         
32226         return pos;
32227         
32228     },
32229     
32230     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32231     {
32232         var pos = [];
32233         
32234         if(box[0].size == 'xs'){
32235             
32236             pos.push({
32237                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32238                 y : minY
32239             });
32240
32241             pos.push({
32242                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32243                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32244             });
32245             
32246             return pos;
32247             
32248         }
32249         
32250         pos.push({
32251             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32252             y : minY
32253         });
32254
32255         pos.push({
32256             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32257             y : minY + (this.unitWidth + this.gutter) * 2
32258         });
32259         
32260         return pos;
32261         
32262     },
32263     
32264     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32265     {
32266         var pos = [];
32267         
32268         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32269             
32270             pos.push({
32271                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32272                 y : minY
32273             });
32274
32275             pos.push({
32276                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32277                 y : minY + (this.unitWidth + this.gutter) * 1
32278             });
32279             
32280             pos.push({
32281                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32282                 y : minY + (this.unitWidth + this.gutter) * 2
32283             });
32284             
32285             return pos;
32286             
32287         }
32288         
32289         if(box[0].size == 'xs' && box[1].size == 'xs'){
32290             
32291             pos.push({
32292                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32293                 y : minY
32294             });
32295
32296             pos.push({
32297                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32298                 y : minY
32299             });
32300             
32301             pos.push({
32302                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32303                 y : minY + (this.unitWidth + this.gutter) * 1
32304             });
32305             
32306             return pos;
32307             
32308         }
32309         
32310         pos.push({
32311             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32312             y : minY
32313         });
32314
32315         pos.push({
32316             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32317             y : minY + (this.unitWidth + this.gutter) * 2
32318         });
32319
32320         pos.push({
32321             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32322             y : minY + (this.unitWidth + this.gutter) * 2
32323         });
32324             
32325         return pos;
32326         
32327     },
32328     
32329     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32330     {
32331         var pos = [];
32332         
32333         if(box[0].size == 'xs'){
32334             
32335             pos.push({
32336                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32337                 y : minY
32338             });
32339
32340             pos.push({
32341                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32342                 y : minY
32343             });
32344             
32345             pos.push({
32346                 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),
32347                 y : minY
32348             });
32349             
32350             pos.push({
32351                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32352                 y : minY + (this.unitWidth + this.gutter) * 1
32353             });
32354             
32355             return pos;
32356             
32357         }
32358         
32359         pos.push({
32360             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32361             y : minY
32362         });
32363         
32364         pos.push({
32365             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32366             y : minY + (this.unitWidth + this.gutter) * 2
32367         });
32368         
32369         pos.push({
32370             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32371             y : minY + (this.unitWidth + this.gutter) * 2
32372         });
32373         
32374         pos.push({
32375             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),
32376             y : minY + (this.unitWidth + this.gutter) * 2
32377         });
32378
32379         return pos;
32380         
32381     },
32382     
32383     /**
32384     * remove a Masonry Brick
32385     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32386     */
32387     removeBrick : function(brick_id)
32388     {
32389         if (!brick_id) {
32390             return;
32391         }
32392         
32393         for (var i = 0; i<this.bricks.length; i++) {
32394             if (this.bricks[i].id == brick_id) {
32395                 this.bricks.splice(i,1);
32396                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32397                 this.initial();
32398             }
32399         }
32400     },
32401     
32402     /**
32403     * adds a Masonry Brick
32404     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32405     */
32406     addBrick : function(cfg)
32407     {
32408         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32409         //this.register(cn);
32410         cn.parentId = this.id;
32411         cn.render(this.el);
32412         return cn;
32413     },
32414     
32415     /**
32416     * register a Masonry Brick
32417     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32418     */
32419     
32420     register : function(brick)
32421     {
32422         this.bricks.push(brick);
32423         brick.masonryId = this.id;
32424     },
32425     
32426     /**
32427     * clear all the Masonry Brick
32428     */
32429     clearAll : function()
32430     {
32431         this.bricks = [];
32432         //this.getChildContainer().dom.innerHTML = "";
32433         this.el.dom.innerHTML = '';
32434     },
32435     
32436     getSelected : function()
32437     {
32438         if (!this.selectedBrick) {
32439             return false;
32440         }
32441         
32442         return this.selectedBrick;
32443     }
32444 });
32445
32446 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32447     
32448     groups: {},
32449      /**
32450     * register a Masonry Layout
32451     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32452     */
32453     
32454     register : function(layout)
32455     {
32456         this.groups[layout.id] = layout;
32457     },
32458     /**
32459     * fetch a  Masonry Layout based on the masonry layout ID
32460     * @param {string} the masonry layout to add
32461     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32462     */
32463     
32464     get: function(layout_id) {
32465         if (typeof(this.groups[layout_id]) == 'undefined') {
32466             return false;
32467         }
32468         return this.groups[layout_id] ;
32469     }
32470     
32471     
32472     
32473 });
32474
32475  
32476
32477  /**
32478  *
32479  * This is based on 
32480  * http://masonry.desandro.com
32481  *
32482  * The idea is to render all the bricks based on vertical width...
32483  *
32484  * The original code extends 'outlayer' - we might need to use that....
32485  * 
32486  */
32487
32488
32489 /**
32490  * @class Roo.bootstrap.LayoutMasonryAuto
32491  * @extends Roo.bootstrap.Component
32492  * Bootstrap Layout Masonry class
32493  * 
32494  * @constructor
32495  * Create a new Element
32496  * @param {Object} config The config object
32497  */
32498
32499 Roo.bootstrap.LayoutMasonryAuto = function(config){
32500     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32501 };
32502
32503 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32504     
32505       /**
32506      * @cfg {Boolean} isFitWidth  - resize the width..
32507      */   
32508     isFitWidth : false,  // options..
32509     /**
32510      * @cfg {Boolean} isOriginLeft = left align?
32511      */   
32512     isOriginLeft : true,
32513     /**
32514      * @cfg {Boolean} isOriginTop = top align?
32515      */   
32516     isOriginTop : false,
32517     /**
32518      * @cfg {Boolean} isLayoutInstant = no animation?
32519      */   
32520     isLayoutInstant : false, // needed?
32521     /**
32522      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32523      */   
32524     isResizingContainer : true,
32525     /**
32526      * @cfg {Number} columnWidth  width of the columns 
32527      */   
32528     
32529     columnWidth : 0,
32530     
32531     /**
32532      * @cfg {Number} maxCols maximum number of columns
32533      */   
32534     
32535     maxCols: 0,
32536     /**
32537      * @cfg {Number} padHeight padding below box..
32538      */   
32539     
32540     padHeight : 10, 
32541     
32542     /**
32543      * @cfg {Boolean} isAutoInitial defalut true
32544      */   
32545     
32546     isAutoInitial : true, 
32547     
32548     // private?
32549     gutter : 0,
32550     
32551     containerWidth: 0,
32552     initialColumnWidth : 0,
32553     currentSize : null,
32554     
32555     colYs : null, // array.
32556     maxY : 0,
32557     padWidth: 10,
32558     
32559     
32560     tag: 'div',
32561     cls: '',
32562     bricks: null, //CompositeElement
32563     cols : 0, // array?
32564     // element : null, // wrapped now this.el
32565     _isLayoutInited : null, 
32566     
32567     
32568     getAutoCreate : function(){
32569         
32570         var cfg = {
32571             tag: this.tag,
32572             cls: 'blog-masonary-wrapper ' + this.cls,
32573             cn : {
32574                 cls : 'mas-boxes masonary'
32575             }
32576         };
32577         
32578         return cfg;
32579     },
32580     
32581     getChildContainer: function( )
32582     {
32583         if (this.boxesEl) {
32584             return this.boxesEl;
32585         }
32586         
32587         this.boxesEl = this.el.select('.mas-boxes').first();
32588         
32589         return this.boxesEl;
32590     },
32591     
32592     
32593     initEvents : function()
32594     {
32595         var _this = this;
32596         
32597         if(this.isAutoInitial){
32598             Roo.log('hook children rendered');
32599             this.on('childrenrendered', function() {
32600                 Roo.log('children rendered');
32601                 _this.initial();
32602             } ,this);
32603         }
32604         
32605     },
32606     
32607     initial : function()
32608     {
32609         this.reloadItems();
32610
32611         this.currentSize = this.el.getBox(true);
32612
32613         /// was window resize... - let's see if this works..
32614         Roo.EventManager.onWindowResize(this.resize, this); 
32615
32616         if(!this.isAutoInitial){
32617             this.layout();
32618             return;
32619         }
32620         
32621         this.layout.defer(500,this);
32622     },
32623     
32624     reloadItems: function()
32625     {
32626         this.bricks = this.el.select('.masonry-brick', true);
32627         
32628         this.bricks.each(function(b) {
32629             //Roo.log(b.getSize());
32630             if (!b.attr('originalwidth')) {
32631                 b.attr('originalwidth',  b.getSize().width);
32632             }
32633             
32634         });
32635         
32636         Roo.log(this.bricks.elements.length);
32637     },
32638     
32639     resize : function()
32640     {
32641         Roo.log('resize');
32642         var cs = this.el.getBox(true);
32643         
32644         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32645             Roo.log("no change in with or X");
32646             return;
32647         }
32648         this.currentSize = cs;
32649         this.layout();
32650     },
32651     
32652     layout : function()
32653     {
32654          Roo.log('layout');
32655         this._resetLayout();
32656         //this._manageStamps();
32657       
32658         // don't animate first layout
32659         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32660         this.layoutItems( isInstant );
32661       
32662         // flag for initalized
32663         this._isLayoutInited = true;
32664     },
32665     
32666     layoutItems : function( isInstant )
32667     {
32668         //var items = this._getItemsForLayout( this.items );
32669         // original code supports filtering layout items.. we just ignore it..
32670         
32671         this._layoutItems( this.bricks , isInstant );
32672       
32673         this._postLayout();
32674     },
32675     _layoutItems : function ( items , isInstant)
32676     {
32677        //this.fireEvent( 'layout', this, items );
32678     
32679
32680         if ( !items || !items.elements.length ) {
32681           // no items, emit event with empty array
32682             return;
32683         }
32684
32685         var queue = [];
32686         items.each(function(item) {
32687             Roo.log("layout item");
32688             Roo.log(item);
32689             // get x/y object from method
32690             var position = this._getItemLayoutPosition( item );
32691             // enqueue
32692             position.item = item;
32693             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32694             queue.push( position );
32695         }, this);
32696       
32697         this._processLayoutQueue( queue );
32698     },
32699     /** Sets position of item in DOM
32700     * @param {Element} item
32701     * @param {Number} x - horizontal position
32702     * @param {Number} y - vertical position
32703     * @param {Boolean} isInstant - disables transitions
32704     */
32705     _processLayoutQueue : function( queue )
32706     {
32707         for ( var i=0, len = queue.length; i < len; i++ ) {
32708             var obj = queue[i];
32709             obj.item.position('absolute');
32710             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32711         }
32712     },
32713       
32714     
32715     /**
32716     * Any logic you want to do after each layout,
32717     * i.e. size the container
32718     */
32719     _postLayout : function()
32720     {
32721         this.resizeContainer();
32722     },
32723     
32724     resizeContainer : function()
32725     {
32726         if ( !this.isResizingContainer ) {
32727             return;
32728         }
32729         var size = this._getContainerSize();
32730         if ( size ) {
32731             this.el.setSize(size.width,size.height);
32732             this.boxesEl.setSize(size.width,size.height);
32733         }
32734     },
32735     
32736     
32737     
32738     _resetLayout : function()
32739     {
32740         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32741         this.colWidth = this.el.getWidth();
32742         //this.gutter = this.el.getWidth(); 
32743         
32744         this.measureColumns();
32745
32746         // reset column Y
32747         var i = this.cols;
32748         this.colYs = [];
32749         while (i--) {
32750             this.colYs.push( 0 );
32751         }
32752     
32753         this.maxY = 0;
32754     },
32755
32756     measureColumns : function()
32757     {
32758         this.getContainerWidth();
32759       // if columnWidth is 0, default to outerWidth of first item
32760         if ( !this.columnWidth ) {
32761             var firstItem = this.bricks.first();
32762             Roo.log(firstItem);
32763             this.columnWidth  = this.containerWidth;
32764             if (firstItem && firstItem.attr('originalwidth') ) {
32765                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32766             }
32767             // columnWidth fall back to item of first element
32768             Roo.log("set column width?");
32769                         this.initialColumnWidth = this.columnWidth  ;
32770
32771             // if first elem has no width, default to size of container
32772             
32773         }
32774         
32775         
32776         if (this.initialColumnWidth) {
32777             this.columnWidth = this.initialColumnWidth;
32778         }
32779         
32780         
32781             
32782         // column width is fixed at the top - however if container width get's smaller we should
32783         // reduce it...
32784         
32785         // this bit calcs how man columns..
32786             
32787         var columnWidth = this.columnWidth += this.gutter;
32788       
32789         // calculate columns
32790         var containerWidth = this.containerWidth + this.gutter;
32791         
32792         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32793         // fix rounding errors, typically with gutters
32794         var excess = columnWidth - containerWidth % columnWidth;
32795         
32796         
32797         // if overshoot is less than a pixel, round up, otherwise floor it
32798         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32799         cols = Math[ mathMethod ]( cols );
32800         this.cols = Math.max( cols, 1 );
32801         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32802         
32803          // padding positioning..
32804         var totalColWidth = this.cols * this.columnWidth;
32805         var padavail = this.containerWidth - totalColWidth;
32806         // so for 2 columns - we need 3 'pads'
32807         
32808         var padNeeded = (1+this.cols) * this.padWidth;
32809         
32810         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32811         
32812         this.columnWidth += padExtra
32813         //this.padWidth = Math.floor(padavail /  ( this.cols));
32814         
32815         // adjust colum width so that padding is fixed??
32816         
32817         // we have 3 columns ... total = width * 3
32818         // we have X left over... that should be used by 
32819         
32820         //if (this.expandC) {
32821             
32822         //}
32823         
32824         
32825         
32826     },
32827     
32828     getContainerWidth : function()
32829     {
32830        /* // container is parent if fit width
32831         var container = this.isFitWidth ? this.element.parentNode : this.element;
32832         // check that this.size and size are there
32833         // IE8 triggers resize on body size change, so they might not be
32834         
32835         var size = getSize( container );  //FIXME
32836         this.containerWidth = size && size.innerWidth; //FIXME
32837         */
32838          
32839         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32840         
32841     },
32842     
32843     _getItemLayoutPosition : function( item )  // what is item?
32844     {
32845         // we resize the item to our columnWidth..
32846       
32847         item.setWidth(this.columnWidth);
32848         item.autoBoxAdjust  = false;
32849         
32850         var sz = item.getSize();
32851  
32852         // how many columns does this brick span
32853         var remainder = this.containerWidth % this.columnWidth;
32854         
32855         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32856         // round if off by 1 pixel, otherwise use ceil
32857         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32858         colSpan = Math.min( colSpan, this.cols );
32859         
32860         // normally this should be '1' as we dont' currently allow multi width columns..
32861         
32862         var colGroup = this._getColGroup( colSpan );
32863         // get the minimum Y value from the columns
32864         var minimumY = Math.min.apply( Math, colGroup );
32865         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32866         
32867         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32868          
32869         // position the brick
32870         var position = {
32871             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32872             y: this.currentSize.y + minimumY + this.padHeight
32873         };
32874         
32875         Roo.log(position);
32876         // apply setHeight to necessary columns
32877         var setHeight = minimumY + sz.height + this.padHeight;
32878         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32879         
32880         var setSpan = this.cols + 1 - colGroup.length;
32881         for ( var i = 0; i < setSpan; i++ ) {
32882           this.colYs[ shortColIndex + i ] = setHeight ;
32883         }
32884       
32885         return position;
32886     },
32887     
32888     /**
32889      * @param {Number} colSpan - number of columns the element spans
32890      * @returns {Array} colGroup
32891      */
32892     _getColGroup : function( colSpan )
32893     {
32894         if ( colSpan < 2 ) {
32895           // if brick spans only one column, use all the column Ys
32896           return this.colYs;
32897         }
32898       
32899         var colGroup = [];
32900         // how many different places could this brick fit horizontally
32901         var groupCount = this.cols + 1 - colSpan;
32902         // for each group potential horizontal position
32903         for ( var i = 0; i < groupCount; i++ ) {
32904           // make an array of colY values for that one group
32905           var groupColYs = this.colYs.slice( i, i + colSpan );
32906           // and get the max value of the array
32907           colGroup[i] = Math.max.apply( Math, groupColYs );
32908         }
32909         return colGroup;
32910     },
32911     /*
32912     _manageStamp : function( stamp )
32913     {
32914         var stampSize =  stamp.getSize();
32915         var offset = stamp.getBox();
32916         // get the columns that this stamp affects
32917         var firstX = this.isOriginLeft ? offset.x : offset.right;
32918         var lastX = firstX + stampSize.width;
32919         var firstCol = Math.floor( firstX / this.columnWidth );
32920         firstCol = Math.max( 0, firstCol );
32921         
32922         var lastCol = Math.floor( lastX / this.columnWidth );
32923         // lastCol should not go over if multiple of columnWidth #425
32924         lastCol -= lastX % this.columnWidth ? 0 : 1;
32925         lastCol = Math.min( this.cols - 1, lastCol );
32926         
32927         // set colYs to bottom of the stamp
32928         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32929             stampSize.height;
32930             
32931         for ( var i = firstCol; i <= lastCol; i++ ) {
32932           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32933         }
32934     },
32935     */
32936     
32937     _getContainerSize : function()
32938     {
32939         this.maxY = Math.max.apply( Math, this.colYs );
32940         var size = {
32941             height: this.maxY
32942         };
32943       
32944         if ( this.isFitWidth ) {
32945             size.width = this._getContainerFitWidth();
32946         }
32947       
32948         return size;
32949     },
32950     
32951     _getContainerFitWidth : function()
32952     {
32953         var unusedCols = 0;
32954         // count unused columns
32955         var i = this.cols;
32956         while ( --i ) {
32957           if ( this.colYs[i] !== 0 ) {
32958             break;
32959           }
32960           unusedCols++;
32961         }
32962         // fit container to columns that have been used
32963         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32964     },
32965     
32966     needsResizeLayout : function()
32967     {
32968         var previousWidth = this.containerWidth;
32969         this.getContainerWidth();
32970         return previousWidth !== this.containerWidth;
32971     }
32972  
32973 });
32974
32975  
32976
32977  /*
32978  * - LGPL
32979  *
32980  * element
32981  * 
32982  */
32983
32984 /**
32985  * @class Roo.bootstrap.MasonryBrick
32986  * @extends Roo.bootstrap.Component
32987  * Bootstrap MasonryBrick class
32988  * 
32989  * @constructor
32990  * Create a new MasonryBrick
32991  * @param {Object} config The config object
32992  */
32993
32994 Roo.bootstrap.MasonryBrick = function(config){
32995     
32996     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32997     
32998     Roo.bootstrap.MasonryBrick.register(this);
32999     
33000     this.addEvents({
33001         // raw events
33002         /**
33003          * @event click
33004          * When a MasonryBrick is clcik
33005          * @param {Roo.bootstrap.MasonryBrick} this
33006          * @param {Roo.EventObject} e
33007          */
33008         "click" : true
33009     });
33010 };
33011
33012 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33013     
33014     /**
33015      * @cfg {String} title
33016      */   
33017     title : '',
33018     /**
33019      * @cfg {String} html
33020      */   
33021     html : '',
33022     /**
33023      * @cfg {String} bgimage
33024      */   
33025     bgimage : '',
33026     /**
33027      * @cfg {String} videourl
33028      */   
33029     videourl : '',
33030     /**
33031      * @cfg {String} cls
33032      */   
33033     cls : '',
33034     /**
33035      * @cfg {String} href
33036      */   
33037     href : '',
33038     /**
33039      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33040      */   
33041     size : 'xs',
33042     
33043     /**
33044      * @cfg {String} placetitle (center|bottom)
33045      */   
33046     placetitle : '',
33047     
33048     /**
33049      * @cfg {Boolean} isFitContainer defalut true
33050      */   
33051     isFitContainer : true, 
33052     
33053     /**
33054      * @cfg {Boolean} preventDefault defalut false
33055      */   
33056     preventDefault : false, 
33057     
33058     /**
33059      * @cfg {Boolean} inverse defalut false
33060      */   
33061     maskInverse : false, 
33062     
33063     getAutoCreate : function()
33064     {
33065         if(!this.isFitContainer){
33066             return this.getSplitAutoCreate();
33067         }
33068         
33069         var cls = 'masonry-brick masonry-brick-full';
33070         
33071         if(this.href.length){
33072             cls += ' masonry-brick-link';
33073         }
33074         
33075         if(this.bgimage.length){
33076             cls += ' masonry-brick-image';
33077         }
33078         
33079         if(this.maskInverse){
33080             cls += ' mask-inverse';
33081         }
33082         
33083         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33084             cls += ' enable-mask';
33085         }
33086         
33087         if(this.size){
33088             cls += ' masonry-' + this.size + '-brick';
33089         }
33090         
33091         if(this.placetitle.length){
33092             
33093             switch (this.placetitle) {
33094                 case 'center' :
33095                     cls += ' masonry-center-title';
33096                     break;
33097                 case 'bottom' :
33098                     cls += ' masonry-bottom-title';
33099                     break;
33100                 default:
33101                     break;
33102             }
33103             
33104         } else {
33105             if(!this.html.length && !this.bgimage.length){
33106                 cls += ' masonry-center-title';
33107             }
33108
33109             if(!this.html.length && this.bgimage.length){
33110                 cls += ' masonry-bottom-title';
33111             }
33112         }
33113         
33114         if(this.cls){
33115             cls += ' ' + this.cls;
33116         }
33117         
33118         var cfg = {
33119             tag: (this.href.length) ? 'a' : 'div',
33120             cls: cls,
33121             cn: [
33122                 {
33123                     tag: 'div',
33124                     cls: 'masonry-brick-mask'
33125                 },
33126                 {
33127                     tag: 'div',
33128                     cls: 'masonry-brick-paragraph',
33129                     cn: []
33130                 }
33131             ]
33132         };
33133         
33134         if(this.href.length){
33135             cfg.href = this.href;
33136         }
33137         
33138         var cn = cfg.cn[1].cn;
33139         
33140         if(this.title.length){
33141             cn.push({
33142                 tag: 'h4',
33143                 cls: 'masonry-brick-title',
33144                 html: this.title
33145             });
33146         }
33147         
33148         if(this.html.length){
33149             cn.push({
33150                 tag: 'p',
33151                 cls: 'masonry-brick-text',
33152                 html: this.html
33153             });
33154         }
33155         
33156         if (!this.title.length && !this.html.length) {
33157             cfg.cn[1].cls += ' hide';
33158         }
33159         
33160         if(this.bgimage.length){
33161             cfg.cn.push({
33162                 tag: 'img',
33163                 cls: 'masonry-brick-image-view',
33164                 src: this.bgimage
33165             });
33166         }
33167         
33168         if(this.videourl.length){
33169             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33170             // youtube support only?
33171             cfg.cn.push({
33172                 tag: 'iframe',
33173                 cls: 'masonry-brick-image-view',
33174                 src: vurl,
33175                 frameborder : 0,
33176                 allowfullscreen : true
33177             });
33178         }
33179         
33180         return cfg;
33181         
33182     },
33183     
33184     getSplitAutoCreate : function()
33185     {
33186         var cls = 'masonry-brick masonry-brick-split';
33187         
33188         if(this.href.length){
33189             cls += ' masonry-brick-link';
33190         }
33191         
33192         if(this.bgimage.length){
33193             cls += ' masonry-brick-image';
33194         }
33195         
33196         if(this.size){
33197             cls += ' masonry-' + this.size + '-brick';
33198         }
33199         
33200         switch (this.placetitle) {
33201             case 'center' :
33202                 cls += ' masonry-center-title';
33203                 break;
33204             case 'bottom' :
33205                 cls += ' masonry-bottom-title';
33206                 break;
33207             default:
33208                 if(!this.bgimage.length){
33209                     cls += ' masonry-center-title';
33210                 }
33211
33212                 if(this.bgimage.length){
33213                     cls += ' masonry-bottom-title';
33214                 }
33215                 break;
33216         }
33217         
33218         if(this.cls){
33219             cls += ' ' + this.cls;
33220         }
33221         
33222         var cfg = {
33223             tag: (this.href.length) ? 'a' : 'div',
33224             cls: cls,
33225             cn: [
33226                 {
33227                     tag: 'div',
33228                     cls: 'masonry-brick-split-head',
33229                     cn: [
33230                         {
33231                             tag: 'div',
33232                             cls: 'masonry-brick-paragraph',
33233                             cn: []
33234                         }
33235                     ]
33236                 },
33237                 {
33238                     tag: 'div',
33239                     cls: 'masonry-brick-split-body',
33240                     cn: []
33241                 }
33242             ]
33243         };
33244         
33245         if(this.href.length){
33246             cfg.href = this.href;
33247         }
33248         
33249         if(this.title.length){
33250             cfg.cn[0].cn[0].cn.push({
33251                 tag: 'h4',
33252                 cls: 'masonry-brick-title',
33253                 html: this.title
33254             });
33255         }
33256         
33257         if(this.html.length){
33258             cfg.cn[1].cn.push({
33259                 tag: 'p',
33260                 cls: 'masonry-brick-text',
33261                 html: this.html
33262             });
33263         }
33264
33265         if(this.bgimage.length){
33266             cfg.cn[0].cn.push({
33267                 tag: 'img',
33268                 cls: 'masonry-brick-image-view',
33269                 src: this.bgimage
33270             });
33271         }
33272         
33273         if(this.videourl.length){
33274             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33275             // youtube support only?
33276             cfg.cn[0].cn.cn.push({
33277                 tag: 'iframe',
33278                 cls: 'masonry-brick-image-view',
33279                 src: vurl,
33280                 frameborder : 0,
33281                 allowfullscreen : true
33282             });
33283         }
33284         
33285         return cfg;
33286     },
33287     
33288     initEvents: function() 
33289     {
33290         switch (this.size) {
33291             case 'xs' :
33292                 this.x = 1;
33293                 this.y = 1;
33294                 break;
33295             case 'sm' :
33296                 this.x = 2;
33297                 this.y = 2;
33298                 break;
33299             case 'md' :
33300             case 'md-left' :
33301             case 'md-right' :
33302                 this.x = 3;
33303                 this.y = 3;
33304                 break;
33305             case 'tall' :
33306                 this.x = 2;
33307                 this.y = 3;
33308                 break;
33309             case 'wide' :
33310                 this.x = 3;
33311                 this.y = 2;
33312                 break;
33313             case 'wide-thin' :
33314                 this.x = 3;
33315                 this.y = 1;
33316                 break;
33317                         
33318             default :
33319                 break;
33320         }
33321         
33322         if(Roo.isTouch){
33323             this.el.on('touchstart', this.onTouchStart, this);
33324             this.el.on('touchmove', this.onTouchMove, this);
33325             this.el.on('touchend', this.onTouchEnd, this);
33326             this.el.on('contextmenu', this.onContextMenu, this);
33327         } else {
33328             this.el.on('mouseenter'  ,this.enter, this);
33329             this.el.on('mouseleave', this.leave, this);
33330             this.el.on('click', this.onClick, this);
33331         }
33332         
33333         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33334             this.parent().bricks.push(this);   
33335         }
33336         
33337     },
33338     
33339     onClick: function(e, el)
33340     {
33341         var time = this.endTimer - this.startTimer;
33342         // Roo.log(e.preventDefault());
33343         if(Roo.isTouch){
33344             if(time > 1000){
33345                 e.preventDefault();
33346                 return;
33347             }
33348         }
33349         
33350         if(!this.preventDefault){
33351             return;
33352         }
33353         
33354         e.preventDefault();
33355         
33356         if (this.activeClass != '') {
33357             this.selectBrick();
33358         }
33359         
33360         this.fireEvent('click', this, e);
33361     },
33362     
33363     enter: function(e, el)
33364     {
33365         e.preventDefault();
33366         
33367         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33368             return;
33369         }
33370         
33371         if(this.bgimage.length && this.html.length){
33372             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33373         }
33374     },
33375     
33376     leave: function(e, el)
33377     {
33378         e.preventDefault();
33379         
33380         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33381             return;
33382         }
33383         
33384         if(this.bgimage.length && this.html.length){
33385             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33386         }
33387     },
33388     
33389     onTouchStart: function(e, el)
33390     {
33391 //        e.preventDefault();
33392         
33393         this.touchmoved = false;
33394         
33395         if(!this.isFitContainer){
33396             return;
33397         }
33398         
33399         if(!this.bgimage.length || !this.html.length){
33400             return;
33401         }
33402         
33403         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33404         
33405         this.timer = new Date().getTime();
33406         
33407     },
33408     
33409     onTouchMove: function(e, el)
33410     {
33411         this.touchmoved = true;
33412     },
33413     
33414     onContextMenu : function(e,el)
33415     {
33416         e.preventDefault();
33417         e.stopPropagation();
33418         return false;
33419     },
33420     
33421     onTouchEnd: function(e, el)
33422     {
33423 //        e.preventDefault();
33424         
33425         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33426         
33427             this.leave(e,el);
33428             
33429             return;
33430         }
33431         
33432         if(!this.bgimage.length || !this.html.length){
33433             
33434             if(this.href.length){
33435                 window.location.href = this.href;
33436             }
33437             
33438             return;
33439         }
33440         
33441         if(!this.isFitContainer){
33442             return;
33443         }
33444         
33445         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33446         
33447         window.location.href = this.href;
33448     },
33449     
33450     //selection on single brick only
33451     selectBrick : function() {
33452         
33453         if (!this.parentId) {
33454             return;
33455         }
33456         
33457         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33458         var index = m.selectedBrick.indexOf(this.id);
33459         
33460         if ( index > -1) {
33461             m.selectedBrick.splice(index,1);
33462             this.el.removeClass(this.activeClass);
33463             return;
33464         }
33465         
33466         for(var i = 0; i < m.selectedBrick.length; i++) {
33467             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33468             b.el.removeClass(b.activeClass);
33469         }
33470         
33471         m.selectedBrick = [];
33472         
33473         m.selectedBrick.push(this.id);
33474         this.el.addClass(this.activeClass);
33475         return;
33476     },
33477     
33478     isSelected : function(){
33479         return this.el.hasClass(this.activeClass);
33480         
33481     }
33482 });
33483
33484 Roo.apply(Roo.bootstrap.MasonryBrick, {
33485     
33486     //groups: {},
33487     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33488      /**
33489     * register a Masonry Brick
33490     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33491     */
33492     
33493     register : function(brick)
33494     {
33495         //this.groups[brick.id] = brick;
33496         this.groups.add(brick.id, brick);
33497     },
33498     /**
33499     * fetch a  masonry brick based on the masonry brick ID
33500     * @param {string} the masonry brick to add
33501     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33502     */
33503     
33504     get: function(brick_id) 
33505     {
33506         // if (typeof(this.groups[brick_id]) == 'undefined') {
33507         //     return false;
33508         // }
33509         // return this.groups[brick_id] ;
33510         
33511         if(this.groups.key(brick_id)) {
33512             return this.groups.key(brick_id);
33513         }
33514         
33515         return false;
33516     }
33517     
33518     
33519     
33520 });
33521
33522  /*
33523  * - LGPL
33524  *
33525  * element
33526  * 
33527  */
33528
33529 /**
33530  * @class Roo.bootstrap.Brick
33531  * @extends Roo.bootstrap.Component
33532  * Bootstrap Brick class
33533  * 
33534  * @constructor
33535  * Create a new Brick
33536  * @param {Object} config The config object
33537  */
33538
33539 Roo.bootstrap.Brick = function(config){
33540     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33541     
33542     this.addEvents({
33543         // raw events
33544         /**
33545          * @event click
33546          * When a Brick is click
33547          * @param {Roo.bootstrap.Brick} this
33548          * @param {Roo.EventObject} e
33549          */
33550         "click" : true
33551     });
33552 };
33553
33554 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33555     
33556     /**
33557      * @cfg {String} title
33558      */   
33559     title : '',
33560     /**
33561      * @cfg {String} html
33562      */   
33563     html : '',
33564     /**
33565      * @cfg {String} bgimage
33566      */   
33567     bgimage : '',
33568     /**
33569      * @cfg {String} cls
33570      */   
33571     cls : '',
33572     /**
33573      * @cfg {String} href
33574      */   
33575     href : '',
33576     /**
33577      * @cfg {String} video
33578      */   
33579     video : '',
33580     /**
33581      * @cfg {Boolean} square
33582      */   
33583     square : true,
33584     
33585     getAutoCreate : function()
33586     {
33587         var cls = 'roo-brick';
33588         
33589         if(this.href.length){
33590             cls += ' roo-brick-link';
33591         }
33592         
33593         if(this.bgimage.length){
33594             cls += ' roo-brick-image';
33595         }
33596         
33597         if(!this.html.length && !this.bgimage.length){
33598             cls += ' roo-brick-center-title';
33599         }
33600         
33601         if(!this.html.length && this.bgimage.length){
33602             cls += ' roo-brick-bottom-title';
33603         }
33604         
33605         if(this.cls){
33606             cls += ' ' + this.cls;
33607         }
33608         
33609         var cfg = {
33610             tag: (this.href.length) ? 'a' : 'div',
33611             cls: cls,
33612             cn: [
33613                 {
33614                     tag: 'div',
33615                     cls: 'roo-brick-paragraph',
33616                     cn: []
33617                 }
33618             ]
33619         };
33620         
33621         if(this.href.length){
33622             cfg.href = this.href;
33623         }
33624         
33625         var cn = cfg.cn[0].cn;
33626         
33627         if(this.title.length){
33628             cn.push({
33629                 tag: 'h4',
33630                 cls: 'roo-brick-title',
33631                 html: this.title
33632             });
33633         }
33634         
33635         if(this.html.length){
33636             cn.push({
33637                 tag: 'p',
33638                 cls: 'roo-brick-text',
33639                 html: this.html
33640             });
33641         } else {
33642             cn.cls += ' hide';
33643         }
33644         
33645         if(this.bgimage.length){
33646             cfg.cn.push({
33647                 tag: 'img',
33648                 cls: 'roo-brick-image-view',
33649                 src: this.bgimage
33650             });
33651         }
33652         
33653         return cfg;
33654     },
33655     
33656     initEvents: function() 
33657     {
33658         if(this.title.length || this.html.length){
33659             this.el.on('mouseenter'  ,this.enter, this);
33660             this.el.on('mouseleave', this.leave, this);
33661         }
33662         
33663         Roo.EventManager.onWindowResize(this.resize, this); 
33664         
33665         if(this.bgimage.length){
33666             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33667             this.imageEl.on('load', this.onImageLoad, this);
33668             return;
33669         }
33670         
33671         this.resize();
33672     },
33673     
33674     onImageLoad : function()
33675     {
33676         this.resize();
33677     },
33678     
33679     resize : function()
33680     {
33681         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33682         
33683         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33684         
33685         if(this.bgimage.length){
33686             var image = this.el.select('.roo-brick-image-view', true).first();
33687             
33688             image.setWidth(paragraph.getWidth());
33689             
33690             if(this.square){
33691                 image.setHeight(paragraph.getWidth());
33692             }
33693             
33694             this.el.setHeight(image.getHeight());
33695             paragraph.setHeight(image.getHeight());
33696             
33697         }
33698         
33699     },
33700     
33701     enter: function(e, el)
33702     {
33703         e.preventDefault();
33704         
33705         if(this.bgimage.length){
33706             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33707             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33708         }
33709     },
33710     
33711     leave: function(e, el)
33712     {
33713         e.preventDefault();
33714         
33715         if(this.bgimage.length){
33716             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33717             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33718         }
33719     }
33720     
33721 });
33722
33723  
33724
33725  /*
33726  * - LGPL
33727  *
33728  * Number field 
33729  */
33730
33731 /**
33732  * @class Roo.bootstrap.NumberField
33733  * @extends Roo.bootstrap.Input
33734  * Bootstrap NumberField class
33735  * 
33736  * 
33737  * 
33738  * 
33739  * @constructor
33740  * Create a new NumberField
33741  * @param {Object} config The config object
33742  */
33743
33744 Roo.bootstrap.NumberField = function(config){
33745     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33746 };
33747
33748 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33749     
33750     /**
33751      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33752      */
33753     allowDecimals : true,
33754     /**
33755      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33756      */
33757     decimalSeparator : ".",
33758     /**
33759      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33760      */
33761     decimalPrecision : 2,
33762     /**
33763      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33764      */
33765     allowNegative : true,
33766     
33767     /**
33768      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33769      */
33770     allowZero: true,
33771     /**
33772      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33773      */
33774     minValue : Number.NEGATIVE_INFINITY,
33775     /**
33776      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33777      */
33778     maxValue : Number.MAX_VALUE,
33779     /**
33780      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33781      */
33782     minText : "The minimum value for this field is {0}",
33783     /**
33784      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33785      */
33786     maxText : "The maximum value for this field is {0}",
33787     /**
33788      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33789      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33790      */
33791     nanText : "{0} is not a valid number",
33792     /**
33793      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33794      */
33795     thousandsDelimiter : false,
33796     /**
33797      * @cfg {String} valueAlign alignment of value
33798      */
33799     valueAlign : "left",
33800
33801     getAutoCreate : function()
33802     {
33803         var hiddenInput = {
33804             tag: 'input',
33805             type: 'hidden',
33806             id: Roo.id(),
33807             cls: 'hidden-number-input'
33808         };
33809         
33810         if (this.name) {
33811             hiddenInput.name = this.name;
33812         }
33813         
33814         this.name = '';
33815         
33816         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33817         
33818         this.name = hiddenInput.name;
33819         
33820         if(cfg.cn.length > 0) {
33821             cfg.cn.push(hiddenInput);
33822         }
33823         
33824         return cfg;
33825     },
33826
33827     // private
33828     initEvents : function()
33829     {   
33830         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33831         
33832         var allowed = "0123456789";
33833         
33834         if(this.allowDecimals){
33835             allowed += this.decimalSeparator;
33836         }
33837         
33838         if(this.allowNegative){
33839             allowed += "-";
33840         }
33841         
33842         if(this.thousandsDelimiter) {
33843             allowed += ",";
33844         }
33845         
33846         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33847         
33848         var keyPress = function(e){
33849             
33850             var k = e.getKey();
33851             
33852             var c = e.getCharCode();
33853             
33854             if(
33855                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33856                     allowed.indexOf(String.fromCharCode(c)) === -1
33857             ){
33858                 e.stopEvent();
33859                 return;
33860             }
33861             
33862             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33863                 return;
33864             }
33865             
33866             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33867                 e.stopEvent();
33868             }
33869         };
33870         
33871         this.el.on("keypress", keyPress, this);
33872     },
33873     
33874     validateValue : function(value)
33875     {
33876         
33877         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33878             return false;
33879         }
33880         
33881         var num = this.parseValue(value);
33882         
33883         if(isNaN(num)){
33884             this.markInvalid(String.format(this.nanText, value));
33885             return false;
33886         }
33887         
33888         if(num < this.minValue){
33889             this.markInvalid(String.format(this.minText, this.minValue));
33890             return false;
33891         }
33892         
33893         if(num > this.maxValue){
33894             this.markInvalid(String.format(this.maxText, this.maxValue));
33895             return false;
33896         }
33897         
33898         return true;
33899     },
33900
33901     getValue : function()
33902     {
33903         var v = this.hiddenEl().getValue();
33904         
33905         return this.fixPrecision(this.parseValue(v));
33906     },
33907
33908     parseValue : function(value)
33909     {
33910         if(this.thousandsDelimiter) {
33911             value += "";
33912             r = new RegExp(",", "g");
33913             value = value.replace(r, "");
33914         }
33915         
33916         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33917         return isNaN(value) ? '' : value;
33918     },
33919
33920     fixPrecision : function(value)
33921     {
33922         if(this.thousandsDelimiter) {
33923             value += "";
33924             r = new RegExp(",", "g");
33925             value = value.replace(r, "");
33926         }
33927         
33928         var nan = isNaN(value);
33929         
33930         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33931             return nan ? '' : value;
33932         }
33933         return parseFloat(value).toFixed(this.decimalPrecision);
33934     },
33935
33936     setValue : function(v)
33937     {
33938         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33939         
33940         this.value = v;
33941         
33942         if(this.rendered){
33943             
33944             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33945             
33946             this.inputEl().dom.value = (v == '') ? '' :
33947                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33948             
33949             if(!this.allowZero && v === '0') {
33950                 this.hiddenEl().dom.value = '';
33951                 this.inputEl().dom.value = '';
33952             }
33953             
33954             this.validate();
33955         }
33956     },
33957
33958     decimalPrecisionFcn : function(v)
33959     {
33960         return Math.floor(v);
33961     },
33962
33963     beforeBlur : function()
33964     {
33965         var v = this.parseValue(this.getRawValue());
33966         
33967         if(v || v === 0 || v === ''){
33968             this.setValue(v);
33969         }
33970     },
33971     
33972     hiddenEl : function()
33973     {
33974         return this.el.select('input.hidden-number-input',true).first();
33975     }
33976     
33977 });
33978
33979  
33980
33981 /*
33982 * Licence: LGPL
33983 */
33984
33985 /**
33986  * @class Roo.bootstrap.DocumentSlider
33987  * @extends Roo.bootstrap.Component
33988  * Bootstrap DocumentSlider class
33989  * 
33990  * @constructor
33991  * Create a new DocumentViewer
33992  * @param {Object} config The config object
33993  */
33994
33995 Roo.bootstrap.DocumentSlider = function(config){
33996     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33997     
33998     this.files = [];
33999     
34000     this.addEvents({
34001         /**
34002          * @event initial
34003          * Fire after initEvent
34004          * @param {Roo.bootstrap.DocumentSlider} this
34005          */
34006         "initial" : true,
34007         /**
34008          * @event update
34009          * Fire after update
34010          * @param {Roo.bootstrap.DocumentSlider} this
34011          */
34012         "update" : true,
34013         /**
34014          * @event click
34015          * Fire after click
34016          * @param {Roo.bootstrap.DocumentSlider} this
34017          */
34018         "click" : true
34019     });
34020 };
34021
34022 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34023     
34024     files : false,
34025     
34026     indicator : 0,
34027     
34028     getAutoCreate : function()
34029     {
34030         var cfg = {
34031             tag : 'div',
34032             cls : 'roo-document-slider',
34033             cn : [
34034                 {
34035                     tag : 'div',
34036                     cls : 'roo-document-slider-header',
34037                     cn : [
34038                         {
34039                             tag : 'div',
34040                             cls : 'roo-document-slider-header-title'
34041                         }
34042                     ]
34043                 },
34044                 {
34045                     tag : 'div',
34046                     cls : 'roo-document-slider-body',
34047                     cn : [
34048                         {
34049                             tag : 'div',
34050                             cls : 'roo-document-slider-prev',
34051                             cn : [
34052                                 {
34053                                     tag : 'i',
34054                                     cls : 'fa fa-chevron-left'
34055                                 }
34056                             ]
34057                         },
34058                         {
34059                             tag : 'div',
34060                             cls : 'roo-document-slider-thumb',
34061                             cn : [
34062                                 {
34063                                     tag : 'img',
34064                                     cls : 'roo-document-slider-image'
34065                                 }
34066                             ]
34067                         },
34068                         {
34069                             tag : 'div',
34070                             cls : 'roo-document-slider-next',
34071                             cn : [
34072                                 {
34073                                     tag : 'i',
34074                                     cls : 'fa fa-chevron-right'
34075                                 }
34076                             ]
34077                         }
34078                     ]
34079                 }
34080             ]
34081         };
34082         
34083         return cfg;
34084     },
34085     
34086     initEvents : function()
34087     {
34088         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34089         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34090         
34091         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34092         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34093         
34094         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34095         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34096         
34097         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34098         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34099         
34100         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34101         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34102         
34103         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34104         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34105         
34106         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34107         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34108         
34109         this.thumbEl.on('click', this.onClick, this);
34110         
34111         this.prevIndicator.on('click', this.prev, this);
34112         
34113         this.nextIndicator.on('click', this.next, this);
34114         
34115     },
34116     
34117     initial : function()
34118     {
34119         if(this.files.length){
34120             this.indicator = 1;
34121             this.update()
34122         }
34123         
34124         this.fireEvent('initial', this);
34125     },
34126     
34127     update : function()
34128     {
34129         this.imageEl.attr('src', this.files[this.indicator - 1]);
34130         
34131         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34132         
34133         this.prevIndicator.show();
34134         
34135         if(this.indicator == 1){
34136             this.prevIndicator.hide();
34137         }
34138         
34139         this.nextIndicator.show();
34140         
34141         if(this.indicator == this.files.length){
34142             this.nextIndicator.hide();
34143         }
34144         
34145         this.thumbEl.scrollTo('top');
34146         
34147         this.fireEvent('update', this);
34148     },
34149     
34150     onClick : function(e)
34151     {
34152         e.preventDefault();
34153         
34154         this.fireEvent('click', this);
34155     },
34156     
34157     prev : function(e)
34158     {
34159         e.preventDefault();
34160         
34161         this.indicator = Math.max(1, this.indicator - 1);
34162         
34163         this.update();
34164     },
34165     
34166     next : function(e)
34167     {
34168         e.preventDefault();
34169         
34170         this.indicator = Math.min(this.files.length, this.indicator + 1);
34171         
34172         this.update();
34173     }
34174 });
34175 /*
34176  * - LGPL
34177  *
34178  * RadioSet
34179  *
34180  *
34181  */
34182
34183 /**
34184  * @class Roo.bootstrap.RadioSet
34185  * @extends Roo.bootstrap.Input
34186  * Bootstrap RadioSet class
34187  * @cfg {String} indicatorpos (left|right) default left
34188  * @cfg {Boolean} inline (true|false) inline the element (default true)
34189  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34190  * @constructor
34191  * Create a new RadioSet
34192  * @param {Object} config The config object
34193  */
34194
34195 Roo.bootstrap.RadioSet = function(config){
34196     
34197     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34198     
34199     this.radioes = [];
34200     
34201     Roo.bootstrap.RadioSet.register(this);
34202     
34203     this.addEvents({
34204         /**
34205         * @event check
34206         * Fires when the element is checked or unchecked.
34207         * @param {Roo.bootstrap.RadioSet} this This radio
34208         * @param {Roo.bootstrap.Radio} item The checked item
34209         */
34210        check : true,
34211        /**
34212         * @event click
34213         * Fires when the element is click.
34214         * @param {Roo.bootstrap.RadioSet} this This radio set
34215         * @param {Roo.bootstrap.Radio} item The checked item
34216         * @param {Roo.EventObject} e The event object
34217         */
34218        click : true
34219     });
34220     
34221 };
34222
34223 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34224
34225     radioes : false,
34226     
34227     inline : true,
34228     
34229     weight : '',
34230     
34231     indicatorpos : 'left',
34232     
34233     getAutoCreate : function()
34234     {
34235         var label = {
34236             tag : 'label',
34237             cls : 'roo-radio-set-label',
34238             cn : [
34239                 {
34240                     tag : 'span',
34241                     html : this.fieldLabel
34242                 }
34243             ]
34244         };
34245         if (Roo.bootstrap.version == 3) {
34246             
34247             
34248             if(this.indicatorpos == 'left'){
34249                 label.cn.unshift({
34250                     tag : 'i',
34251                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34252                     tooltip : 'This field is required'
34253                 });
34254             } else {
34255                 label.cn.push({
34256                     tag : 'i',
34257                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34258                     tooltip : 'This field is required'
34259                 });
34260             }
34261         }
34262         var items = {
34263             tag : 'div',
34264             cls : 'roo-radio-set-items'
34265         };
34266         
34267         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34268         
34269         if (align === 'left' && this.fieldLabel.length) {
34270             
34271             items = {
34272                 cls : "roo-radio-set-right", 
34273                 cn: [
34274                     items
34275                 ]
34276             };
34277             
34278             if(this.labelWidth > 12){
34279                 label.style = "width: " + this.labelWidth + 'px';
34280             }
34281             
34282             if(this.labelWidth < 13 && this.labelmd == 0){
34283                 this.labelmd = this.labelWidth;
34284             }
34285             
34286             if(this.labellg > 0){
34287                 label.cls += ' col-lg-' + this.labellg;
34288                 items.cls += ' col-lg-' + (12 - this.labellg);
34289             }
34290             
34291             if(this.labelmd > 0){
34292                 label.cls += ' col-md-' + this.labelmd;
34293                 items.cls += ' col-md-' + (12 - this.labelmd);
34294             }
34295             
34296             if(this.labelsm > 0){
34297                 label.cls += ' col-sm-' + this.labelsm;
34298                 items.cls += ' col-sm-' + (12 - this.labelsm);
34299             }
34300             
34301             if(this.labelxs > 0){
34302                 label.cls += ' col-xs-' + this.labelxs;
34303                 items.cls += ' col-xs-' + (12 - this.labelxs);
34304             }
34305         }
34306         
34307         var cfg = {
34308             tag : 'div',
34309             cls : 'roo-radio-set',
34310             cn : [
34311                 {
34312                     tag : 'input',
34313                     cls : 'roo-radio-set-input',
34314                     type : 'hidden',
34315                     name : this.name,
34316                     value : this.value ? this.value :  ''
34317                 },
34318                 label,
34319                 items
34320             ]
34321         };
34322         
34323         if(this.weight.length){
34324             cfg.cls += ' roo-radio-' + this.weight;
34325         }
34326         
34327         if(this.inline) {
34328             cfg.cls += ' roo-radio-set-inline';
34329         }
34330         
34331         var settings=this;
34332         ['xs','sm','md','lg'].map(function(size){
34333             if (settings[size]) {
34334                 cfg.cls += ' col-' + size + '-' + settings[size];
34335             }
34336         });
34337         
34338         return cfg;
34339         
34340     },
34341
34342     initEvents : function()
34343     {
34344         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34345         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34346         
34347         if(!this.fieldLabel.length){
34348             this.labelEl.hide();
34349         }
34350         
34351         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34352         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34353         
34354         this.indicator = this.indicatorEl();
34355         
34356         if(this.indicator){
34357             this.indicator.addClass('invisible');
34358         }
34359         
34360         this.originalValue = this.getValue();
34361         
34362     },
34363     
34364     inputEl: function ()
34365     {
34366         return this.el.select('.roo-radio-set-input', true).first();
34367     },
34368     
34369     getChildContainer : function()
34370     {
34371         return this.itemsEl;
34372     },
34373     
34374     register : function(item)
34375     {
34376         this.radioes.push(item);
34377         
34378     },
34379     
34380     validate : function()
34381     {   
34382         if(this.getVisibilityEl().hasClass('hidden')){
34383             return true;
34384         }
34385         
34386         var valid = false;
34387         
34388         Roo.each(this.radioes, function(i){
34389             if(!i.checked){
34390                 return;
34391             }
34392             
34393             valid = true;
34394             return false;
34395         });
34396         
34397         if(this.allowBlank) {
34398             return true;
34399         }
34400         
34401         if(this.disabled || valid){
34402             this.markValid();
34403             return true;
34404         }
34405         
34406         this.markInvalid();
34407         return false;
34408         
34409     },
34410     
34411     markValid : function()
34412     {
34413         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34414             this.indicatorEl().removeClass('visible');
34415             this.indicatorEl().addClass('invisible');
34416         }
34417         
34418         
34419         if (Roo.bootstrap.version == 3) {
34420             this.el.removeClass([this.invalidClass, this.validClass]);
34421             this.el.addClass(this.validClass);
34422         } else {
34423             this.el.removeClass(['is-invalid','is-valid']);
34424             this.el.addClass(['is-valid']);
34425         }
34426         this.fireEvent('valid', this);
34427     },
34428     
34429     markInvalid : function(msg)
34430     {
34431         if(this.allowBlank || this.disabled){
34432             return;
34433         }
34434         
34435         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34436             this.indicatorEl().removeClass('invisible');
34437             this.indicatorEl().addClass('visible');
34438         }
34439         if (Roo.bootstrap.version == 3) {
34440             this.el.removeClass([this.invalidClass, this.validClass]);
34441             this.el.addClass(this.invalidClass);
34442         } else {
34443             this.el.removeClass(['is-invalid','is-valid']);
34444             this.el.addClass(['is-invalid']);
34445         }
34446         
34447         this.fireEvent('invalid', this, msg);
34448         
34449     },
34450     
34451     setValue : function(v, suppressEvent)
34452     {   
34453         if(this.value === v){
34454             return;
34455         }
34456         
34457         this.value = v;
34458         
34459         if(this.rendered){
34460             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34461         }
34462         
34463         Roo.each(this.radioes, function(i){
34464             i.checked = false;
34465             i.el.removeClass('checked');
34466         });
34467         
34468         Roo.each(this.radioes, function(i){
34469             
34470             if(i.value === v || i.value.toString() === v.toString()){
34471                 i.checked = true;
34472                 i.el.addClass('checked');
34473                 
34474                 if(suppressEvent !== true){
34475                     this.fireEvent('check', this, i);
34476                 }
34477                 
34478                 return false;
34479             }
34480             
34481         }, this);
34482         
34483         this.validate();
34484     },
34485     
34486     clearInvalid : function(){
34487         
34488         if(!this.el || this.preventMark){
34489             return;
34490         }
34491         
34492         this.el.removeClass([this.invalidClass]);
34493         
34494         this.fireEvent('valid', this);
34495     }
34496     
34497 });
34498
34499 Roo.apply(Roo.bootstrap.RadioSet, {
34500     
34501     groups: {},
34502     
34503     register : function(set)
34504     {
34505         this.groups[set.name] = set;
34506     },
34507     
34508     get: function(name) 
34509     {
34510         if (typeof(this.groups[name]) == 'undefined') {
34511             return false;
34512         }
34513         
34514         return this.groups[name] ;
34515     }
34516     
34517 });
34518 /*
34519  * Based on:
34520  * Ext JS Library 1.1.1
34521  * Copyright(c) 2006-2007, Ext JS, LLC.
34522  *
34523  * Originally Released Under LGPL - original licence link has changed is not relivant.
34524  *
34525  * Fork - LGPL
34526  * <script type="text/javascript">
34527  */
34528
34529
34530 /**
34531  * @class Roo.bootstrap.SplitBar
34532  * @extends Roo.util.Observable
34533  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34534  * <br><br>
34535  * Usage:
34536  * <pre><code>
34537 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34538                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34539 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34540 split.minSize = 100;
34541 split.maxSize = 600;
34542 split.animate = true;
34543 split.on('moved', splitterMoved);
34544 </code></pre>
34545  * @constructor
34546  * Create a new SplitBar
34547  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34548  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34549  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34550  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34551                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34552                         position of the SplitBar).
34553  */
34554 Roo.bootstrap.SplitBar = function(cfg){
34555     
34556     /** @private */
34557     
34558     //{
34559     //  dragElement : elm
34560     //  resizingElement: el,
34561         // optional..
34562     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34563     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34564         // existingProxy ???
34565     //}
34566     
34567     this.el = Roo.get(cfg.dragElement, true);
34568     this.el.dom.unselectable = "on";
34569     /** @private */
34570     this.resizingEl = Roo.get(cfg.resizingElement, true);
34571
34572     /**
34573      * @private
34574      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34575      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34576      * @type Number
34577      */
34578     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34579     
34580     /**
34581      * The minimum size of the resizing element. (Defaults to 0)
34582      * @type Number
34583      */
34584     this.minSize = 0;
34585     
34586     /**
34587      * The maximum size of the resizing element. (Defaults to 2000)
34588      * @type Number
34589      */
34590     this.maxSize = 2000;
34591     
34592     /**
34593      * Whether to animate the transition to the new size
34594      * @type Boolean
34595      */
34596     this.animate = false;
34597     
34598     /**
34599      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34600      * @type Boolean
34601      */
34602     this.useShim = false;
34603     
34604     /** @private */
34605     this.shim = null;
34606     
34607     if(!cfg.existingProxy){
34608         /** @private */
34609         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34610     }else{
34611         this.proxy = Roo.get(cfg.existingProxy).dom;
34612     }
34613     /** @private */
34614     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34615     
34616     /** @private */
34617     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34618     
34619     /** @private */
34620     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34621     
34622     /** @private */
34623     this.dragSpecs = {};
34624     
34625     /**
34626      * @private The adapter to use to positon and resize elements
34627      */
34628     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34629     this.adapter.init(this);
34630     
34631     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34632         /** @private */
34633         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34634         this.el.addClass("roo-splitbar-h");
34635     }else{
34636         /** @private */
34637         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34638         this.el.addClass("roo-splitbar-v");
34639     }
34640     
34641     this.addEvents({
34642         /**
34643          * @event resize
34644          * Fires when the splitter is moved (alias for {@link #event-moved})
34645          * @param {Roo.bootstrap.SplitBar} this
34646          * @param {Number} newSize the new width or height
34647          */
34648         "resize" : true,
34649         /**
34650          * @event moved
34651          * Fires when the splitter is moved
34652          * @param {Roo.bootstrap.SplitBar} this
34653          * @param {Number} newSize the new width or height
34654          */
34655         "moved" : true,
34656         /**
34657          * @event beforeresize
34658          * Fires before the splitter is dragged
34659          * @param {Roo.bootstrap.SplitBar} this
34660          */
34661         "beforeresize" : true,
34662
34663         "beforeapply" : true
34664     });
34665
34666     Roo.util.Observable.call(this);
34667 };
34668
34669 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34670     onStartProxyDrag : function(x, y){
34671         this.fireEvent("beforeresize", this);
34672         if(!this.overlay){
34673             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34674             o.unselectable();
34675             o.enableDisplayMode("block");
34676             // all splitbars share the same overlay
34677             Roo.bootstrap.SplitBar.prototype.overlay = o;
34678         }
34679         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34680         this.overlay.show();
34681         Roo.get(this.proxy).setDisplayed("block");
34682         var size = this.adapter.getElementSize(this);
34683         this.activeMinSize = this.getMinimumSize();;
34684         this.activeMaxSize = this.getMaximumSize();;
34685         var c1 = size - this.activeMinSize;
34686         var c2 = Math.max(this.activeMaxSize - size, 0);
34687         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34688             this.dd.resetConstraints();
34689             this.dd.setXConstraint(
34690                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34691                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34692             );
34693             this.dd.setYConstraint(0, 0);
34694         }else{
34695             this.dd.resetConstraints();
34696             this.dd.setXConstraint(0, 0);
34697             this.dd.setYConstraint(
34698                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34699                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34700             );
34701          }
34702         this.dragSpecs.startSize = size;
34703         this.dragSpecs.startPoint = [x, y];
34704         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34705     },
34706     
34707     /** 
34708      * @private Called after the drag operation by the DDProxy
34709      */
34710     onEndProxyDrag : function(e){
34711         Roo.get(this.proxy).setDisplayed(false);
34712         var endPoint = Roo.lib.Event.getXY(e);
34713         if(this.overlay){
34714             this.overlay.hide();
34715         }
34716         var newSize;
34717         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34718             newSize = this.dragSpecs.startSize + 
34719                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34720                     endPoint[0] - this.dragSpecs.startPoint[0] :
34721                     this.dragSpecs.startPoint[0] - endPoint[0]
34722                 );
34723         }else{
34724             newSize = this.dragSpecs.startSize + 
34725                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34726                     endPoint[1] - this.dragSpecs.startPoint[1] :
34727                     this.dragSpecs.startPoint[1] - endPoint[1]
34728                 );
34729         }
34730         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34731         if(newSize != this.dragSpecs.startSize){
34732             if(this.fireEvent('beforeapply', this, newSize) !== false){
34733                 this.adapter.setElementSize(this, newSize);
34734                 this.fireEvent("moved", this, newSize);
34735                 this.fireEvent("resize", this, newSize);
34736             }
34737         }
34738     },
34739     
34740     /**
34741      * Get the adapter this SplitBar uses
34742      * @return The adapter object
34743      */
34744     getAdapter : function(){
34745         return this.adapter;
34746     },
34747     
34748     /**
34749      * Set the adapter this SplitBar uses
34750      * @param {Object} adapter A SplitBar adapter object
34751      */
34752     setAdapter : function(adapter){
34753         this.adapter = adapter;
34754         this.adapter.init(this);
34755     },
34756     
34757     /**
34758      * Gets the minimum size for the resizing element
34759      * @return {Number} The minimum size
34760      */
34761     getMinimumSize : function(){
34762         return this.minSize;
34763     },
34764     
34765     /**
34766      * Sets the minimum size for the resizing element
34767      * @param {Number} minSize The minimum size
34768      */
34769     setMinimumSize : function(minSize){
34770         this.minSize = minSize;
34771     },
34772     
34773     /**
34774      * Gets the maximum size for the resizing element
34775      * @return {Number} The maximum size
34776      */
34777     getMaximumSize : function(){
34778         return this.maxSize;
34779     },
34780     
34781     /**
34782      * Sets the maximum size for the resizing element
34783      * @param {Number} maxSize The maximum size
34784      */
34785     setMaximumSize : function(maxSize){
34786         this.maxSize = maxSize;
34787     },
34788     
34789     /**
34790      * Sets the initialize size for the resizing element
34791      * @param {Number} size The initial size
34792      */
34793     setCurrentSize : function(size){
34794         var oldAnimate = this.animate;
34795         this.animate = false;
34796         this.adapter.setElementSize(this, size);
34797         this.animate = oldAnimate;
34798     },
34799     
34800     /**
34801      * Destroy this splitbar. 
34802      * @param {Boolean} removeEl True to remove the element
34803      */
34804     destroy : function(removeEl){
34805         if(this.shim){
34806             this.shim.remove();
34807         }
34808         this.dd.unreg();
34809         this.proxy.parentNode.removeChild(this.proxy);
34810         if(removeEl){
34811             this.el.remove();
34812         }
34813     }
34814 });
34815
34816 /**
34817  * @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.
34818  */
34819 Roo.bootstrap.SplitBar.createProxy = function(dir){
34820     var proxy = new Roo.Element(document.createElement("div"));
34821     proxy.unselectable();
34822     var cls = 'roo-splitbar-proxy';
34823     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34824     document.body.appendChild(proxy.dom);
34825     return proxy.dom;
34826 };
34827
34828 /** 
34829  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34830  * Default Adapter. It assumes the splitter and resizing element are not positioned
34831  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34832  */
34833 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34834 };
34835
34836 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34837     // do nothing for now
34838     init : function(s){
34839     
34840     },
34841     /**
34842      * Called before drag operations to get the current size of the resizing element. 
34843      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34844      */
34845      getElementSize : function(s){
34846         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34847             return s.resizingEl.getWidth();
34848         }else{
34849             return s.resizingEl.getHeight();
34850         }
34851     },
34852     
34853     /**
34854      * Called after drag operations to set the size of the resizing element.
34855      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34856      * @param {Number} newSize The new size to set
34857      * @param {Function} onComplete A function to be invoked when resizing is complete
34858      */
34859     setElementSize : function(s, newSize, onComplete){
34860         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34861             if(!s.animate){
34862                 s.resizingEl.setWidth(newSize);
34863                 if(onComplete){
34864                     onComplete(s, newSize);
34865                 }
34866             }else{
34867                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34868             }
34869         }else{
34870             
34871             if(!s.animate){
34872                 s.resizingEl.setHeight(newSize);
34873                 if(onComplete){
34874                     onComplete(s, newSize);
34875                 }
34876             }else{
34877                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34878             }
34879         }
34880     }
34881 };
34882
34883 /** 
34884  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34885  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34886  * Adapter that  moves the splitter element to align with the resized sizing element. 
34887  * Used with an absolute positioned SplitBar.
34888  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34889  * document.body, make sure you assign an id to the body element.
34890  */
34891 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34892     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34893     this.container = Roo.get(container);
34894 };
34895
34896 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34897     init : function(s){
34898         this.basic.init(s);
34899     },
34900     
34901     getElementSize : function(s){
34902         return this.basic.getElementSize(s);
34903     },
34904     
34905     setElementSize : function(s, newSize, onComplete){
34906         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34907     },
34908     
34909     moveSplitter : function(s){
34910         var yes = Roo.bootstrap.SplitBar;
34911         switch(s.placement){
34912             case yes.LEFT:
34913                 s.el.setX(s.resizingEl.getRight());
34914                 break;
34915             case yes.RIGHT:
34916                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34917                 break;
34918             case yes.TOP:
34919                 s.el.setY(s.resizingEl.getBottom());
34920                 break;
34921             case yes.BOTTOM:
34922                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34923                 break;
34924         }
34925     }
34926 };
34927
34928 /**
34929  * Orientation constant - Create a vertical SplitBar
34930  * @static
34931  * @type Number
34932  */
34933 Roo.bootstrap.SplitBar.VERTICAL = 1;
34934
34935 /**
34936  * Orientation constant - Create a horizontal SplitBar
34937  * @static
34938  * @type Number
34939  */
34940 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34941
34942 /**
34943  * Placement constant - The resizing element is to the left of the splitter element
34944  * @static
34945  * @type Number
34946  */
34947 Roo.bootstrap.SplitBar.LEFT = 1;
34948
34949 /**
34950  * Placement constant - The resizing element is to the right of the splitter element
34951  * @static
34952  * @type Number
34953  */
34954 Roo.bootstrap.SplitBar.RIGHT = 2;
34955
34956 /**
34957  * Placement constant - The resizing element is positioned above the splitter element
34958  * @static
34959  * @type Number
34960  */
34961 Roo.bootstrap.SplitBar.TOP = 3;
34962
34963 /**
34964  * Placement constant - The resizing element is positioned under splitter element
34965  * @static
34966  * @type Number
34967  */
34968 Roo.bootstrap.SplitBar.BOTTOM = 4;
34969 Roo.namespace("Roo.bootstrap.layout");/*
34970  * Based on:
34971  * Ext JS Library 1.1.1
34972  * Copyright(c) 2006-2007, Ext JS, LLC.
34973  *
34974  * Originally Released Under LGPL - original licence link has changed is not relivant.
34975  *
34976  * Fork - LGPL
34977  * <script type="text/javascript">
34978  */
34979
34980 /**
34981  * @class Roo.bootstrap.layout.Manager
34982  * @extends Roo.bootstrap.Component
34983  * Base class for layout managers.
34984  */
34985 Roo.bootstrap.layout.Manager = function(config)
34986 {
34987     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34988
34989
34990
34991
34992
34993     /** false to disable window resize monitoring @type Boolean */
34994     this.monitorWindowResize = true;
34995     this.regions = {};
34996     this.addEvents({
34997         /**
34998          * @event layout
34999          * Fires when a layout is performed.
35000          * @param {Roo.LayoutManager} this
35001          */
35002         "layout" : true,
35003         /**
35004          * @event regionresized
35005          * Fires when the user resizes a region.
35006          * @param {Roo.LayoutRegion} region The resized region
35007          * @param {Number} newSize The new size (width for east/west, height for north/south)
35008          */
35009         "regionresized" : true,
35010         /**
35011          * @event regioncollapsed
35012          * Fires when a region is collapsed.
35013          * @param {Roo.LayoutRegion} region The collapsed region
35014          */
35015         "regioncollapsed" : true,
35016         /**
35017          * @event regionexpanded
35018          * Fires when a region is expanded.
35019          * @param {Roo.LayoutRegion} region The expanded region
35020          */
35021         "regionexpanded" : true
35022     });
35023     this.updating = false;
35024
35025     if (config.el) {
35026         this.el = Roo.get(config.el);
35027         this.initEvents();
35028     }
35029
35030 };
35031
35032 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35033
35034
35035     regions : null,
35036
35037     monitorWindowResize : true,
35038
35039
35040     updating : false,
35041
35042
35043     onRender : function(ct, position)
35044     {
35045         if(!this.el){
35046             this.el = Roo.get(ct);
35047             this.initEvents();
35048         }
35049         //this.fireEvent('render',this);
35050     },
35051
35052
35053     initEvents: function()
35054     {
35055
35056
35057         // ie scrollbar fix
35058         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35059             document.body.scroll = "no";
35060         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35061             this.el.position('relative');
35062         }
35063         this.id = this.el.id;
35064         this.el.addClass("roo-layout-container");
35065         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35066         if(this.el.dom != document.body ) {
35067             this.el.on('resize', this.layout,this);
35068             this.el.on('show', this.layout,this);
35069         }
35070
35071     },
35072
35073     /**
35074      * Returns true if this layout is currently being updated
35075      * @return {Boolean}
35076      */
35077     isUpdating : function(){
35078         return this.updating;
35079     },
35080
35081     /**
35082      * Suspend the LayoutManager from doing auto-layouts while
35083      * making multiple add or remove calls
35084      */
35085     beginUpdate : function(){
35086         this.updating = true;
35087     },
35088
35089     /**
35090      * Restore auto-layouts and optionally disable the manager from performing a layout
35091      * @param {Boolean} noLayout true to disable a layout update
35092      */
35093     endUpdate : function(noLayout){
35094         this.updating = false;
35095         if(!noLayout){
35096             this.layout();
35097         }
35098     },
35099
35100     layout: function(){
35101         // abstract...
35102     },
35103
35104     onRegionResized : function(region, newSize){
35105         this.fireEvent("regionresized", region, newSize);
35106         this.layout();
35107     },
35108
35109     onRegionCollapsed : function(region){
35110         this.fireEvent("regioncollapsed", region);
35111     },
35112
35113     onRegionExpanded : function(region){
35114         this.fireEvent("regionexpanded", region);
35115     },
35116
35117     /**
35118      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35119      * performs box-model adjustments.
35120      * @return {Object} The size as an object {width: (the width), height: (the height)}
35121      */
35122     getViewSize : function()
35123     {
35124         var size;
35125         if(this.el.dom != document.body){
35126             size = this.el.getSize();
35127         }else{
35128             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35129         }
35130         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35131         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35132         return size;
35133     },
35134
35135     /**
35136      * Returns the Element this layout is bound to.
35137      * @return {Roo.Element}
35138      */
35139     getEl : function(){
35140         return this.el;
35141     },
35142
35143     /**
35144      * Returns the specified region.
35145      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35146      * @return {Roo.LayoutRegion}
35147      */
35148     getRegion : function(target){
35149         return this.regions[target.toLowerCase()];
35150     },
35151
35152     onWindowResize : function(){
35153         if(this.monitorWindowResize){
35154             this.layout();
35155         }
35156     }
35157 });
35158 /*
35159  * Based on:
35160  * Ext JS Library 1.1.1
35161  * Copyright(c) 2006-2007, Ext JS, LLC.
35162  *
35163  * Originally Released Under LGPL - original licence link has changed is not relivant.
35164  *
35165  * Fork - LGPL
35166  * <script type="text/javascript">
35167  */
35168 /**
35169  * @class Roo.bootstrap.layout.Border
35170  * @extends Roo.bootstrap.layout.Manager
35171  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35172  * please see: examples/bootstrap/nested.html<br><br>
35173  
35174 <b>The container the layout is rendered into can be either the body element or any other element.
35175 If it is not the body element, the container needs to either be an absolute positioned element,
35176 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35177 the container size if it is not the body element.</b>
35178
35179 * @constructor
35180 * Create a new Border
35181 * @param {Object} config Configuration options
35182  */
35183 Roo.bootstrap.layout.Border = function(config){
35184     config = config || {};
35185     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35186     
35187     
35188     
35189     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35190         if(config[region]){
35191             config[region].region = region;
35192             this.addRegion(config[region]);
35193         }
35194     },this);
35195     
35196 };
35197
35198 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35199
35200 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35201     
35202     parent : false, // this might point to a 'nest' or a ???
35203     
35204     /**
35205      * Creates and adds a new region if it doesn't already exist.
35206      * @param {String} target The target region key (north, south, east, west or center).
35207      * @param {Object} config The regions config object
35208      * @return {BorderLayoutRegion} The new region
35209      */
35210     addRegion : function(config)
35211     {
35212         if(!this.regions[config.region]){
35213             var r = this.factory(config);
35214             this.bindRegion(r);
35215         }
35216         return this.regions[config.region];
35217     },
35218
35219     // private (kinda)
35220     bindRegion : function(r){
35221         this.regions[r.config.region] = r;
35222         
35223         r.on("visibilitychange",    this.layout, this);
35224         r.on("paneladded",          this.layout, this);
35225         r.on("panelremoved",        this.layout, this);
35226         r.on("invalidated",         this.layout, this);
35227         r.on("resized",             this.onRegionResized, this);
35228         r.on("collapsed",           this.onRegionCollapsed, this);
35229         r.on("expanded",            this.onRegionExpanded, this);
35230     },
35231
35232     /**
35233      * Performs a layout update.
35234      */
35235     layout : function()
35236     {
35237         if(this.updating) {
35238             return;
35239         }
35240         
35241         // render all the rebions if they have not been done alreayd?
35242         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35243             if(this.regions[region] && !this.regions[region].bodyEl){
35244                 this.regions[region].onRender(this.el)
35245             }
35246         },this);
35247         
35248         var size = this.getViewSize();
35249         var w = size.width;
35250         var h = size.height;
35251         var centerW = w;
35252         var centerH = h;
35253         var centerY = 0;
35254         var centerX = 0;
35255         //var x = 0, y = 0;
35256
35257         var rs = this.regions;
35258         var north = rs["north"];
35259         var south = rs["south"]; 
35260         var west = rs["west"];
35261         var east = rs["east"];
35262         var center = rs["center"];
35263         //if(this.hideOnLayout){ // not supported anymore
35264             //c.el.setStyle("display", "none");
35265         //}
35266         if(north && north.isVisible()){
35267             var b = north.getBox();
35268             var m = north.getMargins();
35269             b.width = w - (m.left+m.right);
35270             b.x = m.left;
35271             b.y = m.top;
35272             centerY = b.height + b.y + m.bottom;
35273             centerH -= centerY;
35274             north.updateBox(this.safeBox(b));
35275         }
35276         if(south && south.isVisible()){
35277             var b = south.getBox();
35278             var m = south.getMargins();
35279             b.width = w - (m.left+m.right);
35280             b.x = m.left;
35281             var totalHeight = (b.height + m.top + m.bottom);
35282             b.y = h - totalHeight + m.top;
35283             centerH -= totalHeight;
35284             south.updateBox(this.safeBox(b));
35285         }
35286         if(west && west.isVisible()){
35287             var b = west.getBox();
35288             var m = west.getMargins();
35289             b.height = centerH - (m.top+m.bottom);
35290             b.x = m.left;
35291             b.y = centerY + m.top;
35292             var totalWidth = (b.width + m.left + m.right);
35293             centerX += totalWidth;
35294             centerW -= totalWidth;
35295             west.updateBox(this.safeBox(b));
35296         }
35297         if(east && east.isVisible()){
35298             var b = east.getBox();
35299             var m = east.getMargins();
35300             b.height = centerH - (m.top+m.bottom);
35301             var totalWidth = (b.width + m.left + m.right);
35302             b.x = w - totalWidth + m.left;
35303             b.y = centerY + m.top;
35304             centerW -= totalWidth;
35305             east.updateBox(this.safeBox(b));
35306         }
35307         if(center){
35308             var m = center.getMargins();
35309             var centerBox = {
35310                 x: centerX + m.left,
35311                 y: centerY + m.top,
35312                 width: centerW - (m.left+m.right),
35313                 height: centerH - (m.top+m.bottom)
35314             };
35315             //if(this.hideOnLayout){
35316                 //center.el.setStyle("display", "block");
35317             //}
35318             center.updateBox(this.safeBox(centerBox));
35319         }
35320         this.el.repaint();
35321         this.fireEvent("layout", this);
35322     },
35323
35324     // private
35325     safeBox : function(box){
35326         box.width = Math.max(0, box.width);
35327         box.height = Math.max(0, box.height);
35328         return box;
35329     },
35330
35331     /**
35332      * Adds a ContentPanel (or subclass) to this layout.
35333      * @param {String} target The target region key (north, south, east, west or center).
35334      * @param {Roo.ContentPanel} panel The panel to add
35335      * @return {Roo.ContentPanel} The added panel
35336      */
35337     add : function(target, panel){
35338          
35339         target = target.toLowerCase();
35340         return this.regions[target].add(panel);
35341     },
35342
35343     /**
35344      * Remove a ContentPanel (or subclass) to this layout.
35345      * @param {String} target The target region key (north, south, east, west or center).
35346      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35347      * @return {Roo.ContentPanel} The removed panel
35348      */
35349     remove : function(target, panel){
35350         target = target.toLowerCase();
35351         return this.regions[target].remove(panel);
35352     },
35353
35354     /**
35355      * Searches all regions for a panel with the specified id
35356      * @param {String} panelId
35357      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35358      */
35359     findPanel : function(panelId){
35360         var rs = this.regions;
35361         for(var target in rs){
35362             if(typeof rs[target] != "function"){
35363                 var p = rs[target].getPanel(panelId);
35364                 if(p){
35365                     return p;
35366                 }
35367             }
35368         }
35369         return null;
35370     },
35371
35372     /**
35373      * Searches all regions for a panel with the specified id and activates (shows) it.
35374      * @param {String/ContentPanel} panelId The panels id or the panel itself
35375      * @return {Roo.ContentPanel} The shown panel or null
35376      */
35377     showPanel : function(panelId) {
35378       var rs = this.regions;
35379       for(var target in rs){
35380          var r = rs[target];
35381          if(typeof r != "function"){
35382             if(r.hasPanel(panelId)){
35383                return r.showPanel(panelId);
35384             }
35385          }
35386       }
35387       return null;
35388    },
35389
35390    /**
35391      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35392      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35393      */
35394    /*
35395     restoreState : function(provider){
35396         if(!provider){
35397             provider = Roo.state.Manager;
35398         }
35399         var sm = new Roo.LayoutStateManager();
35400         sm.init(this, provider);
35401     },
35402 */
35403  
35404  
35405     /**
35406      * Adds a xtype elements to the layout.
35407      * <pre><code>
35408
35409 layout.addxtype({
35410        xtype : 'ContentPanel',
35411        region: 'west',
35412        items: [ .... ]
35413    }
35414 );
35415
35416 layout.addxtype({
35417         xtype : 'NestedLayoutPanel',
35418         region: 'west',
35419         layout: {
35420            center: { },
35421            west: { }   
35422         },
35423         items : [ ... list of content panels or nested layout panels.. ]
35424    }
35425 );
35426 </code></pre>
35427      * @param {Object} cfg Xtype definition of item to add.
35428      */
35429     addxtype : function(cfg)
35430     {
35431         // basically accepts a pannel...
35432         // can accept a layout region..!?!?
35433         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35434         
35435         
35436         // theory?  children can only be panels??
35437         
35438         //if (!cfg.xtype.match(/Panel$/)) {
35439         //    return false;
35440         //}
35441         var ret = false;
35442         
35443         if (typeof(cfg.region) == 'undefined') {
35444             Roo.log("Failed to add Panel, region was not set");
35445             Roo.log(cfg);
35446             return false;
35447         }
35448         var region = cfg.region;
35449         delete cfg.region;
35450         
35451           
35452         var xitems = [];
35453         if (cfg.items) {
35454             xitems = cfg.items;
35455             delete cfg.items;
35456         }
35457         var nb = false;
35458         
35459         if ( region == 'center') {
35460             Roo.log("Center: " + cfg.title);
35461         }
35462         
35463         
35464         switch(cfg.xtype) 
35465         {
35466             case 'Content':  // ContentPanel (el, cfg)
35467             case 'Scroll':  // ContentPanel (el, cfg)
35468             case 'View': 
35469                 cfg.autoCreate = cfg.autoCreate || true;
35470                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35471                 //} else {
35472                 //    var el = this.el.createChild();
35473                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35474                 //}
35475                 
35476                 this.add(region, ret);
35477                 break;
35478             
35479             /*
35480             case 'TreePanel': // our new panel!
35481                 cfg.el = this.el.createChild();
35482                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35483                 this.add(region, ret);
35484                 break;
35485             */
35486             
35487             case 'Nest': 
35488                 // create a new Layout (which is  a Border Layout...
35489                 
35490                 var clayout = cfg.layout;
35491                 clayout.el  = this.el.createChild();
35492                 clayout.items   = clayout.items  || [];
35493                 
35494                 delete cfg.layout;
35495                 
35496                 // replace this exitems with the clayout ones..
35497                 xitems = clayout.items;
35498                  
35499                 // force background off if it's in center...
35500                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35501                     cfg.background = false;
35502                 }
35503                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35504                 
35505                 
35506                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35507                 //console.log('adding nested layout panel '  + cfg.toSource());
35508                 this.add(region, ret);
35509                 nb = {}; /// find first...
35510                 break;
35511             
35512             case 'Grid':
35513                 
35514                 // needs grid and region
35515                 
35516                 //var el = this.getRegion(region).el.createChild();
35517                 /*
35518                  *var el = this.el.createChild();
35519                 // create the grid first...
35520                 cfg.grid.container = el;
35521                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35522                 */
35523                 
35524                 if (region == 'center' && this.active ) {
35525                     cfg.background = false;
35526                 }
35527                 
35528                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35529                 
35530                 this.add(region, ret);
35531                 /*
35532                 if (cfg.background) {
35533                     // render grid on panel activation (if panel background)
35534                     ret.on('activate', function(gp) {
35535                         if (!gp.grid.rendered) {
35536                     //        gp.grid.render(el);
35537                         }
35538                     });
35539                 } else {
35540                   //  cfg.grid.render(el);
35541                 }
35542                 */
35543                 break;
35544            
35545            
35546             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35547                 // it was the old xcomponent building that caused this before.
35548                 // espeically if border is the top element in the tree.
35549                 ret = this;
35550                 break; 
35551                 
35552                     
35553                 
35554                 
35555                 
35556             default:
35557                 /*
35558                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35559                     
35560                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35561                     this.add(region, ret);
35562                 } else {
35563                 */
35564                     Roo.log(cfg);
35565                     throw "Can not add '" + cfg.xtype + "' to Border";
35566                     return null;
35567              
35568                                 
35569              
35570         }
35571         this.beginUpdate();
35572         // add children..
35573         var region = '';
35574         var abn = {};
35575         Roo.each(xitems, function(i)  {
35576             region = nb && i.region ? i.region : false;
35577             
35578             var add = ret.addxtype(i);
35579            
35580             if (region) {
35581                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35582                 if (!i.background) {
35583                     abn[region] = nb[region] ;
35584                 }
35585             }
35586             
35587         });
35588         this.endUpdate();
35589
35590         // make the last non-background panel active..
35591         //if (nb) { Roo.log(abn); }
35592         if (nb) {
35593             
35594             for(var r in abn) {
35595                 region = this.getRegion(r);
35596                 if (region) {
35597                     // tried using nb[r], but it does not work..
35598                      
35599                     region.showPanel(abn[r]);
35600                    
35601                 }
35602             }
35603         }
35604         return ret;
35605         
35606     },
35607     
35608     
35609 // private
35610     factory : function(cfg)
35611     {
35612         
35613         var validRegions = Roo.bootstrap.layout.Border.regions;
35614
35615         var target = cfg.region;
35616         cfg.mgr = this;
35617         
35618         var r = Roo.bootstrap.layout;
35619         Roo.log(target);
35620         switch(target){
35621             case "north":
35622                 return new r.North(cfg);
35623             case "south":
35624                 return new r.South(cfg);
35625             case "east":
35626                 return new r.East(cfg);
35627             case "west":
35628                 return new r.West(cfg);
35629             case "center":
35630                 return new r.Center(cfg);
35631         }
35632         throw 'Layout region "'+target+'" not supported.';
35633     }
35634     
35635     
35636 });
35637  /*
35638  * Based on:
35639  * Ext JS Library 1.1.1
35640  * Copyright(c) 2006-2007, Ext JS, LLC.
35641  *
35642  * Originally Released Under LGPL - original licence link has changed is not relivant.
35643  *
35644  * Fork - LGPL
35645  * <script type="text/javascript">
35646  */
35647  
35648 /**
35649  * @class Roo.bootstrap.layout.Basic
35650  * @extends Roo.util.Observable
35651  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35652  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35653  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35654  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35655  * @cfg {string}   region  the region that it inhabits..
35656  * @cfg {bool}   skipConfig skip config?
35657  * 
35658
35659  */
35660 Roo.bootstrap.layout.Basic = function(config){
35661     
35662     this.mgr = config.mgr;
35663     
35664     this.position = config.region;
35665     
35666     var skipConfig = config.skipConfig;
35667     
35668     this.events = {
35669         /**
35670          * @scope Roo.BasicLayoutRegion
35671          */
35672         
35673         /**
35674          * @event beforeremove
35675          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35676          * @param {Roo.LayoutRegion} this
35677          * @param {Roo.ContentPanel} panel The panel
35678          * @param {Object} e The cancel event object
35679          */
35680         "beforeremove" : true,
35681         /**
35682          * @event invalidated
35683          * Fires when the layout for this region is changed.
35684          * @param {Roo.LayoutRegion} this
35685          */
35686         "invalidated" : true,
35687         /**
35688          * @event visibilitychange
35689          * Fires when this region is shown or hidden 
35690          * @param {Roo.LayoutRegion} this
35691          * @param {Boolean} visibility true or false
35692          */
35693         "visibilitychange" : true,
35694         /**
35695          * @event paneladded
35696          * Fires when a panel is added. 
35697          * @param {Roo.LayoutRegion} this
35698          * @param {Roo.ContentPanel} panel The panel
35699          */
35700         "paneladded" : true,
35701         /**
35702          * @event panelremoved
35703          * Fires when a panel is removed. 
35704          * @param {Roo.LayoutRegion} this
35705          * @param {Roo.ContentPanel} panel The panel
35706          */
35707         "panelremoved" : true,
35708         /**
35709          * @event beforecollapse
35710          * Fires when this region before collapse.
35711          * @param {Roo.LayoutRegion} this
35712          */
35713         "beforecollapse" : true,
35714         /**
35715          * @event collapsed
35716          * Fires when this region is collapsed.
35717          * @param {Roo.LayoutRegion} this
35718          */
35719         "collapsed" : true,
35720         /**
35721          * @event expanded
35722          * Fires when this region is expanded.
35723          * @param {Roo.LayoutRegion} this
35724          */
35725         "expanded" : true,
35726         /**
35727          * @event slideshow
35728          * Fires when this region is slid into view.
35729          * @param {Roo.LayoutRegion} this
35730          */
35731         "slideshow" : true,
35732         /**
35733          * @event slidehide
35734          * Fires when this region slides out of view. 
35735          * @param {Roo.LayoutRegion} this
35736          */
35737         "slidehide" : true,
35738         /**
35739          * @event panelactivated
35740          * Fires when a panel is activated. 
35741          * @param {Roo.LayoutRegion} this
35742          * @param {Roo.ContentPanel} panel The activated panel
35743          */
35744         "panelactivated" : true,
35745         /**
35746          * @event resized
35747          * Fires when the user resizes this region. 
35748          * @param {Roo.LayoutRegion} this
35749          * @param {Number} newSize The new size (width for east/west, height for north/south)
35750          */
35751         "resized" : true
35752     };
35753     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35754     this.panels = new Roo.util.MixedCollection();
35755     this.panels.getKey = this.getPanelId.createDelegate(this);
35756     this.box = null;
35757     this.activePanel = null;
35758     // ensure listeners are added...
35759     
35760     if (config.listeners || config.events) {
35761         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35762             listeners : config.listeners || {},
35763             events : config.events || {}
35764         });
35765     }
35766     
35767     if(skipConfig !== true){
35768         this.applyConfig(config);
35769     }
35770 };
35771
35772 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35773 {
35774     getPanelId : function(p){
35775         return p.getId();
35776     },
35777     
35778     applyConfig : function(config){
35779         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35780         this.config = config;
35781         
35782     },
35783     
35784     /**
35785      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35786      * the width, for horizontal (north, south) the height.
35787      * @param {Number} newSize The new width or height
35788      */
35789     resizeTo : function(newSize){
35790         var el = this.el ? this.el :
35791                  (this.activePanel ? this.activePanel.getEl() : null);
35792         if(el){
35793             switch(this.position){
35794                 case "east":
35795                 case "west":
35796                     el.setWidth(newSize);
35797                     this.fireEvent("resized", this, newSize);
35798                 break;
35799                 case "north":
35800                 case "south":
35801                     el.setHeight(newSize);
35802                     this.fireEvent("resized", this, newSize);
35803                 break;                
35804             }
35805         }
35806     },
35807     
35808     getBox : function(){
35809         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35810     },
35811     
35812     getMargins : function(){
35813         return this.margins;
35814     },
35815     
35816     updateBox : function(box){
35817         this.box = box;
35818         var el = this.activePanel.getEl();
35819         el.dom.style.left = box.x + "px";
35820         el.dom.style.top = box.y + "px";
35821         this.activePanel.setSize(box.width, box.height);
35822     },
35823     
35824     /**
35825      * Returns the container element for this region.
35826      * @return {Roo.Element}
35827      */
35828     getEl : function(){
35829         return this.activePanel;
35830     },
35831     
35832     /**
35833      * Returns true if this region is currently visible.
35834      * @return {Boolean}
35835      */
35836     isVisible : function(){
35837         return this.activePanel ? true : false;
35838     },
35839     
35840     setActivePanel : function(panel){
35841         panel = this.getPanel(panel);
35842         if(this.activePanel && this.activePanel != panel){
35843             this.activePanel.setActiveState(false);
35844             this.activePanel.getEl().setLeftTop(-10000,-10000);
35845         }
35846         this.activePanel = panel;
35847         panel.setActiveState(true);
35848         if(this.box){
35849             panel.setSize(this.box.width, this.box.height);
35850         }
35851         this.fireEvent("panelactivated", this, panel);
35852         this.fireEvent("invalidated");
35853     },
35854     
35855     /**
35856      * Show the specified panel.
35857      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35858      * @return {Roo.ContentPanel} The shown panel or null
35859      */
35860     showPanel : function(panel){
35861         panel = this.getPanel(panel);
35862         if(panel){
35863             this.setActivePanel(panel);
35864         }
35865         return panel;
35866     },
35867     
35868     /**
35869      * Get the active panel for this region.
35870      * @return {Roo.ContentPanel} The active panel or null
35871      */
35872     getActivePanel : function(){
35873         return this.activePanel;
35874     },
35875     
35876     /**
35877      * Add the passed ContentPanel(s)
35878      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35879      * @return {Roo.ContentPanel} The panel added (if only one was added)
35880      */
35881     add : function(panel){
35882         if(arguments.length > 1){
35883             for(var i = 0, len = arguments.length; i < len; i++) {
35884                 this.add(arguments[i]);
35885             }
35886             return null;
35887         }
35888         if(this.hasPanel(panel)){
35889             this.showPanel(panel);
35890             return panel;
35891         }
35892         var el = panel.getEl();
35893         if(el.dom.parentNode != this.mgr.el.dom){
35894             this.mgr.el.dom.appendChild(el.dom);
35895         }
35896         if(panel.setRegion){
35897             panel.setRegion(this);
35898         }
35899         this.panels.add(panel);
35900         el.setStyle("position", "absolute");
35901         if(!panel.background){
35902             this.setActivePanel(panel);
35903             if(this.config.initialSize && this.panels.getCount()==1){
35904                 this.resizeTo(this.config.initialSize);
35905             }
35906         }
35907         this.fireEvent("paneladded", this, panel);
35908         return panel;
35909     },
35910     
35911     /**
35912      * Returns true if the panel is in this region.
35913      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35914      * @return {Boolean}
35915      */
35916     hasPanel : function(panel){
35917         if(typeof panel == "object"){ // must be panel obj
35918             panel = panel.getId();
35919         }
35920         return this.getPanel(panel) ? true : false;
35921     },
35922     
35923     /**
35924      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35925      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35926      * @param {Boolean} preservePanel Overrides the config preservePanel option
35927      * @return {Roo.ContentPanel} The panel that was removed
35928      */
35929     remove : function(panel, preservePanel){
35930         panel = this.getPanel(panel);
35931         if(!panel){
35932             return null;
35933         }
35934         var e = {};
35935         this.fireEvent("beforeremove", this, panel, e);
35936         if(e.cancel === true){
35937             return null;
35938         }
35939         var panelId = panel.getId();
35940         this.panels.removeKey(panelId);
35941         return panel;
35942     },
35943     
35944     /**
35945      * Returns the panel specified or null if it's not in this region.
35946      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35947      * @return {Roo.ContentPanel}
35948      */
35949     getPanel : function(id){
35950         if(typeof id == "object"){ // must be panel obj
35951             return id;
35952         }
35953         return this.panels.get(id);
35954     },
35955     
35956     /**
35957      * Returns this regions position (north/south/east/west/center).
35958      * @return {String} 
35959      */
35960     getPosition: function(){
35961         return this.position;    
35962     }
35963 });/*
35964  * Based on:
35965  * Ext JS Library 1.1.1
35966  * Copyright(c) 2006-2007, Ext JS, LLC.
35967  *
35968  * Originally Released Under LGPL - original licence link has changed is not relivant.
35969  *
35970  * Fork - LGPL
35971  * <script type="text/javascript">
35972  */
35973  
35974 /**
35975  * @class Roo.bootstrap.layout.Region
35976  * @extends Roo.bootstrap.layout.Basic
35977  * This class represents a region in a layout manager.
35978  
35979  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35980  * @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})
35981  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35982  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35983  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35984  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35985  * @cfg {String}    title           The title for the region (overrides panel titles)
35986  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35987  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35988  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35989  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35990  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35991  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35992  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35993  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35994  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35995  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35996
35997  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35998  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35999  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36000  * @cfg {Number}    width           For East/West panels
36001  * @cfg {Number}    height          For North/South panels
36002  * @cfg {Boolean}   split           To show the splitter
36003  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36004  * 
36005  * @cfg {string}   cls             Extra CSS classes to add to region
36006  * 
36007  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36008  * @cfg {string}   region  the region that it inhabits..
36009  *
36010
36011  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36012  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36013
36014  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36015  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36016  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36017  */
36018 Roo.bootstrap.layout.Region = function(config)
36019 {
36020     this.applyConfig(config);
36021
36022     var mgr = config.mgr;
36023     var pos = config.region;
36024     config.skipConfig = true;
36025     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36026     
36027     if (mgr.el) {
36028         this.onRender(mgr.el);   
36029     }
36030      
36031     this.visible = true;
36032     this.collapsed = false;
36033     this.unrendered_panels = [];
36034 };
36035
36036 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36037
36038     position: '', // set by wrapper (eg. north/south etc..)
36039     unrendered_panels : null,  // unrendered panels.
36040     
36041     tabPosition : false,
36042     
36043     mgr: false, // points to 'Border'
36044     
36045     
36046     createBody : function(){
36047         /** This region's body element 
36048         * @type Roo.Element */
36049         this.bodyEl = this.el.createChild({
36050                 tag: "div",
36051                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36052         });
36053     },
36054
36055     onRender: function(ctr, pos)
36056     {
36057         var dh = Roo.DomHelper;
36058         /** This region's container element 
36059         * @type Roo.Element */
36060         this.el = dh.append(ctr.dom, {
36061                 tag: "div",
36062                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36063             }, true);
36064         /** This region's title element 
36065         * @type Roo.Element */
36066     
36067         this.titleEl = dh.append(this.el.dom,  {
36068                 tag: "div",
36069                 unselectable: "on",
36070                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36071                 children:[
36072                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36073                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36074                 ]
36075             }, true);
36076         
36077         this.titleEl.enableDisplayMode();
36078         /** This region's title text element 
36079         * @type HTMLElement */
36080         this.titleTextEl = this.titleEl.dom.firstChild;
36081         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36082         /*
36083         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36084         this.closeBtn.enableDisplayMode();
36085         this.closeBtn.on("click", this.closeClicked, this);
36086         this.closeBtn.hide();
36087     */
36088         this.createBody(this.config);
36089         if(this.config.hideWhenEmpty){
36090             this.hide();
36091             this.on("paneladded", this.validateVisibility, this);
36092             this.on("panelremoved", this.validateVisibility, this);
36093         }
36094         if(this.autoScroll){
36095             this.bodyEl.setStyle("overflow", "auto");
36096         }else{
36097             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36098         }
36099         //if(c.titlebar !== false){
36100             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36101                 this.titleEl.hide();
36102             }else{
36103                 this.titleEl.show();
36104                 if(this.config.title){
36105                     this.titleTextEl.innerHTML = this.config.title;
36106                 }
36107             }
36108         //}
36109         if(this.config.collapsed){
36110             this.collapse(true);
36111         }
36112         if(this.config.hidden){
36113             this.hide();
36114         }
36115         
36116         if (this.unrendered_panels && this.unrendered_panels.length) {
36117             for (var i =0;i< this.unrendered_panels.length; i++) {
36118                 this.add(this.unrendered_panels[i]);
36119             }
36120             this.unrendered_panels = null;
36121             
36122         }
36123         
36124     },
36125     
36126     applyConfig : function(c)
36127     {
36128         /*
36129          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36130             var dh = Roo.DomHelper;
36131             if(c.titlebar !== false){
36132                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36133                 this.collapseBtn.on("click", this.collapse, this);
36134                 this.collapseBtn.enableDisplayMode();
36135                 /*
36136                 if(c.showPin === true || this.showPin){
36137                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36138                     this.stickBtn.enableDisplayMode();
36139                     this.stickBtn.on("click", this.expand, this);
36140                     this.stickBtn.hide();
36141                 }
36142                 
36143             }
36144             */
36145             /** This region's collapsed element
36146             * @type Roo.Element */
36147             /*
36148              *
36149             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36150                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36151             ]}, true);
36152             
36153             if(c.floatable !== false){
36154                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36155                this.collapsedEl.on("click", this.collapseClick, this);
36156             }
36157
36158             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36159                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36160                    id: "message", unselectable: "on", style:{"float":"left"}});
36161                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36162              }
36163             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36164             this.expandBtn.on("click", this.expand, this);
36165             
36166         }
36167         
36168         if(this.collapseBtn){
36169             this.collapseBtn.setVisible(c.collapsible == true);
36170         }
36171         
36172         this.cmargins = c.cmargins || this.cmargins ||
36173                          (this.position == "west" || this.position == "east" ?
36174                              {top: 0, left: 2, right:2, bottom: 0} :
36175                              {top: 2, left: 0, right:0, bottom: 2});
36176         */
36177         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36178         
36179         
36180         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36181         
36182         this.autoScroll = c.autoScroll || false;
36183         
36184         
36185        
36186         
36187         this.duration = c.duration || .30;
36188         this.slideDuration = c.slideDuration || .45;
36189         this.config = c;
36190        
36191     },
36192     /**
36193      * Returns true if this region is currently visible.
36194      * @return {Boolean}
36195      */
36196     isVisible : function(){
36197         return this.visible;
36198     },
36199
36200     /**
36201      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36202      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36203      */
36204     //setCollapsedTitle : function(title){
36205     //    title = title || "&#160;";
36206      //   if(this.collapsedTitleTextEl){
36207       //      this.collapsedTitleTextEl.innerHTML = title;
36208        // }
36209     //},
36210
36211     getBox : function(){
36212         var b;
36213       //  if(!this.collapsed){
36214             b = this.el.getBox(false, true);
36215        // }else{
36216           //  b = this.collapsedEl.getBox(false, true);
36217         //}
36218         return b;
36219     },
36220
36221     getMargins : function(){
36222         return this.margins;
36223         //return this.collapsed ? this.cmargins : this.margins;
36224     },
36225 /*
36226     highlight : function(){
36227         this.el.addClass("x-layout-panel-dragover");
36228     },
36229
36230     unhighlight : function(){
36231         this.el.removeClass("x-layout-panel-dragover");
36232     },
36233 */
36234     updateBox : function(box)
36235     {
36236         if (!this.bodyEl) {
36237             return; // not rendered yet..
36238         }
36239         
36240         this.box = box;
36241         if(!this.collapsed){
36242             this.el.dom.style.left = box.x + "px";
36243             this.el.dom.style.top = box.y + "px";
36244             this.updateBody(box.width, box.height);
36245         }else{
36246             this.collapsedEl.dom.style.left = box.x + "px";
36247             this.collapsedEl.dom.style.top = box.y + "px";
36248             this.collapsedEl.setSize(box.width, box.height);
36249         }
36250         if(this.tabs){
36251             this.tabs.autoSizeTabs();
36252         }
36253     },
36254
36255     updateBody : function(w, h)
36256     {
36257         if(w !== null){
36258             this.el.setWidth(w);
36259             w -= this.el.getBorderWidth("rl");
36260             if(this.config.adjustments){
36261                 w += this.config.adjustments[0];
36262             }
36263         }
36264         if(h !== null && h > 0){
36265             this.el.setHeight(h);
36266             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36267             h -= this.el.getBorderWidth("tb");
36268             if(this.config.adjustments){
36269                 h += this.config.adjustments[1];
36270             }
36271             this.bodyEl.setHeight(h);
36272             if(this.tabs){
36273                 h = this.tabs.syncHeight(h);
36274             }
36275         }
36276         if(this.panelSize){
36277             w = w !== null ? w : this.panelSize.width;
36278             h = h !== null ? h : this.panelSize.height;
36279         }
36280         if(this.activePanel){
36281             var el = this.activePanel.getEl();
36282             w = w !== null ? w : el.getWidth();
36283             h = h !== null ? h : el.getHeight();
36284             this.panelSize = {width: w, height: h};
36285             this.activePanel.setSize(w, h);
36286         }
36287         if(Roo.isIE && this.tabs){
36288             this.tabs.el.repaint();
36289         }
36290     },
36291
36292     /**
36293      * Returns the container element for this region.
36294      * @return {Roo.Element}
36295      */
36296     getEl : function(){
36297         return this.el;
36298     },
36299
36300     /**
36301      * Hides this region.
36302      */
36303     hide : function(){
36304         //if(!this.collapsed){
36305             this.el.dom.style.left = "-2000px";
36306             this.el.hide();
36307         //}else{
36308          //   this.collapsedEl.dom.style.left = "-2000px";
36309          //   this.collapsedEl.hide();
36310        // }
36311         this.visible = false;
36312         this.fireEvent("visibilitychange", this, false);
36313     },
36314
36315     /**
36316      * Shows this region if it was previously hidden.
36317      */
36318     show : function(){
36319         //if(!this.collapsed){
36320             this.el.show();
36321         //}else{
36322         //    this.collapsedEl.show();
36323        // }
36324         this.visible = true;
36325         this.fireEvent("visibilitychange", this, true);
36326     },
36327 /*
36328     closeClicked : function(){
36329         if(this.activePanel){
36330             this.remove(this.activePanel);
36331         }
36332     },
36333
36334     collapseClick : function(e){
36335         if(this.isSlid){
36336            e.stopPropagation();
36337            this.slideIn();
36338         }else{
36339            e.stopPropagation();
36340            this.slideOut();
36341         }
36342     },
36343 */
36344     /**
36345      * Collapses this region.
36346      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36347      */
36348     /*
36349     collapse : function(skipAnim, skipCheck = false){
36350         if(this.collapsed) {
36351             return;
36352         }
36353         
36354         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36355             
36356             this.collapsed = true;
36357             if(this.split){
36358                 this.split.el.hide();
36359             }
36360             if(this.config.animate && skipAnim !== true){
36361                 this.fireEvent("invalidated", this);
36362                 this.animateCollapse();
36363             }else{
36364                 this.el.setLocation(-20000,-20000);
36365                 this.el.hide();
36366                 this.collapsedEl.show();
36367                 this.fireEvent("collapsed", this);
36368                 this.fireEvent("invalidated", this);
36369             }
36370         }
36371         
36372     },
36373 */
36374     animateCollapse : function(){
36375         // overridden
36376     },
36377
36378     /**
36379      * Expands this region if it was previously collapsed.
36380      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36381      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36382      */
36383     /*
36384     expand : function(e, skipAnim){
36385         if(e) {
36386             e.stopPropagation();
36387         }
36388         if(!this.collapsed || this.el.hasActiveFx()) {
36389             return;
36390         }
36391         if(this.isSlid){
36392             this.afterSlideIn();
36393             skipAnim = true;
36394         }
36395         this.collapsed = false;
36396         if(this.config.animate && skipAnim !== true){
36397             this.animateExpand();
36398         }else{
36399             this.el.show();
36400             if(this.split){
36401                 this.split.el.show();
36402             }
36403             this.collapsedEl.setLocation(-2000,-2000);
36404             this.collapsedEl.hide();
36405             this.fireEvent("invalidated", this);
36406             this.fireEvent("expanded", this);
36407         }
36408     },
36409 */
36410     animateExpand : function(){
36411         // overridden
36412     },
36413
36414     initTabs : function()
36415     {
36416         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36417         
36418         var ts = new Roo.bootstrap.panel.Tabs({
36419             el: this.bodyEl.dom,
36420             region : this,
36421             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36422             disableTooltips: this.config.disableTabTips,
36423             toolbar : this.config.toolbar
36424         });
36425         
36426         if(this.config.hideTabs){
36427             ts.stripWrap.setDisplayed(false);
36428         }
36429         this.tabs = ts;
36430         ts.resizeTabs = this.config.resizeTabs === true;
36431         ts.minTabWidth = this.config.minTabWidth || 40;
36432         ts.maxTabWidth = this.config.maxTabWidth || 250;
36433         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36434         ts.monitorResize = false;
36435         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36436         ts.bodyEl.addClass('roo-layout-tabs-body');
36437         this.panels.each(this.initPanelAsTab, this);
36438     },
36439
36440     initPanelAsTab : function(panel){
36441         var ti = this.tabs.addTab(
36442             panel.getEl().id,
36443             panel.getTitle(),
36444             null,
36445             this.config.closeOnTab && panel.isClosable(),
36446             panel.tpl
36447         );
36448         if(panel.tabTip !== undefined){
36449             ti.setTooltip(panel.tabTip);
36450         }
36451         ti.on("activate", function(){
36452               this.setActivePanel(panel);
36453         }, this);
36454         
36455         if(this.config.closeOnTab){
36456             ti.on("beforeclose", function(t, e){
36457                 e.cancel = true;
36458                 this.remove(panel);
36459             }, this);
36460         }
36461         
36462         panel.tabItem = ti;
36463         
36464         return ti;
36465     },
36466
36467     updatePanelTitle : function(panel, title)
36468     {
36469         if(this.activePanel == panel){
36470             this.updateTitle(title);
36471         }
36472         if(this.tabs){
36473             var ti = this.tabs.getTab(panel.getEl().id);
36474             ti.setText(title);
36475             if(panel.tabTip !== undefined){
36476                 ti.setTooltip(panel.tabTip);
36477             }
36478         }
36479     },
36480
36481     updateTitle : function(title){
36482         if(this.titleTextEl && !this.config.title){
36483             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36484         }
36485     },
36486
36487     setActivePanel : function(panel)
36488     {
36489         panel = this.getPanel(panel);
36490         if(this.activePanel && this.activePanel != panel){
36491             if(this.activePanel.setActiveState(false) === false){
36492                 return;
36493             }
36494         }
36495         this.activePanel = panel;
36496         panel.setActiveState(true);
36497         if(this.panelSize){
36498             panel.setSize(this.panelSize.width, this.panelSize.height);
36499         }
36500         if(this.closeBtn){
36501             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36502         }
36503         this.updateTitle(panel.getTitle());
36504         if(this.tabs){
36505             this.fireEvent("invalidated", this);
36506         }
36507         this.fireEvent("panelactivated", this, panel);
36508     },
36509
36510     /**
36511      * Shows the specified panel.
36512      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36513      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36514      */
36515     showPanel : function(panel)
36516     {
36517         panel = this.getPanel(panel);
36518         if(panel){
36519             if(this.tabs){
36520                 var tab = this.tabs.getTab(panel.getEl().id);
36521                 if(tab.isHidden()){
36522                     this.tabs.unhideTab(tab.id);
36523                 }
36524                 tab.activate();
36525             }else{
36526                 this.setActivePanel(panel);
36527             }
36528         }
36529         return panel;
36530     },
36531
36532     /**
36533      * Get the active panel for this region.
36534      * @return {Roo.ContentPanel} The active panel or null
36535      */
36536     getActivePanel : function(){
36537         return this.activePanel;
36538     },
36539
36540     validateVisibility : function(){
36541         if(this.panels.getCount() < 1){
36542             this.updateTitle("&#160;");
36543             this.closeBtn.hide();
36544             this.hide();
36545         }else{
36546             if(!this.isVisible()){
36547                 this.show();
36548             }
36549         }
36550     },
36551
36552     /**
36553      * Adds the passed ContentPanel(s) to this region.
36554      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36555      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36556      */
36557     add : function(panel)
36558     {
36559         if(arguments.length > 1){
36560             for(var i = 0, len = arguments.length; i < len; i++) {
36561                 this.add(arguments[i]);
36562             }
36563             return null;
36564         }
36565         
36566         // if we have not been rendered yet, then we can not really do much of this..
36567         if (!this.bodyEl) {
36568             this.unrendered_panels.push(panel);
36569             return panel;
36570         }
36571         
36572         
36573         
36574         
36575         if(this.hasPanel(panel)){
36576             this.showPanel(panel);
36577             return panel;
36578         }
36579         panel.setRegion(this);
36580         this.panels.add(panel);
36581        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36582             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36583             // and hide them... ???
36584             this.bodyEl.dom.appendChild(panel.getEl().dom);
36585             if(panel.background !== true){
36586                 this.setActivePanel(panel);
36587             }
36588             this.fireEvent("paneladded", this, panel);
36589             return panel;
36590         }
36591         */
36592         if(!this.tabs){
36593             this.initTabs();
36594         }else{
36595             this.initPanelAsTab(panel);
36596         }
36597         
36598         
36599         if(panel.background !== true){
36600             this.tabs.activate(panel.getEl().id);
36601         }
36602         this.fireEvent("paneladded", this, panel);
36603         return panel;
36604     },
36605
36606     /**
36607      * Hides the tab for the specified panel.
36608      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36609      */
36610     hidePanel : function(panel){
36611         if(this.tabs && (panel = this.getPanel(panel))){
36612             this.tabs.hideTab(panel.getEl().id);
36613         }
36614     },
36615
36616     /**
36617      * Unhides the tab for a previously hidden panel.
36618      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36619      */
36620     unhidePanel : function(panel){
36621         if(this.tabs && (panel = this.getPanel(panel))){
36622             this.tabs.unhideTab(panel.getEl().id);
36623         }
36624     },
36625
36626     clearPanels : function(){
36627         while(this.panels.getCount() > 0){
36628              this.remove(this.panels.first());
36629         }
36630     },
36631
36632     /**
36633      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36634      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36635      * @param {Boolean} preservePanel Overrides the config preservePanel option
36636      * @return {Roo.ContentPanel} The panel that was removed
36637      */
36638     remove : function(panel, preservePanel)
36639     {
36640         panel = this.getPanel(panel);
36641         if(!panel){
36642             return null;
36643         }
36644         var e = {};
36645         this.fireEvent("beforeremove", this, panel, e);
36646         if(e.cancel === true){
36647             return null;
36648         }
36649         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36650         var panelId = panel.getId();
36651         this.panels.removeKey(panelId);
36652         if(preservePanel){
36653             document.body.appendChild(panel.getEl().dom);
36654         }
36655         if(this.tabs){
36656             this.tabs.removeTab(panel.getEl().id);
36657         }else if (!preservePanel){
36658             this.bodyEl.dom.removeChild(panel.getEl().dom);
36659         }
36660         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36661             var p = this.panels.first();
36662             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36663             tempEl.appendChild(p.getEl().dom);
36664             this.bodyEl.update("");
36665             this.bodyEl.dom.appendChild(p.getEl().dom);
36666             tempEl = null;
36667             this.updateTitle(p.getTitle());
36668             this.tabs = null;
36669             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36670             this.setActivePanel(p);
36671         }
36672         panel.setRegion(null);
36673         if(this.activePanel == panel){
36674             this.activePanel = null;
36675         }
36676         if(this.config.autoDestroy !== false && preservePanel !== true){
36677             try{panel.destroy();}catch(e){}
36678         }
36679         this.fireEvent("panelremoved", this, panel);
36680         return panel;
36681     },
36682
36683     /**
36684      * Returns the TabPanel component used by this region
36685      * @return {Roo.TabPanel}
36686      */
36687     getTabs : function(){
36688         return this.tabs;
36689     },
36690
36691     createTool : function(parentEl, className){
36692         var btn = Roo.DomHelper.append(parentEl, {
36693             tag: "div",
36694             cls: "x-layout-tools-button",
36695             children: [ {
36696                 tag: "div",
36697                 cls: "roo-layout-tools-button-inner " + className,
36698                 html: "&#160;"
36699             }]
36700         }, true);
36701         btn.addClassOnOver("roo-layout-tools-button-over");
36702         return btn;
36703     }
36704 });/*
36705  * Based on:
36706  * Ext JS Library 1.1.1
36707  * Copyright(c) 2006-2007, Ext JS, LLC.
36708  *
36709  * Originally Released Under LGPL - original licence link has changed is not relivant.
36710  *
36711  * Fork - LGPL
36712  * <script type="text/javascript">
36713  */
36714  
36715
36716
36717 /**
36718  * @class Roo.SplitLayoutRegion
36719  * @extends Roo.LayoutRegion
36720  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36721  */
36722 Roo.bootstrap.layout.Split = function(config){
36723     this.cursor = config.cursor;
36724     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36725 };
36726
36727 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36728 {
36729     splitTip : "Drag to resize.",
36730     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36731     useSplitTips : false,
36732
36733     applyConfig : function(config){
36734         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36735     },
36736     
36737     onRender : function(ctr,pos) {
36738         
36739         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36740         if(!this.config.split){
36741             return;
36742         }
36743         if(!this.split){
36744             
36745             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36746                             tag: "div",
36747                             id: this.el.id + "-split",
36748                             cls: "roo-layout-split roo-layout-split-"+this.position,
36749                             html: "&#160;"
36750             });
36751             /** The SplitBar for this region 
36752             * @type Roo.SplitBar */
36753             // does not exist yet...
36754             Roo.log([this.position, this.orientation]);
36755             
36756             this.split = new Roo.bootstrap.SplitBar({
36757                 dragElement : splitEl,
36758                 resizingElement: this.el,
36759                 orientation : this.orientation
36760             });
36761             
36762             this.split.on("moved", this.onSplitMove, this);
36763             this.split.useShim = this.config.useShim === true;
36764             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36765             if(this.useSplitTips){
36766                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36767             }
36768             //if(config.collapsible){
36769             //    this.split.el.on("dblclick", this.collapse,  this);
36770             //}
36771         }
36772         if(typeof this.config.minSize != "undefined"){
36773             this.split.minSize = this.config.minSize;
36774         }
36775         if(typeof this.config.maxSize != "undefined"){
36776             this.split.maxSize = this.config.maxSize;
36777         }
36778         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36779             this.hideSplitter();
36780         }
36781         
36782     },
36783
36784     getHMaxSize : function(){
36785          var cmax = this.config.maxSize || 10000;
36786          var center = this.mgr.getRegion("center");
36787          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36788     },
36789
36790     getVMaxSize : function(){
36791          var cmax = this.config.maxSize || 10000;
36792          var center = this.mgr.getRegion("center");
36793          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36794     },
36795
36796     onSplitMove : function(split, newSize){
36797         this.fireEvent("resized", this, newSize);
36798     },
36799     
36800     /** 
36801      * Returns the {@link Roo.SplitBar} for this region.
36802      * @return {Roo.SplitBar}
36803      */
36804     getSplitBar : function(){
36805         return this.split;
36806     },
36807     
36808     hide : function(){
36809         this.hideSplitter();
36810         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36811     },
36812
36813     hideSplitter : function(){
36814         if(this.split){
36815             this.split.el.setLocation(-2000,-2000);
36816             this.split.el.hide();
36817         }
36818     },
36819
36820     show : function(){
36821         if(this.split){
36822             this.split.el.show();
36823         }
36824         Roo.bootstrap.layout.Split.superclass.show.call(this);
36825     },
36826     
36827     beforeSlide: function(){
36828         if(Roo.isGecko){// firefox overflow auto bug workaround
36829             this.bodyEl.clip();
36830             if(this.tabs) {
36831                 this.tabs.bodyEl.clip();
36832             }
36833             if(this.activePanel){
36834                 this.activePanel.getEl().clip();
36835                 
36836                 if(this.activePanel.beforeSlide){
36837                     this.activePanel.beforeSlide();
36838                 }
36839             }
36840         }
36841     },
36842     
36843     afterSlide : function(){
36844         if(Roo.isGecko){// firefox overflow auto bug workaround
36845             this.bodyEl.unclip();
36846             if(this.tabs) {
36847                 this.tabs.bodyEl.unclip();
36848             }
36849             if(this.activePanel){
36850                 this.activePanel.getEl().unclip();
36851                 if(this.activePanel.afterSlide){
36852                     this.activePanel.afterSlide();
36853                 }
36854             }
36855         }
36856     },
36857
36858     initAutoHide : function(){
36859         if(this.autoHide !== false){
36860             if(!this.autoHideHd){
36861                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36862                 this.autoHideHd = {
36863                     "mouseout": function(e){
36864                         if(!e.within(this.el, true)){
36865                             st.delay(500);
36866                         }
36867                     },
36868                     "mouseover" : function(e){
36869                         st.cancel();
36870                     },
36871                     scope : this
36872                 };
36873             }
36874             this.el.on(this.autoHideHd);
36875         }
36876     },
36877
36878     clearAutoHide : function(){
36879         if(this.autoHide !== false){
36880             this.el.un("mouseout", this.autoHideHd.mouseout);
36881             this.el.un("mouseover", this.autoHideHd.mouseover);
36882         }
36883     },
36884
36885     clearMonitor : function(){
36886         Roo.get(document).un("click", this.slideInIf, this);
36887     },
36888
36889     // these names are backwards but not changed for compat
36890     slideOut : function(){
36891         if(this.isSlid || this.el.hasActiveFx()){
36892             return;
36893         }
36894         this.isSlid = true;
36895         if(this.collapseBtn){
36896             this.collapseBtn.hide();
36897         }
36898         this.closeBtnState = this.closeBtn.getStyle('display');
36899         this.closeBtn.hide();
36900         if(this.stickBtn){
36901             this.stickBtn.show();
36902         }
36903         this.el.show();
36904         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36905         this.beforeSlide();
36906         this.el.setStyle("z-index", 10001);
36907         this.el.slideIn(this.getSlideAnchor(), {
36908             callback: function(){
36909                 this.afterSlide();
36910                 this.initAutoHide();
36911                 Roo.get(document).on("click", this.slideInIf, this);
36912                 this.fireEvent("slideshow", this);
36913             },
36914             scope: this,
36915             block: true
36916         });
36917     },
36918
36919     afterSlideIn : function(){
36920         this.clearAutoHide();
36921         this.isSlid = false;
36922         this.clearMonitor();
36923         this.el.setStyle("z-index", "");
36924         if(this.collapseBtn){
36925             this.collapseBtn.show();
36926         }
36927         this.closeBtn.setStyle('display', this.closeBtnState);
36928         if(this.stickBtn){
36929             this.stickBtn.hide();
36930         }
36931         this.fireEvent("slidehide", this);
36932     },
36933
36934     slideIn : function(cb){
36935         if(!this.isSlid || this.el.hasActiveFx()){
36936             Roo.callback(cb);
36937             return;
36938         }
36939         this.isSlid = false;
36940         this.beforeSlide();
36941         this.el.slideOut(this.getSlideAnchor(), {
36942             callback: function(){
36943                 this.el.setLeftTop(-10000, -10000);
36944                 this.afterSlide();
36945                 this.afterSlideIn();
36946                 Roo.callback(cb);
36947             },
36948             scope: this,
36949             block: true
36950         });
36951     },
36952     
36953     slideInIf : function(e){
36954         if(!e.within(this.el)){
36955             this.slideIn();
36956         }
36957     },
36958
36959     animateCollapse : function(){
36960         this.beforeSlide();
36961         this.el.setStyle("z-index", 20000);
36962         var anchor = this.getSlideAnchor();
36963         this.el.slideOut(anchor, {
36964             callback : function(){
36965                 this.el.setStyle("z-index", "");
36966                 this.collapsedEl.slideIn(anchor, {duration:.3});
36967                 this.afterSlide();
36968                 this.el.setLocation(-10000,-10000);
36969                 this.el.hide();
36970                 this.fireEvent("collapsed", this);
36971             },
36972             scope: this,
36973             block: true
36974         });
36975     },
36976
36977     animateExpand : function(){
36978         this.beforeSlide();
36979         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36980         this.el.setStyle("z-index", 20000);
36981         this.collapsedEl.hide({
36982             duration:.1
36983         });
36984         this.el.slideIn(this.getSlideAnchor(), {
36985             callback : function(){
36986                 this.el.setStyle("z-index", "");
36987                 this.afterSlide();
36988                 if(this.split){
36989                     this.split.el.show();
36990                 }
36991                 this.fireEvent("invalidated", this);
36992                 this.fireEvent("expanded", this);
36993             },
36994             scope: this,
36995             block: true
36996         });
36997     },
36998
36999     anchors : {
37000         "west" : "left",
37001         "east" : "right",
37002         "north" : "top",
37003         "south" : "bottom"
37004     },
37005
37006     sanchors : {
37007         "west" : "l",
37008         "east" : "r",
37009         "north" : "t",
37010         "south" : "b"
37011     },
37012
37013     canchors : {
37014         "west" : "tl-tr",
37015         "east" : "tr-tl",
37016         "north" : "tl-bl",
37017         "south" : "bl-tl"
37018     },
37019
37020     getAnchor : function(){
37021         return this.anchors[this.position];
37022     },
37023
37024     getCollapseAnchor : function(){
37025         return this.canchors[this.position];
37026     },
37027
37028     getSlideAnchor : function(){
37029         return this.sanchors[this.position];
37030     },
37031
37032     getAlignAdj : function(){
37033         var cm = this.cmargins;
37034         switch(this.position){
37035             case "west":
37036                 return [0, 0];
37037             break;
37038             case "east":
37039                 return [0, 0];
37040             break;
37041             case "north":
37042                 return [0, 0];
37043             break;
37044             case "south":
37045                 return [0, 0];
37046             break;
37047         }
37048     },
37049
37050     getExpandAdj : function(){
37051         var c = this.collapsedEl, cm = this.cmargins;
37052         switch(this.position){
37053             case "west":
37054                 return [-(cm.right+c.getWidth()+cm.left), 0];
37055             break;
37056             case "east":
37057                 return [cm.right+c.getWidth()+cm.left, 0];
37058             break;
37059             case "north":
37060                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37061             break;
37062             case "south":
37063                 return [0, cm.top+cm.bottom+c.getHeight()];
37064             break;
37065         }
37066     }
37067 });/*
37068  * Based on:
37069  * Ext JS Library 1.1.1
37070  * Copyright(c) 2006-2007, Ext JS, LLC.
37071  *
37072  * Originally Released Under LGPL - original licence link has changed is not relivant.
37073  *
37074  * Fork - LGPL
37075  * <script type="text/javascript">
37076  */
37077 /*
37078  * These classes are private internal classes
37079  */
37080 Roo.bootstrap.layout.Center = function(config){
37081     config.region = "center";
37082     Roo.bootstrap.layout.Region.call(this, config);
37083     this.visible = true;
37084     this.minWidth = config.minWidth || 20;
37085     this.minHeight = config.minHeight || 20;
37086 };
37087
37088 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37089     hide : function(){
37090         // center panel can't be hidden
37091     },
37092     
37093     show : function(){
37094         // center panel can't be hidden
37095     },
37096     
37097     getMinWidth: function(){
37098         return this.minWidth;
37099     },
37100     
37101     getMinHeight: function(){
37102         return this.minHeight;
37103     }
37104 });
37105
37106
37107
37108
37109  
37110
37111
37112
37113
37114
37115
37116 Roo.bootstrap.layout.North = function(config)
37117 {
37118     config.region = 'north';
37119     config.cursor = 'n-resize';
37120     
37121     Roo.bootstrap.layout.Split.call(this, config);
37122     
37123     
37124     if(this.split){
37125         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37126         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37127         this.split.el.addClass("roo-layout-split-v");
37128     }
37129     var size = config.initialSize || config.height;
37130     if(typeof size != "undefined"){
37131         this.el.setHeight(size);
37132     }
37133 };
37134 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37135 {
37136     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37137     
37138     
37139     
37140     getBox : function(){
37141         if(this.collapsed){
37142             return this.collapsedEl.getBox();
37143         }
37144         var box = this.el.getBox();
37145         if(this.split){
37146             box.height += this.split.el.getHeight();
37147         }
37148         return box;
37149     },
37150     
37151     updateBox : function(box){
37152         if(this.split && !this.collapsed){
37153             box.height -= this.split.el.getHeight();
37154             this.split.el.setLeft(box.x);
37155             this.split.el.setTop(box.y+box.height);
37156             this.split.el.setWidth(box.width);
37157         }
37158         if(this.collapsed){
37159             this.updateBody(box.width, null);
37160         }
37161         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37162     }
37163 });
37164
37165
37166
37167
37168
37169 Roo.bootstrap.layout.South = function(config){
37170     config.region = 'south';
37171     config.cursor = 's-resize';
37172     Roo.bootstrap.layout.Split.call(this, config);
37173     if(this.split){
37174         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37175         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37176         this.split.el.addClass("roo-layout-split-v");
37177     }
37178     var size = config.initialSize || config.height;
37179     if(typeof size != "undefined"){
37180         this.el.setHeight(size);
37181     }
37182 };
37183
37184 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37185     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37186     getBox : function(){
37187         if(this.collapsed){
37188             return this.collapsedEl.getBox();
37189         }
37190         var box = this.el.getBox();
37191         if(this.split){
37192             var sh = this.split.el.getHeight();
37193             box.height += sh;
37194             box.y -= sh;
37195         }
37196         return box;
37197     },
37198     
37199     updateBox : function(box){
37200         if(this.split && !this.collapsed){
37201             var sh = this.split.el.getHeight();
37202             box.height -= sh;
37203             box.y += sh;
37204             this.split.el.setLeft(box.x);
37205             this.split.el.setTop(box.y-sh);
37206             this.split.el.setWidth(box.width);
37207         }
37208         if(this.collapsed){
37209             this.updateBody(box.width, null);
37210         }
37211         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37212     }
37213 });
37214
37215 Roo.bootstrap.layout.East = function(config){
37216     config.region = "east";
37217     config.cursor = "e-resize";
37218     Roo.bootstrap.layout.Split.call(this, config);
37219     if(this.split){
37220         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37221         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37222         this.split.el.addClass("roo-layout-split-h");
37223     }
37224     var size = config.initialSize || config.width;
37225     if(typeof size != "undefined"){
37226         this.el.setWidth(size);
37227     }
37228 };
37229 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37230     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37231     getBox : function(){
37232         if(this.collapsed){
37233             return this.collapsedEl.getBox();
37234         }
37235         var box = this.el.getBox();
37236         if(this.split){
37237             var sw = this.split.el.getWidth();
37238             box.width += sw;
37239             box.x -= sw;
37240         }
37241         return box;
37242     },
37243
37244     updateBox : function(box){
37245         if(this.split && !this.collapsed){
37246             var sw = this.split.el.getWidth();
37247             box.width -= sw;
37248             this.split.el.setLeft(box.x);
37249             this.split.el.setTop(box.y);
37250             this.split.el.setHeight(box.height);
37251             box.x += sw;
37252         }
37253         if(this.collapsed){
37254             this.updateBody(null, box.height);
37255         }
37256         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37257     }
37258 });
37259
37260 Roo.bootstrap.layout.West = function(config){
37261     config.region = "west";
37262     config.cursor = "w-resize";
37263     
37264     Roo.bootstrap.layout.Split.call(this, config);
37265     if(this.split){
37266         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37267         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37268         this.split.el.addClass("roo-layout-split-h");
37269     }
37270     
37271 };
37272 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37273     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37274     
37275     onRender: function(ctr, pos)
37276     {
37277         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37278         var size = this.config.initialSize || this.config.width;
37279         if(typeof size != "undefined"){
37280             this.el.setWidth(size);
37281         }
37282     },
37283     
37284     getBox : function(){
37285         if(this.collapsed){
37286             return this.collapsedEl.getBox();
37287         }
37288         var box = this.el.getBox();
37289         if(this.split){
37290             box.width += this.split.el.getWidth();
37291         }
37292         return box;
37293     },
37294     
37295     updateBox : function(box){
37296         if(this.split && !this.collapsed){
37297             var sw = this.split.el.getWidth();
37298             box.width -= sw;
37299             this.split.el.setLeft(box.x+box.width);
37300             this.split.el.setTop(box.y);
37301             this.split.el.setHeight(box.height);
37302         }
37303         if(this.collapsed){
37304             this.updateBody(null, box.height);
37305         }
37306         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37307     }
37308 });Roo.namespace("Roo.bootstrap.panel");/*
37309  * Based on:
37310  * Ext JS Library 1.1.1
37311  * Copyright(c) 2006-2007, Ext JS, LLC.
37312  *
37313  * Originally Released Under LGPL - original licence link has changed is not relivant.
37314  *
37315  * Fork - LGPL
37316  * <script type="text/javascript">
37317  */
37318 /**
37319  * @class Roo.ContentPanel
37320  * @extends Roo.util.Observable
37321  * A basic ContentPanel element.
37322  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37323  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37324  * @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
37325  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37326  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37327  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37328  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37329  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37330  * @cfg {String} title          The title for this panel
37331  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37332  * @cfg {String} url            Calls {@link #setUrl} with this value
37333  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37334  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37335  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37336  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37337  * @cfg {Boolean} badges render the badges
37338
37339  * @constructor
37340  * Create a new ContentPanel.
37341  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37342  * @param {String/Object} config A string to set only the title or a config object
37343  * @param {String} content (optional) Set the HTML content for this panel
37344  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37345  */
37346 Roo.bootstrap.panel.Content = function( config){
37347     
37348     this.tpl = config.tpl || false;
37349     
37350     var el = config.el;
37351     var content = config.content;
37352
37353     if(config.autoCreate){ // xtype is available if this is called from factory
37354         el = Roo.id();
37355     }
37356     this.el = Roo.get(el);
37357     if(!this.el && config && config.autoCreate){
37358         if(typeof config.autoCreate == "object"){
37359             if(!config.autoCreate.id){
37360                 config.autoCreate.id = config.id||el;
37361             }
37362             this.el = Roo.DomHelper.append(document.body,
37363                         config.autoCreate, true);
37364         }else{
37365             var elcfg =  {   tag: "div",
37366                             cls: "roo-layout-inactive-content",
37367                             id: config.id||el
37368                             };
37369             if (config.html) {
37370                 elcfg.html = config.html;
37371                 
37372             }
37373                         
37374             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37375         }
37376     } 
37377     this.closable = false;
37378     this.loaded = false;
37379     this.active = false;
37380    
37381       
37382     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37383         
37384         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37385         
37386         this.wrapEl = this.el; //this.el.wrap();
37387         var ti = [];
37388         if (config.toolbar.items) {
37389             ti = config.toolbar.items ;
37390             delete config.toolbar.items ;
37391         }
37392         
37393         var nitems = [];
37394         this.toolbar.render(this.wrapEl, 'before');
37395         for(var i =0;i < ti.length;i++) {
37396           //  Roo.log(['add child', items[i]]);
37397             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37398         }
37399         this.toolbar.items = nitems;
37400         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37401         delete config.toolbar;
37402         
37403     }
37404     /*
37405     // xtype created footer. - not sure if will work as we normally have to render first..
37406     if (this.footer && !this.footer.el && this.footer.xtype) {
37407         if (!this.wrapEl) {
37408             this.wrapEl = this.el.wrap();
37409         }
37410     
37411         this.footer.container = this.wrapEl.createChild();
37412          
37413         this.footer = Roo.factory(this.footer, Roo);
37414         
37415     }
37416     */
37417     
37418      if(typeof config == "string"){
37419         this.title = config;
37420     }else{
37421         Roo.apply(this, config);
37422     }
37423     
37424     if(this.resizeEl){
37425         this.resizeEl = Roo.get(this.resizeEl, true);
37426     }else{
37427         this.resizeEl = this.el;
37428     }
37429     // handle view.xtype
37430     
37431  
37432     
37433     
37434     this.addEvents({
37435         /**
37436          * @event activate
37437          * Fires when this panel is activated. 
37438          * @param {Roo.ContentPanel} this
37439          */
37440         "activate" : true,
37441         /**
37442          * @event deactivate
37443          * Fires when this panel is activated. 
37444          * @param {Roo.ContentPanel} this
37445          */
37446         "deactivate" : true,
37447
37448         /**
37449          * @event resize
37450          * Fires when this panel is resized if fitToFrame is true.
37451          * @param {Roo.ContentPanel} this
37452          * @param {Number} width The width after any component adjustments
37453          * @param {Number} height The height after any component adjustments
37454          */
37455         "resize" : true,
37456         
37457          /**
37458          * @event render
37459          * Fires when this tab is created
37460          * @param {Roo.ContentPanel} this
37461          */
37462         "render" : true
37463         
37464         
37465         
37466     });
37467     
37468
37469     
37470     
37471     if(this.autoScroll){
37472         this.resizeEl.setStyle("overflow", "auto");
37473     } else {
37474         // fix randome scrolling
37475         //this.el.on('scroll', function() {
37476         //    Roo.log('fix random scolling');
37477         //    this.scrollTo('top',0); 
37478         //});
37479     }
37480     content = content || this.content;
37481     if(content){
37482         this.setContent(content);
37483     }
37484     if(config && config.url){
37485         this.setUrl(this.url, this.params, this.loadOnce);
37486     }
37487     
37488     
37489     
37490     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37491     
37492     if (this.view && typeof(this.view.xtype) != 'undefined') {
37493         this.view.el = this.el.appendChild(document.createElement("div"));
37494         this.view = Roo.factory(this.view); 
37495         this.view.render  &&  this.view.render(false, '');  
37496     }
37497     
37498     
37499     this.fireEvent('render', this);
37500 };
37501
37502 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37503     
37504     tabTip : '',
37505     
37506     setRegion : function(region){
37507         this.region = region;
37508         this.setActiveClass(region && !this.background);
37509     },
37510     
37511     
37512     setActiveClass: function(state)
37513     {
37514         if(state){
37515            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37516            this.el.setStyle('position','relative');
37517         }else{
37518            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37519            this.el.setStyle('position', 'absolute');
37520         } 
37521     },
37522     
37523     /**
37524      * Returns the toolbar for this Panel if one was configured. 
37525      * @return {Roo.Toolbar} 
37526      */
37527     getToolbar : function(){
37528         return this.toolbar;
37529     },
37530     
37531     setActiveState : function(active)
37532     {
37533         this.active = active;
37534         this.setActiveClass(active);
37535         if(!active){
37536             if(this.fireEvent("deactivate", this) === false){
37537                 return false;
37538             }
37539             return true;
37540         }
37541         this.fireEvent("activate", this);
37542         return true;
37543     },
37544     /**
37545      * Updates this panel's element
37546      * @param {String} content The new content
37547      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37548     */
37549     setContent : function(content, loadScripts){
37550         this.el.update(content, loadScripts);
37551     },
37552
37553     ignoreResize : function(w, h){
37554         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37555             return true;
37556         }else{
37557             this.lastSize = {width: w, height: h};
37558             return false;
37559         }
37560     },
37561     /**
37562      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37563      * @return {Roo.UpdateManager} The UpdateManager
37564      */
37565     getUpdateManager : function(){
37566         return this.el.getUpdateManager();
37567     },
37568      /**
37569      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37570      * @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:
37571 <pre><code>
37572 panel.load({
37573     url: "your-url.php",
37574     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37575     callback: yourFunction,
37576     scope: yourObject, //(optional scope)
37577     discardUrl: false,
37578     nocache: false,
37579     text: "Loading...",
37580     timeout: 30,
37581     scripts: false
37582 });
37583 </code></pre>
37584      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37585      * 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.
37586      * @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}
37587      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37588      * @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.
37589      * @return {Roo.ContentPanel} this
37590      */
37591     load : function(){
37592         var um = this.el.getUpdateManager();
37593         um.update.apply(um, arguments);
37594         return this;
37595     },
37596
37597
37598     /**
37599      * 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.
37600      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37601      * @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)
37602      * @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)
37603      * @return {Roo.UpdateManager} The UpdateManager
37604      */
37605     setUrl : function(url, params, loadOnce){
37606         if(this.refreshDelegate){
37607             this.removeListener("activate", this.refreshDelegate);
37608         }
37609         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37610         this.on("activate", this.refreshDelegate);
37611         return this.el.getUpdateManager();
37612     },
37613     
37614     _handleRefresh : function(url, params, loadOnce){
37615         if(!loadOnce || !this.loaded){
37616             var updater = this.el.getUpdateManager();
37617             updater.update(url, params, this._setLoaded.createDelegate(this));
37618         }
37619     },
37620     
37621     _setLoaded : function(){
37622         this.loaded = true;
37623     }, 
37624     
37625     /**
37626      * Returns this panel's id
37627      * @return {String} 
37628      */
37629     getId : function(){
37630         return this.el.id;
37631     },
37632     
37633     /** 
37634      * Returns this panel's element - used by regiosn to add.
37635      * @return {Roo.Element} 
37636      */
37637     getEl : function(){
37638         return this.wrapEl || this.el;
37639     },
37640     
37641    
37642     
37643     adjustForComponents : function(width, height)
37644     {
37645         //Roo.log('adjustForComponents ');
37646         if(this.resizeEl != this.el){
37647             width -= this.el.getFrameWidth('lr');
37648             height -= this.el.getFrameWidth('tb');
37649         }
37650         if(this.toolbar){
37651             var te = this.toolbar.getEl();
37652             te.setWidth(width);
37653             height -= te.getHeight();
37654         }
37655         if(this.footer){
37656             var te = this.footer.getEl();
37657             te.setWidth(width);
37658             height -= te.getHeight();
37659         }
37660         
37661         
37662         if(this.adjustments){
37663             width += this.adjustments[0];
37664             height += this.adjustments[1];
37665         }
37666         return {"width": width, "height": height};
37667     },
37668     
37669     setSize : function(width, height){
37670         if(this.fitToFrame && !this.ignoreResize(width, height)){
37671             if(this.fitContainer && this.resizeEl != this.el){
37672                 this.el.setSize(width, height);
37673             }
37674             var size = this.adjustForComponents(width, height);
37675             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37676             this.fireEvent('resize', this, size.width, size.height);
37677         }
37678     },
37679     
37680     /**
37681      * Returns this panel's title
37682      * @return {String} 
37683      */
37684     getTitle : function(){
37685         
37686         if (typeof(this.title) != 'object') {
37687             return this.title;
37688         }
37689         
37690         var t = '';
37691         for (var k in this.title) {
37692             if (!this.title.hasOwnProperty(k)) {
37693                 continue;
37694             }
37695             
37696             if (k.indexOf('-') >= 0) {
37697                 var s = k.split('-');
37698                 for (var i = 0; i<s.length; i++) {
37699                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37700                 }
37701             } else {
37702                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37703             }
37704         }
37705         return t;
37706     },
37707     
37708     /**
37709      * Set this panel's title
37710      * @param {String} title
37711      */
37712     setTitle : function(title){
37713         this.title = title;
37714         if(this.region){
37715             this.region.updatePanelTitle(this, title);
37716         }
37717     },
37718     
37719     /**
37720      * Returns true is this panel was configured to be closable
37721      * @return {Boolean} 
37722      */
37723     isClosable : function(){
37724         return this.closable;
37725     },
37726     
37727     beforeSlide : function(){
37728         this.el.clip();
37729         this.resizeEl.clip();
37730     },
37731     
37732     afterSlide : function(){
37733         this.el.unclip();
37734         this.resizeEl.unclip();
37735     },
37736     
37737     /**
37738      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37739      *   Will fail silently if the {@link #setUrl} method has not been called.
37740      *   This does not activate the panel, just updates its content.
37741      */
37742     refresh : function(){
37743         if(this.refreshDelegate){
37744            this.loaded = false;
37745            this.refreshDelegate();
37746         }
37747     },
37748     
37749     /**
37750      * Destroys this panel
37751      */
37752     destroy : function(){
37753         this.el.removeAllListeners();
37754         var tempEl = document.createElement("span");
37755         tempEl.appendChild(this.el.dom);
37756         tempEl.innerHTML = "";
37757         this.el.remove();
37758         this.el = null;
37759     },
37760     
37761     /**
37762      * form - if the content panel contains a form - this is a reference to it.
37763      * @type {Roo.form.Form}
37764      */
37765     form : false,
37766     /**
37767      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37768      *    This contains a reference to it.
37769      * @type {Roo.View}
37770      */
37771     view : false,
37772     
37773       /**
37774      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37775      * <pre><code>
37776
37777 layout.addxtype({
37778        xtype : 'Form',
37779        items: [ .... ]
37780    }
37781 );
37782
37783 </code></pre>
37784      * @param {Object} cfg Xtype definition of item to add.
37785      */
37786     
37787     
37788     getChildContainer: function () {
37789         return this.getEl();
37790     }
37791     
37792     
37793     /*
37794         var  ret = new Roo.factory(cfg);
37795         return ret;
37796         
37797         
37798         // add form..
37799         if (cfg.xtype.match(/^Form$/)) {
37800             
37801             var el;
37802             //if (this.footer) {
37803             //    el = this.footer.container.insertSibling(false, 'before');
37804             //} else {
37805                 el = this.el.createChild();
37806             //}
37807
37808             this.form = new  Roo.form.Form(cfg);
37809             
37810             
37811             if ( this.form.allItems.length) {
37812                 this.form.render(el.dom);
37813             }
37814             return this.form;
37815         }
37816         // should only have one of theses..
37817         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37818             // views.. should not be just added - used named prop 'view''
37819             
37820             cfg.el = this.el.appendChild(document.createElement("div"));
37821             // factory?
37822             
37823             var ret = new Roo.factory(cfg);
37824              
37825              ret.render && ret.render(false, ''); // render blank..
37826             this.view = ret;
37827             return ret;
37828         }
37829         return false;
37830     }
37831     \*/
37832 });
37833  
37834 /**
37835  * @class Roo.bootstrap.panel.Grid
37836  * @extends Roo.bootstrap.panel.Content
37837  * @constructor
37838  * Create a new GridPanel.
37839  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37840  * @param {Object} config A the config object
37841   
37842  */
37843
37844
37845
37846 Roo.bootstrap.panel.Grid = function(config)
37847 {
37848     
37849       
37850     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37851         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37852
37853     config.el = this.wrapper;
37854     //this.el = this.wrapper;
37855     
37856       if (config.container) {
37857         // ctor'ed from a Border/panel.grid
37858         
37859         
37860         this.wrapper.setStyle("overflow", "hidden");
37861         this.wrapper.addClass('roo-grid-container');
37862
37863     }
37864     
37865     
37866     if(config.toolbar){
37867         var tool_el = this.wrapper.createChild();    
37868         this.toolbar = Roo.factory(config.toolbar);
37869         var ti = [];
37870         if (config.toolbar.items) {
37871             ti = config.toolbar.items ;
37872             delete config.toolbar.items ;
37873         }
37874         
37875         var nitems = [];
37876         this.toolbar.render(tool_el);
37877         for(var i =0;i < ti.length;i++) {
37878           //  Roo.log(['add child', items[i]]);
37879             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37880         }
37881         this.toolbar.items = nitems;
37882         
37883         delete config.toolbar;
37884     }
37885     
37886     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37887     config.grid.scrollBody = true;;
37888     config.grid.monitorWindowResize = false; // turn off autosizing
37889     config.grid.autoHeight = false;
37890     config.grid.autoWidth = false;
37891     
37892     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37893     
37894     if (config.background) {
37895         // render grid on panel activation (if panel background)
37896         this.on('activate', function(gp) {
37897             if (!gp.grid.rendered) {
37898                 gp.grid.render(this.wrapper);
37899                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37900             }
37901         });
37902             
37903     } else {
37904         this.grid.render(this.wrapper);
37905         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37906
37907     }
37908     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37909     // ??? needed ??? config.el = this.wrapper;
37910     
37911     
37912     
37913   
37914     // xtype created footer. - not sure if will work as we normally have to render first..
37915     if (this.footer && !this.footer.el && this.footer.xtype) {
37916         
37917         var ctr = this.grid.getView().getFooterPanel(true);
37918         this.footer.dataSource = this.grid.dataSource;
37919         this.footer = Roo.factory(this.footer, Roo);
37920         this.footer.render(ctr);
37921         
37922     }
37923     
37924     
37925     
37926     
37927      
37928 };
37929
37930 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37931     getId : function(){
37932         return this.grid.id;
37933     },
37934     
37935     /**
37936      * Returns the grid for this panel
37937      * @return {Roo.bootstrap.Table} 
37938      */
37939     getGrid : function(){
37940         return this.grid;    
37941     },
37942     
37943     setSize : function(width, height){
37944         if(!this.ignoreResize(width, height)){
37945             var grid = this.grid;
37946             var size = this.adjustForComponents(width, height);
37947             var gridel = grid.getGridEl();
37948             gridel.setSize(size.width, size.height);
37949             /*
37950             var thd = grid.getGridEl().select('thead',true).first();
37951             var tbd = grid.getGridEl().select('tbody', true).first();
37952             if (tbd) {
37953                 tbd.setSize(width, height - thd.getHeight());
37954             }
37955             */
37956             grid.autoSize();
37957         }
37958     },
37959      
37960     
37961     
37962     beforeSlide : function(){
37963         this.grid.getView().scroller.clip();
37964     },
37965     
37966     afterSlide : function(){
37967         this.grid.getView().scroller.unclip();
37968     },
37969     
37970     destroy : function(){
37971         this.grid.destroy();
37972         delete this.grid;
37973         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37974     }
37975 });
37976
37977 /**
37978  * @class Roo.bootstrap.panel.Nest
37979  * @extends Roo.bootstrap.panel.Content
37980  * @constructor
37981  * Create a new Panel, that can contain a layout.Border.
37982  * 
37983  * 
37984  * @param {Roo.BorderLayout} layout The layout for this panel
37985  * @param {String/Object} config A string to set only the title or a config object
37986  */
37987 Roo.bootstrap.panel.Nest = function(config)
37988 {
37989     // construct with only one argument..
37990     /* FIXME - implement nicer consturctors
37991     if (layout.layout) {
37992         config = layout;
37993         layout = config.layout;
37994         delete config.layout;
37995     }
37996     if (layout.xtype && !layout.getEl) {
37997         // then layout needs constructing..
37998         layout = Roo.factory(layout, Roo);
37999     }
38000     */
38001     
38002     config.el =  config.layout.getEl();
38003     
38004     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38005     
38006     config.layout.monitorWindowResize = false; // turn off autosizing
38007     this.layout = config.layout;
38008     this.layout.getEl().addClass("roo-layout-nested-layout");
38009     this.layout.parent = this;
38010     
38011     
38012     
38013     
38014 };
38015
38016 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38017
38018     setSize : function(width, height){
38019         if(!this.ignoreResize(width, height)){
38020             var size = this.adjustForComponents(width, height);
38021             var el = this.layout.getEl();
38022             if (size.height < 1) {
38023                 el.setWidth(size.width);   
38024             } else {
38025                 el.setSize(size.width, size.height);
38026             }
38027             var touch = el.dom.offsetWidth;
38028             this.layout.layout();
38029             // ie requires a double layout on the first pass
38030             if(Roo.isIE && !this.initialized){
38031                 this.initialized = true;
38032                 this.layout.layout();
38033             }
38034         }
38035     },
38036     
38037     // activate all subpanels if not currently active..
38038     
38039     setActiveState : function(active){
38040         this.active = active;
38041         this.setActiveClass(active);
38042         
38043         if(!active){
38044             this.fireEvent("deactivate", this);
38045             return;
38046         }
38047         
38048         this.fireEvent("activate", this);
38049         // not sure if this should happen before or after..
38050         if (!this.layout) {
38051             return; // should not happen..
38052         }
38053         var reg = false;
38054         for (var r in this.layout.regions) {
38055             reg = this.layout.getRegion(r);
38056             if (reg.getActivePanel()) {
38057                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38058                 reg.setActivePanel(reg.getActivePanel());
38059                 continue;
38060             }
38061             if (!reg.panels.length) {
38062                 continue;
38063             }
38064             reg.showPanel(reg.getPanel(0));
38065         }
38066         
38067         
38068         
38069         
38070     },
38071     
38072     /**
38073      * Returns the nested BorderLayout for this panel
38074      * @return {Roo.BorderLayout} 
38075      */
38076     getLayout : function(){
38077         return this.layout;
38078     },
38079     
38080      /**
38081      * Adds a xtype elements to the layout of the nested panel
38082      * <pre><code>
38083
38084 panel.addxtype({
38085        xtype : 'ContentPanel',
38086        region: 'west',
38087        items: [ .... ]
38088    }
38089 );
38090
38091 panel.addxtype({
38092         xtype : 'NestedLayoutPanel',
38093         region: 'west',
38094         layout: {
38095            center: { },
38096            west: { }   
38097         },
38098         items : [ ... list of content panels or nested layout panels.. ]
38099    }
38100 );
38101 </code></pre>
38102      * @param {Object} cfg Xtype definition of item to add.
38103      */
38104     addxtype : function(cfg) {
38105         return this.layout.addxtype(cfg);
38106     
38107     }
38108 });/*
38109  * Based on:
38110  * Ext JS Library 1.1.1
38111  * Copyright(c) 2006-2007, Ext JS, LLC.
38112  *
38113  * Originally Released Under LGPL - original licence link has changed is not relivant.
38114  *
38115  * Fork - LGPL
38116  * <script type="text/javascript">
38117  */
38118 /**
38119  * @class Roo.TabPanel
38120  * @extends Roo.util.Observable
38121  * A lightweight tab container.
38122  * <br><br>
38123  * Usage:
38124  * <pre><code>
38125 // basic tabs 1, built from existing content
38126 var tabs = new Roo.TabPanel("tabs1");
38127 tabs.addTab("script", "View Script");
38128 tabs.addTab("markup", "View Markup");
38129 tabs.activate("script");
38130
38131 // more advanced tabs, built from javascript
38132 var jtabs = new Roo.TabPanel("jtabs");
38133 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38134
38135 // set up the UpdateManager
38136 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38137 var updater = tab2.getUpdateManager();
38138 updater.setDefaultUrl("ajax1.htm");
38139 tab2.on('activate', updater.refresh, updater, true);
38140
38141 // Use setUrl for Ajax loading
38142 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38143 tab3.setUrl("ajax2.htm", null, true);
38144
38145 // Disabled tab
38146 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38147 tab4.disable();
38148
38149 jtabs.activate("jtabs-1");
38150  * </code></pre>
38151  * @constructor
38152  * Create a new TabPanel.
38153  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38154  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38155  */
38156 Roo.bootstrap.panel.Tabs = function(config){
38157     /**
38158     * The container element for this TabPanel.
38159     * @type Roo.Element
38160     */
38161     this.el = Roo.get(config.el);
38162     delete config.el;
38163     if(config){
38164         if(typeof config == "boolean"){
38165             this.tabPosition = config ? "bottom" : "top";
38166         }else{
38167             Roo.apply(this, config);
38168         }
38169     }
38170     
38171     if(this.tabPosition == "bottom"){
38172         // if tabs are at the bottom = create the body first.
38173         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38174         this.el.addClass("roo-tabs-bottom");
38175     }
38176     // next create the tabs holders
38177     
38178     if (this.tabPosition == "west"){
38179         
38180         var reg = this.region; // fake it..
38181         while (reg) {
38182             if (!reg.mgr.parent) {
38183                 break;
38184             }
38185             reg = reg.mgr.parent.region;
38186         }
38187         Roo.log("got nest?");
38188         Roo.log(reg);
38189         if (reg.mgr.getRegion('west')) {
38190             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38191             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38192             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38193             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38194             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38195         
38196             
38197         }
38198         
38199         
38200     } else {
38201      
38202         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38203         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38204         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38205         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38206     }
38207     
38208     
38209     if(Roo.isIE){
38210         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38211     }
38212     
38213     // finally - if tabs are at the top, then create the body last..
38214     if(this.tabPosition != "bottom"){
38215         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38216          * @type Roo.Element
38217          */
38218         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38219         this.el.addClass("roo-tabs-top");
38220     }
38221     this.items = [];
38222
38223     this.bodyEl.setStyle("position", "relative");
38224
38225     this.active = null;
38226     this.activateDelegate = this.activate.createDelegate(this);
38227
38228     this.addEvents({
38229         /**
38230          * @event tabchange
38231          * Fires when the active tab changes
38232          * @param {Roo.TabPanel} this
38233          * @param {Roo.TabPanelItem} activePanel The new active tab
38234          */
38235         "tabchange": true,
38236         /**
38237          * @event beforetabchange
38238          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38239          * @param {Roo.TabPanel} this
38240          * @param {Object} e Set cancel to true on this object to cancel the tab change
38241          * @param {Roo.TabPanelItem} tab The tab being changed to
38242          */
38243         "beforetabchange" : true
38244     });
38245
38246     Roo.EventManager.onWindowResize(this.onResize, this);
38247     this.cpad = this.el.getPadding("lr");
38248     this.hiddenCount = 0;
38249
38250
38251     // toolbar on the tabbar support...
38252     if (this.toolbar) {
38253         alert("no toolbar support yet");
38254         this.toolbar  = false;
38255         /*
38256         var tcfg = this.toolbar;
38257         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38258         this.toolbar = new Roo.Toolbar(tcfg);
38259         if (Roo.isSafari) {
38260             var tbl = tcfg.container.child('table', true);
38261             tbl.setAttribute('width', '100%');
38262         }
38263         */
38264         
38265     }
38266    
38267
38268
38269     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38270 };
38271
38272 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38273     /*
38274      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38275      */
38276     tabPosition : "top",
38277     /*
38278      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38279      */
38280     currentTabWidth : 0,
38281     /*
38282      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38283      */
38284     minTabWidth : 40,
38285     /*
38286      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38287      */
38288     maxTabWidth : 250,
38289     /*
38290      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38291      */
38292     preferredTabWidth : 175,
38293     /*
38294      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38295      */
38296     resizeTabs : false,
38297     /*
38298      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38299      */
38300     monitorResize : true,
38301     /*
38302      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38303      */
38304     toolbar : false,  // set by caller..
38305     
38306     region : false, /// set by caller
38307     
38308     disableTooltips : true, // not used yet...
38309
38310     /**
38311      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38312      * @param {String} id The id of the div to use <b>or create</b>
38313      * @param {String} text The text for the tab
38314      * @param {String} content (optional) Content to put in the TabPanelItem body
38315      * @param {Boolean} closable (optional) True to create a close icon on the tab
38316      * @return {Roo.TabPanelItem} The created TabPanelItem
38317      */
38318     addTab : function(id, text, content, closable, tpl)
38319     {
38320         var item = new Roo.bootstrap.panel.TabItem({
38321             panel: this,
38322             id : id,
38323             text : text,
38324             closable : closable,
38325             tpl : tpl
38326         });
38327         this.addTabItem(item);
38328         if(content){
38329             item.setContent(content);
38330         }
38331         return item;
38332     },
38333
38334     /**
38335      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38336      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38337      * @return {Roo.TabPanelItem}
38338      */
38339     getTab : function(id){
38340         return this.items[id];
38341     },
38342
38343     /**
38344      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38345      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38346      */
38347     hideTab : function(id){
38348         var t = this.items[id];
38349         if(!t.isHidden()){
38350            t.setHidden(true);
38351            this.hiddenCount++;
38352            this.autoSizeTabs();
38353         }
38354     },
38355
38356     /**
38357      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38358      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38359      */
38360     unhideTab : function(id){
38361         var t = this.items[id];
38362         if(t.isHidden()){
38363            t.setHidden(false);
38364            this.hiddenCount--;
38365            this.autoSizeTabs();
38366         }
38367     },
38368
38369     /**
38370      * Adds an existing {@link Roo.TabPanelItem}.
38371      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38372      */
38373     addTabItem : function(item)
38374     {
38375         this.items[item.id] = item;
38376         this.items.push(item);
38377         this.autoSizeTabs();
38378       //  if(this.resizeTabs){
38379     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38380   //         this.autoSizeTabs();
38381 //        }else{
38382 //            item.autoSize();
38383        // }
38384     },
38385
38386     /**
38387      * Removes a {@link Roo.TabPanelItem}.
38388      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38389      */
38390     removeTab : function(id){
38391         var items = this.items;
38392         var tab = items[id];
38393         if(!tab) { return; }
38394         var index = items.indexOf(tab);
38395         if(this.active == tab && items.length > 1){
38396             var newTab = this.getNextAvailable(index);
38397             if(newTab) {
38398                 newTab.activate();
38399             }
38400         }
38401         this.stripEl.dom.removeChild(tab.pnode.dom);
38402         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38403             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38404         }
38405         items.splice(index, 1);
38406         delete this.items[tab.id];
38407         tab.fireEvent("close", tab);
38408         tab.purgeListeners();
38409         this.autoSizeTabs();
38410     },
38411
38412     getNextAvailable : function(start){
38413         var items = this.items;
38414         var index = start;
38415         // look for a next tab that will slide over to
38416         // replace the one being removed
38417         while(index < items.length){
38418             var item = items[++index];
38419             if(item && !item.isHidden()){
38420                 return item;
38421             }
38422         }
38423         // if one isn't found select the previous tab (on the left)
38424         index = start;
38425         while(index >= 0){
38426             var item = items[--index];
38427             if(item && !item.isHidden()){
38428                 return item;
38429             }
38430         }
38431         return null;
38432     },
38433
38434     /**
38435      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38436      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38437      */
38438     disableTab : function(id){
38439         var tab = this.items[id];
38440         if(tab && this.active != tab){
38441             tab.disable();
38442         }
38443     },
38444
38445     /**
38446      * Enables a {@link Roo.TabPanelItem} that is disabled.
38447      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38448      */
38449     enableTab : function(id){
38450         var tab = this.items[id];
38451         tab.enable();
38452     },
38453
38454     /**
38455      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38456      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38457      * @return {Roo.TabPanelItem} The TabPanelItem.
38458      */
38459     activate : function(id)
38460     {
38461         //Roo.log('activite:'  + id);
38462         
38463         var tab = this.items[id];
38464         if(!tab){
38465             return null;
38466         }
38467         if(tab == this.active || tab.disabled){
38468             return tab;
38469         }
38470         var e = {};
38471         this.fireEvent("beforetabchange", this, e, tab);
38472         if(e.cancel !== true && !tab.disabled){
38473             if(this.active){
38474                 this.active.hide();
38475             }
38476             this.active = this.items[id];
38477             this.active.show();
38478             this.fireEvent("tabchange", this, this.active);
38479         }
38480         return tab;
38481     },
38482
38483     /**
38484      * Gets the active {@link Roo.TabPanelItem}.
38485      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38486      */
38487     getActiveTab : function(){
38488         return this.active;
38489     },
38490
38491     /**
38492      * Updates the tab body element to fit the height of the container element
38493      * for overflow scrolling
38494      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38495      */
38496     syncHeight : function(targetHeight){
38497         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38498         var bm = this.bodyEl.getMargins();
38499         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38500         this.bodyEl.setHeight(newHeight);
38501         return newHeight;
38502     },
38503
38504     onResize : function(){
38505         if(this.monitorResize){
38506             this.autoSizeTabs();
38507         }
38508     },
38509
38510     /**
38511      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38512      */
38513     beginUpdate : function(){
38514         this.updating = true;
38515     },
38516
38517     /**
38518      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38519      */
38520     endUpdate : function(){
38521         this.updating = false;
38522         this.autoSizeTabs();
38523     },
38524
38525     /**
38526      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38527      */
38528     autoSizeTabs : function()
38529     {
38530         var count = this.items.length;
38531         var vcount = count - this.hiddenCount;
38532         
38533         if (vcount < 2) {
38534             this.stripEl.hide();
38535         } else {
38536             this.stripEl.show();
38537         }
38538         
38539         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38540             return;
38541         }
38542         
38543         
38544         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38545         var availWidth = Math.floor(w / vcount);
38546         var b = this.stripBody;
38547         if(b.getWidth() > w){
38548             var tabs = this.items;
38549             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38550             if(availWidth < this.minTabWidth){
38551                 /*if(!this.sleft){    // incomplete scrolling code
38552                     this.createScrollButtons();
38553                 }
38554                 this.showScroll();
38555                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38556             }
38557         }else{
38558             if(this.currentTabWidth < this.preferredTabWidth){
38559                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38560             }
38561         }
38562     },
38563
38564     /**
38565      * Returns the number of tabs in this TabPanel.
38566      * @return {Number}
38567      */
38568      getCount : function(){
38569          return this.items.length;
38570      },
38571
38572     /**
38573      * Resizes all the tabs to the passed width
38574      * @param {Number} The new width
38575      */
38576     setTabWidth : function(width){
38577         this.currentTabWidth = width;
38578         for(var i = 0, len = this.items.length; i < len; i++) {
38579                 if(!this.items[i].isHidden()) {
38580                 this.items[i].setWidth(width);
38581             }
38582         }
38583     },
38584
38585     /**
38586      * Destroys this TabPanel
38587      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38588      */
38589     destroy : function(removeEl){
38590         Roo.EventManager.removeResizeListener(this.onResize, this);
38591         for(var i = 0, len = this.items.length; i < len; i++){
38592             this.items[i].purgeListeners();
38593         }
38594         if(removeEl === true){
38595             this.el.update("");
38596             this.el.remove();
38597         }
38598     },
38599     
38600     createStrip : function(container)
38601     {
38602         var strip = document.createElement("nav");
38603         strip.className = Roo.bootstrap.version == 4 ?
38604             "navbar-light bg-light" : 
38605             "navbar navbar-default"; //"x-tabs-wrap";
38606         container.appendChild(strip);
38607         return strip;
38608     },
38609     
38610     createStripList : function(strip)
38611     {
38612         // div wrapper for retard IE
38613         // returns the "tr" element.
38614         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38615         //'<div class="x-tabs-strip-wrap">'+
38616           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38617           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38618         return strip.firstChild; //.firstChild.firstChild.firstChild;
38619     },
38620     createBody : function(container)
38621     {
38622         var body = document.createElement("div");
38623         Roo.id(body, "tab-body");
38624         //Roo.fly(body).addClass("x-tabs-body");
38625         Roo.fly(body).addClass("tab-content");
38626         container.appendChild(body);
38627         return body;
38628     },
38629     createItemBody :function(bodyEl, id){
38630         var body = Roo.getDom(id);
38631         if(!body){
38632             body = document.createElement("div");
38633             body.id = id;
38634         }
38635         //Roo.fly(body).addClass("x-tabs-item-body");
38636         Roo.fly(body).addClass("tab-pane");
38637          bodyEl.insertBefore(body, bodyEl.firstChild);
38638         return body;
38639     },
38640     /** @private */
38641     createStripElements :  function(stripEl, text, closable, tpl)
38642     {
38643         var td = document.createElement("li"); // was td..
38644         td.className = 'nav-item';
38645         
38646         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38647         
38648         
38649         stripEl.appendChild(td);
38650         /*if(closable){
38651             td.className = "x-tabs-closable";
38652             if(!this.closeTpl){
38653                 this.closeTpl = new Roo.Template(
38654                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38655                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38656                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38657                 );
38658             }
38659             var el = this.closeTpl.overwrite(td, {"text": text});
38660             var close = el.getElementsByTagName("div")[0];
38661             var inner = el.getElementsByTagName("em")[0];
38662             return {"el": el, "close": close, "inner": inner};
38663         } else {
38664         */
38665         // not sure what this is..
38666 //            if(!this.tabTpl){
38667                 //this.tabTpl = new Roo.Template(
38668                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38669                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38670                 //);
38671 //                this.tabTpl = new Roo.Template(
38672 //                   '<a href="#">' +
38673 //                   '<span unselectable="on"' +
38674 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38675 //                            ' >{text}</span></a>'
38676 //                );
38677 //                
38678 //            }
38679
38680
38681             var template = tpl || this.tabTpl || false;
38682             
38683             if(!template){
38684                 template =  new Roo.Template(
38685                         Roo.bootstrap.version == 4 ? 
38686                             (
38687                                 '<a class="nav-link" href="#" unselectable="on"' +
38688                                      (this.disableTooltips ? '' : ' title="{text}"') +
38689                                      ' >{text}</a>'
38690                             ) : (
38691                                 '<a class="nav-link" href="#">' +
38692                                 '<span unselectable="on"' +
38693                                          (this.disableTooltips ? '' : ' title="{text}"') +
38694                                     ' >{text}</span></a>'
38695                             )
38696                 );
38697             }
38698             
38699             switch (typeof(template)) {
38700                 case 'object' :
38701                     break;
38702                 case 'string' :
38703                     template = new Roo.Template(template);
38704                     break;
38705                 default :
38706                     break;
38707             }
38708             
38709             var el = template.overwrite(td, {"text": text});
38710             
38711             var inner = el.getElementsByTagName("span")[0];
38712             
38713             return {"el": el, "inner": inner};
38714             
38715     }
38716         
38717     
38718 });
38719
38720 /**
38721  * @class Roo.TabPanelItem
38722  * @extends Roo.util.Observable
38723  * Represents an individual item (tab plus body) in a TabPanel.
38724  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38725  * @param {String} id The id of this TabPanelItem
38726  * @param {String} text The text for the tab of this TabPanelItem
38727  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38728  */
38729 Roo.bootstrap.panel.TabItem = function(config){
38730     /**
38731      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38732      * @type Roo.TabPanel
38733      */
38734     this.tabPanel = config.panel;
38735     /**
38736      * The id for this TabPanelItem
38737      * @type String
38738      */
38739     this.id = config.id;
38740     /** @private */
38741     this.disabled = false;
38742     /** @private */
38743     this.text = config.text;
38744     /** @private */
38745     this.loaded = false;
38746     this.closable = config.closable;
38747
38748     /**
38749      * The body element for this TabPanelItem.
38750      * @type Roo.Element
38751      */
38752     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38753     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38754     this.bodyEl.setStyle("display", "block");
38755     this.bodyEl.setStyle("zoom", "1");
38756     //this.hideAction();
38757
38758     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38759     /** @private */
38760     this.el = Roo.get(els.el);
38761     this.inner = Roo.get(els.inner, true);
38762      this.textEl = Roo.bootstrap.version == 4 ?
38763         this.el : Roo.get(this.el.dom.firstChild, true);
38764
38765     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38766     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38767
38768     
38769 //    this.el.on("mousedown", this.onTabMouseDown, this);
38770     this.el.on("click", this.onTabClick, this);
38771     /** @private */
38772     if(config.closable){
38773         var c = Roo.get(els.close, true);
38774         c.dom.title = this.closeText;
38775         c.addClassOnOver("close-over");
38776         c.on("click", this.closeClick, this);
38777      }
38778
38779     this.addEvents({
38780          /**
38781          * @event activate
38782          * Fires when this tab becomes the active tab.
38783          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38784          * @param {Roo.TabPanelItem} this
38785          */
38786         "activate": true,
38787         /**
38788          * @event beforeclose
38789          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38790          * @param {Roo.TabPanelItem} this
38791          * @param {Object} e Set cancel to true on this object to cancel the close.
38792          */
38793         "beforeclose": true,
38794         /**
38795          * @event close
38796          * Fires when this tab is closed.
38797          * @param {Roo.TabPanelItem} this
38798          */
38799          "close": true,
38800         /**
38801          * @event deactivate
38802          * Fires when this tab is no longer the active tab.
38803          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38804          * @param {Roo.TabPanelItem} this
38805          */
38806          "deactivate" : true
38807     });
38808     this.hidden = false;
38809
38810     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38811 };
38812
38813 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38814            {
38815     purgeListeners : function(){
38816        Roo.util.Observable.prototype.purgeListeners.call(this);
38817        this.el.removeAllListeners();
38818     },
38819     /**
38820      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38821      */
38822     show : function(){
38823         this.status_node.addClass("active");
38824         this.showAction();
38825         if(Roo.isOpera){
38826             this.tabPanel.stripWrap.repaint();
38827         }
38828         this.fireEvent("activate", this.tabPanel, this);
38829     },
38830
38831     /**
38832      * Returns true if this tab is the active tab.
38833      * @return {Boolean}
38834      */
38835     isActive : function(){
38836         return this.tabPanel.getActiveTab() == this;
38837     },
38838
38839     /**
38840      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38841      */
38842     hide : function(){
38843         this.status_node.removeClass("active");
38844         this.hideAction();
38845         this.fireEvent("deactivate", this.tabPanel, this);
38846     },
38847
38848     hideAction : function(){
38849         this.bodyEl.hide();
38850         this.bodyEl.setStyle("position", "absolute");
38851         this.bodyEl.setLeft("-20000px");
38852         this.bodyEl.setTop("-20000px");
38853     },
38854
38855     showAction : function(){
38856         this.bodyEl.setStyle("position", "relative");
38857         this.bodyEl.setTop("");
38858         this.bodyEl.setLeft("");
38859         this.bodyEl.show();
38860     },
38861
38862     /**
38863      * Set the tooltip for the tab.
38864      * @param {String} tooltip The tab's tooltip
38865      */
38866     setTooltip : function(text){
38867         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38868             this.textEl.dom.qtip = text;
38869             this.textEl.dom.removeAttribute('title');
38870         }else{
38871             this.textEl.dom.title = text;
38872         }
38873     },
38874
38875     onTabClick : function(e){
38876         e.preventDefault();
38877         this.tabPanel.activate(this.id);
38878     },
38879
38880     onTabMouseDown : function(e){
38881         e.preventDefault();
38882         this.tabPanel.activate(this.id);
38883     },
38884 /*
38885     getWidth : function(){
38886         return this.inner.getWidth();
38887     },
38888
38889     setWidth : function(width){
38890         var iwidth = width - this.linode.getPadding("lr");
38891         this.inner.setWidth(iwidth);
38892         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38893         this.linode.setWidth(width);
38894     },
38895 */
38896     /**
38897      * Show or hide the tab
38898      * @param {Boolean} hidden True to hide or false to show.
38899      */
38900     setHidden : function(hidden){
38901         this.hidden = hidden;
38902         this.linode.setStyle("display", hidden ? "none" : "");
38903     },
38904
38905     /**
38906      * Returns true if this tab is "hidden"
38907      * @return {Boolean}
38908      */
38909     isHidden : function(){
38910         return this.hidden;
38911     },
38912
38913     /**
38914      * Returns the text for this tab
38915      * @return {String}
38916      */
38917     getText : function(){
38918         return this.text;
38919     },
38920     /*
38921     autoSize : function(){
38922         //this.el.beginMeasure();
38923         this.textEl.setWidth(1);
38924         /*
38925          *  #2804 [new] Tabs in Roojs
38926          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38927          */
38928         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38929         //this.el.endMeasure();
38930     //},
38931
38932     /**
38933      * Sets the text for the tab (Note: this also sets the tooltip text)
38934      * @param {String} text The tab's text and tooltip
38935      */
38936     setText : function(text){
38937         this.text = text;
38938         this.textEl.update(text);
38939         this.setTooltip(text);
38940         //if(!this.tabPanel.resizeTabs){
38941         //    this.autoSize();
38942         //}
38943     },
38944     /**
38945      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38946      */
38947     activate : function(){
38948         this.tabPanel.activate(this.id);
38949     },
38950
38951     /**
38952      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38953      */
38954     disable : function(){
38955         if(this.tabPanel.active != this){
38956             this.disabled = true;
38957             this.status_node.addClass("disabled");
38958         }
38959     },
38960
38961     /**
38962      * Enables this TabPanelItem if it was previously disabled.
38963      */
38964     enable : function(){
38965         this.disabled = false;
38966         this.status_node.removeClass("disabled");
38967     },
38968
38969     /**
38970      * Sets the content for this TabPanelItem.
38971      * @param {String} content The content
38972      * @param {Boolean} loadScripts true to look for and load scripts
38973      */
38974     setContent : function(content, loadScripts){
38975         this.bodyEl.update(content, loadScripts);
38976     },
38977
38978     /**
38979      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38980      * @return {Roo.UpdateManager} The UpdateManager
38981      */
38982     getUpdateManager : function(){
38983         return this.bodyEl.getUpdateManager();
38984     },
38985
38986     /**
38987      * Set a URL to be used to load the content for this TabPanelItem.
38988      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38989      * @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)
38990      * @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)
38991      * @return {Roo.UpdateManager} The UpdateManager
38992      */
38993     setUrl : function(url, params, loadOnce){
38994         if(this.refreshDelegate){
38995             this.un('activate', this.refreshDelegate);
38996         }
38997         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38998         this.on("activate", this.refreshDelegate);
38999         return this.bodyEl.getUpdateManager();
39000     },
39001
39002     /** @private */
39003     _handleRefresh : function(url, params, loadOnce){
39004         if(!loadOnce || !this.loaded){
39005             var updater = this.bodyEl.getUpdateManager();
39006             updater.update(url, params, this._setLoaded.createDelegate(this));
39007         }
39008     },
39009
39010     /**
39011      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39012      *   Will fail silently if the setUrl method has not been called.
39013      *   This does not activate the panel, just updates its content.
39014      */
39015     refresh : function(){
39016         if(this.refreshDelegate){
39017            this.loaded = false;
39018            this.refreshDelegate();
39019         }
39020     },
39021
39022     /** @private */
39023     _setLoaded : function(){
39024         this.loaded = true;
39025     },
39026
39027     /** @private */
39028     closeClick : function(e){
39029         var o = {};
39030         e.stopEvent();
39031         this.fireEvent("beforeclose", this, o);
39032         if(o.cancel !== true){
39033             this.tabPanel.removeTab(this.id);
39034         }
39035     },
39036     /**
39037      * The text displayed in the tooltip for the close icon.
39038      * @type String
39039      */
39040     closeText : "Close this tab"
39041 });
39042 /**
39043 *    This script refer to:
39044 *    Title: International Telephone Input
39045 *    Author: Jack O'Connor
39046 *    Code version:  v12.1.12
39047 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39048 **/
39049
39050 Roo.bootstrap.PhoneInputData = function() {
39051     var d = [
39052       [
39053         "Afghanistan (‫افغانستان‬‎)",
39054         "af",
39055         "93"
39056       ],
39057       [
39058         "Albania (Shqipëri)",
39059         "al",
39060         "355"
39061       ],
39062       [
39063         "Algeria (‫الجزائر‬‎)",
39064         "dz",
39065         "213"
39066       ],
39067       [
39068         "American Samoa",
39069         "as",
39070         "1684"
39071       ],
39072       [
39073         "Andorra",
39074         "ad",
39075         "376"
39076       ],
39077       [
39078         "Angola",
39079         "ao",
39080         "244"
39081       ],
39082       [
39083         "Anguilla",
39084         "ai",
39085         "1264"
39086       ],
39087       [
39088         "Antigua and Barbuda",
39089         "ag",
39090         "1268"
39091       ],
39092       [
39093         "Argentina",
39094         "ar",
39095         "54"
39096       ],
39097       [
39098         "Armenia (Հայաստան)",
39099         "am",
39100         "374"
39101       ],
39102       [
39103         "Aruba",
39104         "aw",
39105         "297"
39106       ],
39107       [
39108         "Australia",
39109         "au",
39110         "61",
39111         0
39112       ],
39113       [
39114         "Austria (Österreich)",
39115         "at",
39116         "43"
39117       ],
39118       [
39119         "Azerbaijan (Azərbaycan)",
39120         "az",
39121         "994"
39122       ],
39123       [
39124         "Bahamas",
39125         "bs",
39126         "1242"
39127       ],
39128       [
39129         "Bahrain (‫البحرين‬‎)",
39130         "bh",
39131         "973"
39132       ],
39133       [
39134         "Bangladesh (বাংলাদেশ)",
39135         "bd",
39136         "880"
39137       ],
39138       [
39139         "Barbados",
39140         "bb",
39141         "1246"
39142       ],
39143       [
39144         "Belarus (Беларусь)",
39145         "by",
39146         "375"
39147       ],
39148       [
39149         "Belgium (België)",
39150         "be",
39151         "32"
39152       ],
39153       [
39154         "Belize",
39155         "bz",
39156         "501"
39157       ],
39158       [
39159         "Benin (Bénin)",
39160         "bj",
39161         "229"
39162       ],
39163       [
39164         "Bermuda",
39165         "bm",
39166         "1441"
39167       ],
39168       [
39169         "Bhutan (འབྲུག)",
39170         "bt",
39171         "975"
39172       ],
39173       [
39174         "Bolivia",
39175         "bo",
39176         "591"
39177       ],
39178       [
39179         "Bosnia and Herzegovina (Босна и Херцеговина)",
39180         "ba",
39181         "387"
39182       ],
39183       [
39184         "Botswana",
39185         "bw",
39186         "267"
39187       ],
39188       [
39189         "Brazil (Brasil)",
39190         "br",
39191         "55"
39192       ],
39193       [
39194         "British Indian Ocean Territory",
39195         "io",
39196         "246"
39197       ],
39198       [
39199         "British Virgin Islands",
39200         "vg",
39201         "1284"
39202       ],
39203       [
39204         "Brunei",
39205         "bn",
39206         "673"
39207       ],
39208       [
39209         "Bulgaria (България)",
39210         "bg",
39211         "359"
39212       ],
39213       [
39214         "Burkina Faso",
39215         "bf",
39216         "226"
39217       ],
39218       [
39219         "Burundi (Uburundi)",
39220         "bi",
39221         "257"
39222       ],
39223       [
39224         "Cambodia (កម្ពុជា)",
39225         "kh",
39226         "855"
39227       ],
39228       [
39229         "Cameroon (Cameroun)",
39230         "cm",
39231         "237"
39232       ],
39233       [
39234         "Canada",
39235         "ca",
39236         "1",
39237         1,
39238         ["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"]
39239       ],
39240       [
39241         "Cape Verde (Kabu Verdi)",
39242         "cv",
39243         "238"
39244       ],
39245       [
39246         "Caribbean Netherlands",
39247         "bq",
39248         "599",
39249         1
39250       ],
39251       [
39252         "Cayman Islands",
39253         "ky",
39254         "1345"
39255       ],
39256       [
39257         "Central African Republic (République centrafricaine)",
39258         "cf",
39259         "236"
39260       ],
39261       [
39262         "Chad (Tchad)",
39263         "td",
39264         "235"
39265       ],
39266       [
39267         "Chile",
39268         "cl",
39269         "56"
39270       ],
39271       [
39272         "China (中国)",
39273         "cn",
39274         "86"
39275       ],
39276       [
39277         "Christmas Island",
39278         "cx",
39279         "61",
39280         2
39281       ],
39282       [
39283         "Cocos (Keeling) Islands",
39284         "cc",
39285         "61",
39286         1
39287       ],
39288       [
39289         "Colombia",
39290         "co",
39291         "57"
39292       ],
39293       [
39294         "Comoros (‫جزر القمر‬‎)",
39295         "km",
39296         "269"
39297       ],
39298       [
39299         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39300         "cd",
39301         "243"
39302       ],
39303       [
39304         "Congo (Republic) (Congo-Brazzaville)",
39305         "cg",
39306         "242"
39307       ],
39308       [
39309         "Cook Islands",
39310         "ck",
39311         "682"
39312       ],
39313       [
39314         "Costa Rica",
39315         "cr",
39316         "506"
39317       ],
39318       [
39319         "Côte d’Ivoire",
39320         "ci",
39321         "225"
39322       ],
39323       [
39324         "Croatia (Hrvatska)",
39325         "hr",
39326         "385"
39327       ],
39328       [
39329         "Cuba",
39330         "cu",
39331         "53"
39332       ],
39333       [
39334         "Curaçao",
39335         "cw",
39336         "599",
39337         0
39338       ],
39339       [
39340         "Cyprus (Κύπρος)",
39341         "cy",
39342         "357"
39343       ],
39344       [
39345         "Czech Republic (Česká republika)",
39346         "cz",
39347         "420"
39348       ],
39349       [
39350         "Denmark (Danmark)",
39351         "dk",
39352         "45"
39353       ],
39354       [
39355         "Djibouti",
39356         "dj",
39357         "253"
39358       ],
39359       [
39360         "Dominica",
39361         "dm",
39362         "1767"
39363       ],
39364       [
39365         "Dominican Republic (República Dominicana)",
39366         "do",
39367         "1",
39368         2,
39369         ["809", "829", "849"]
39370       ],
39371       [
39372         "Ecuador",
39373         "ec",
39374         "593"
39375       ],
39376       [
39377         "Egypt (‫مصر‬‎)",
39378         "eg",
39379         "20"
39380       ],
39381       [
39382         "El Salvador",
39383         "sv",
39384         "503"
39385       ],
39386       [
39387         "Equatorial Guinea (Guinea Ecuatorial)",
39388         "gq",
39389         "240"
39390       ],
39391       [
39392         "Eritrea",
39393         "er",
39394         "291"
39395       ],
39396       [
39397         "Estonia (Eesti)",
39398         "ee",
39399         "372"
39400       ],
39401       [
39402         "Ethiopia",
39403         "et",
39404         "251"
39405       ],
39406       [
39407         "Falkland Islands (Islas Malvinas)",
39408         "fk",
39409         "500"
39410       ],
39411       [
39412         "Faroe Islands (Føroyar)",
39413         "fo",
39414         "298"
39415       ],
39416       [
39417         "Fiji",
39418         "fj",
39419         "679"
39420       ],
39421       [
39422         "Finland (Suomi)",
39423         "fi",
39424         "358",
39425         0
39426       ],
39427       [
39428         "France",
39429         "fr",
39430         "33"
39431       ],
39432       [
39433         "French Guiana (Guyane française)",
39434         "gf",
39435         "594"
39436       ],
39437       [
39438         "French Polynesia (Polynésie française)",
39439         "pf",
39440         "689"
39441       ],
39442       [
39443         "Gabon",
39444         "ga",
39445         "241"
39446       ],
39447       [
39448         "Gambia",
39449         "gm",
39450         "220"
39451       ],
39452       [
39453         "Georgia (საქართველო)",
39454         "ge",
39455         "995"
39456       ],
39457       [
39458         "Germany (Deutschland)",
39459         "de",
39460         "49"
39461       ],
39462       [
39463         "Ghana (Gaana)",
39464         "gh",
39465         "233"
39466       ],
39467       [
39468         "Gibraltar",
39469         "gi",
39470         "350"
39471       ],
39472       [
39473         "Greece (Ελλάδα)",
39474         "gr",
39475         "30"
39476       ],
39477       [
39478         "Greenland (Kalaallit Nunaat)",
39479         "gl",
39480         "299"
39481       ],
39482       [
39483         "Grenada",
39484         "gd",
39485         "1473"
39486       ],
39487       [
39488         "Guadeloupe",
39489         "gp",
39490         "590",
39491         0
39492       ],
39493       [
39494         "Guam",
39495         "gu",
39496         "1671"
39497       ],
39498       [
39499         "Guatemala",
39500         "gt",
39501         "502"
39502       ],
39503       [
39504         "Guernsey",
39505         "gg",
39506         "44",
39507         1
39508       ],
39509       [
39510         "Guinea (Guinée)",
39511         "gn",
39512         "224"
39513       ],
39514       [
39515         "Guinea-Bissau (Guiné Bissau)",
39516         "gw",
39517         "245"
39518       ],
39519       [
39520         "Guyana",
39521         "gy",
39522         "592"
39523       ],
39524       [
39525         "Haiti",
39526         "ht",
39527         "509"
39528       ],
39529       [
39530         "Honduras",
39531         "hn",
39532         "504"
39533       ],
39534       [
39535         "Hong Kong (香港)",
39536         "hk",
39537         "852"
39538       ],
39539       [
39540         "Hungary (Magyarország)",
39541         "hu",
39542         "36"
39543       ],
39544       [
39545         "Iceland (Ísland)",
39546         "is",
39547         "354"
39548       ],
39549       [
39550         "India (भारत)",
39551         "in",
39552         "91"
39553       ],
39554       [
39555         "Indonesia",
39556         "id",
39557         "62"
39558       ],
39559       [
39560         "Iran (‫ایران‬‎)",
39561         "ir",
39562         "98"
39563       ],
39564       [
39565         "Iraq (‫العراق‬‎)",
39566         "iq",
39567         "964"
39568       ],
39569       [
39570         "Ireland",
39571         "ie",
39572         "353"
39573       ],
39574       [
39575         "Isle of Man",
39576         "im",
39577         "44",
39578         2
39579       ],
39580       [
39581         "Israel (‫ישראל‬‎)",
39582         "il",
39583         "972"
39584       ],
39585       [
39586         "Italy (Italia)",
39587         "it",
39588         "39",
39589         0
39590       ],
39591       [
39592         "Jamaica",
39593         "jm",
39594         "1876"
39595       ],
39596       [
39597         "Japan (日本)",
39598         "jp",
39599         "81"
39600       ],
39601       [
39602         "Jersey",
39603         "je",
39604         "44",
39605         3
39606       ],
39607       [
39608         "Jordan (‫الأردن‬‎)",
39609         "jo",
39610         "962"
39611       ],
39612       [
39613         "Kazakhstan (Казахстан)",
39614         "kz",
39615         "7",
39616         1
39617       ],
39618       [
39619         "Kenya",
39620         "ke",
39621         "254"
39622       ],
39623       [
39624         "Kiribati",
39625         "ki",
39626         "686"
39627       ],
39628       [
39629         "Kosovo",
39630         "xk",
39631         "383"
39632       ],
39633       [
39634         "Kuwait (‫الكويت‬‎)",
39635         "kw",
39636         "965"
39637       ],
39638       [
39639         "Kyrgyzstan (Кыргызстан)",
39640         "kg",
39641         "996"
39642       ],
39643       [
39644         "Laos (ລາວ)",
39645         "la",
39646         "856"
39647       ],
39648       [
39649         "Latvia (Latvija)",
39650         "lv",
39651         "371"
39652       ],
39653       [
39654         "Lebanon (‫لبنان‬‎)",
39655         "lb",
39656         "961"
39657       ],
39658       [
39659         "Lesotho",
39660         "ls",
39661         "266"
39662       ],
39663       [
39664         "Liberia",
39665         "lr",
39666         "231"
39667       ],
39668       [
39669         "Libya (‫ليبيا‬‎)",
39670         "ly",
39671         "218"
39672       ],
39673       [
39674         "Liechtenstein",
39675         "li",
39676         "423"
39677       ],
39678       [
39679         "Lithuania (Lietuva)",
39680         "lt",
39681         "370"
39682       ],
39683       [
39684         "Luxembourg",
39685         "lu",
39686         "352"
39687       ],
39688       [
39689         "Macau (澳門)",
39690         "mo",
39691         "853"
39692       ],
39693       [
39694         "Macedonia (FYROM) (Македонија)",
39695         "mk",
39696         "389"
39697       ],
39698       [
39699         "Madagascar (Madagasikara)",
39700         "mg",
39701         "261"
39702       ],
39703       [
39704         "Malawi",
39705         "mw",
39706         "265"
39707       ],
39708       [
39709         "Malaysia",
39710         "my",
39711         "60"
39712       ],
39713       [
39714         "Maldives",
39715         "mv",
39716         "960"
39717       ],
39718       [
39719         "Mali",
39720         "ml",
39721         "223"
39722       ],
39723       [
39724         "Malta",
39725         "mt",
39726         "356"
39727       ],
39728       [
39729         "Marshall Islands",
39730         "mh",
39731         "692"
39732       ],
39733       [
39734         "Martinique",
39735         "mq",
39736         "596"
39737       ],
39738       [
39739         "Mauritania (‫موريتانيا‬‎)",
39740         "mr",
39741         "222"
39742       ],
39743       [
39744         "Mauritius (Moris)",
39745         "mu",
39746         "230"
39747       ],
39748       [
39749         "Mayotte",
39750         "yt",
39751         "262",
39752         1
39753       ],
39754       [
39755         "Mexico (México)",
39756         "mx",
39757         "52"
39758       ],
39759       [
39760         "Micronesia",
39761         "fm",
39762         "691"
39763       ],
39764       [
39765         "Moldova (Republica Moldova)",
39766         "md",
39767         "373"
39768       ],
39769       [
39770         "Monaco",
39771         "mc",
39772         "377"
39773       ],
39774       [
39775         "Mongolia (Монгол)",
39776         "mn",
39777         "976"
39778       ],
39779       [
39780         "Montenegro (Crna Gora)",
39781         "me",
39782         "382"
39783       ],
39784       [
39785         "Montserrat",
39786         "ms",
39787         "1664"
39788       ],
39789       [
39790         "Morocco (‫المغرب‬‎)",
39791         "ma",
39792         "212",
39793         0
39794       ],
39795       [
39796         "Mozambique (Moçambique)",
39797         "mz",
39798         "258"
39799       ],
39800       [
39801         "Myanmar (Burma) (မြန်မာ)",
39802         "mm",
39803         "95"
39804       ],
39805       [
39806         "Namibia (Namibië)",
39807         "na",
39808         "264"
39809       ],
39810       [
39811         "Nauru",
39812         "nr",
39813         "674"
39814       ],
39815       [
39816         "Nepal (नेपाल)",
39817         "np",
39818         "977"
39819       ],
39820       [
39821         "Netherlands (Nederland)",
39822         "nl",
39823         "31"
39824       ],
39825       [
39826         "New Caledonia (Nouvelle-Calédonie)",
39827         "nc",
39828         "687"
39829       ],
39830       [
39831         "New Zealand",
39832         "nz",
39833         "64"
39834       ],
39835       [
39836         "Nicaragua",
39837         "ni",
39838         "505"
39839       ],
39840       [
39841         "Niger (Nijar)",
39842         "ne",
39843         "227"
39844       ],
39845       [
39846         "Nigeria",
39847         "ng",
39848         "234"
39849       ],
39850       [
39851         "Niue",
39852         "nu",
39853         "683"
39854       ],
39855       [
39856         "Norfolk Island",
39857         "nf",
39858         "672"
39859       ],
39860       [
39861         "North Korea (조선 민주주의 인민 공화국)",
39862         "kp",
39863         "850"
39864       ],
39865       [
39866         "Northern Mariana Islands",
39867         "mp",
39868         "1670"
39869       ],
39870       [
39871         "Norway (Norge)",
39872         "no",
39873         "47",
39874         0
39875       ],
39876       [
39877         "Oman (‫عُمان‬‎)",
39878         "om",
39879         "968"
39880       ],
39881       [
39882         "Pakistan (‫پاکستان‬‎)",
39883         "pk",
39884         "92"
39885       ],
39886       [
39887         "Palau",
39888         "pw",
39889         "680"
39890       ],
39891       [
39892         "Palestine (‫فلسطين‬‎)",
39893         "ps",
39894         "970"
39895       ],
39896       [
39897         "Panama (Panamá)",
39898         "pa",
39899         "507"
39900       ],
39901       [
39902         "Papua New Guinea",
39903         "pg",
39904         "675"
39905       ],
39906       [
39907         "Paraguay",
39908         "py",
39909         "595"
39910       ],
39911       [
39912         "Peru (Perú)",
39913         "pe",
39914         "51"
39915       ],
39916       [
39917         "Philippines",
39918         "ph",
39919         "63"
39920       ],
39921       [
39922         "Poland (Polska)",
39923         "pl",
39924         "48"
39925       ],
39926       [
39927         "Portugal",
39928         "pt",
39929         "351"
39930       ],
39931       [
39932         "Puerto Rico",
39933         "pr",
39934         "1",
39935         3,
39936         ["787", "939"]
39937       ],
39938       [
39939         "Qatar (‫قطر‬‎)",
39940         "qa",
39941         "974"
39942       ],
39943       [
39944         "Réunion (La Réunion)",
39945         "re",
39946         "262",
39947         0
39948       ],
39949       [
39950         "Romania (România)",
39951         "ro",
39952         "40"
39953       ],
39954       [
39955         "Russia (Россия)",
39956         "ru",
39957         "7",
39958         0
39959       ],
39960       [
39961         "Rwanda",
39962         "rw",
39963         "250"
39964       ],
39965       [
39966         "Saint Barthélemy",
39967         "bl",
39968         "590",
39969         1
39970       ],
39971       [
39972         "Saint Helena",
39973         "sh",
39974         "290"
39975       ],
39976       [
39977         "Saint Kitts and Nevis",
39978         "kn",
39979         "1869"
39980       ],
39981       [
39982         "Saint Lucia",
39983         "lc",
39984         "1758"
39985       ],
39986       [
39987         "Saint Martin (Saint-Martin (partie française))",
39988         "mf",
39989         "590",
39990         2
39991       ],
39992       [
39993         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39994         "pm",
39995         "508"
39996       ],
39997       [
39998         "Saint Vincent and the Grenadines",
39999         "vc",
40000         "1784"
40001       ],
40002       [
40003         "Samoa",
40004         "ws",
40005         "685"
40006       ],
40007       [
40008         "San Marino",
40009         "sm",
40010         "378"
40011       ],
40012       [
40013         "São Tomé and Príncipe (São Tomé e Príncipe)",
40014         "st",
40015         "239"
40016       ],
40017       [
40018         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40019         "sa",
40020         "966"
40021       ],
40022       [
40023         "Senegal (Sénégal)",
40024         "sn",
40025         "221"
40026       ],
40027       [
40028         "Serbia (Србија)",
40029         "rs",
40030         "381"
40031       ],
40032       [
40033         "Seychelles",
40034         "sc",
40035         "248"
40036       ],
40037       [
40038         "Sierra Leone",
40039         "sl",
40040         "232"
40041       ],
40042       [
40043         "Singapore",
40044         "sg",
40045         "65"
40046       ],
40047       [
40048         "Sint Maarten",
40049         "sx",
40050         "1721"
40051       ],
40052       [
40053         "Slovakia (Slovensko)",
40054         "sk",
40055         "421"
40056       ],
40057       [
40058         "Slovenia (Slovenija)",
40059         "si",
40060         "386"
40061       ],
40062       [
40063         "Solomon Islands",
40064         "sb",
40065         "677"
40066       ],
40067       [
40068         "Somalia (Soomaaliya)",
40069         "so",
40070         "252"
40071       ],
40072       [
40073         "South Africa",
40074         "za",
40075         "27"
40076       ],
40077       [
40078         "South Korea (대한민국)",
40079         "kr",
40080         "82"
40081       ],
40082       [
40083         "South Sudan (‫جنوب السودان‬‎)",
40084         "ss",
40085         "211"
40086       ],
40087       [
40088         "Spain (España)",
40089         "es",
40090         "34"
40091       ],
40092       [
40093         "Sri Lanka (ශ්‍රී ලංකාව)",
40094         "lk",
40095         "94"
40096       ],
40097       [
40098         "Sudan (‫السودان‬‎)",
40099         "sd",
40100         "249"
40101       ],
40102       [
40103         "Suriname",
40104         "sr",
40105         "597"
40106       ],
40107       [
40108         "Svalbard and Jan Mayen",
40109         "sj",
40110         "47",
40111         1
40112       ],
40113       [
40114         "Swaziland",
40115         "sz",
40116         "268"
40117       ],
40118       [
40119         "Sweden (Sverige)",
40120         "se",
40121         "46"
40122       ],
40123       [
40124         "Switzerland (Schweiz)",
40125         "ch",
40126         "41"
40127       ],
40128       [
40129         "Syria (‫سوريا‬‎)",
40130         "sy",
40131         "963"
40132       ],
40133       [
40134         "Taiwan (台灣)",
40135         "tw",
40136         "886"
40137       ],
40138       [
40139         "Tajikistan",
40140         "tj",
40141         "992"
40142       ],
40143       [
40144         "Tanzania",
40145         "tz",
40146         "255"
40147       ],
40148       [
40149         "Thailand (ไทย)",
40150         "th",
40151         "66"
40152       ],
40153       [
40154         "Timor-Leste",
40155         "tl",
40156         "670"
40157       ],
40158       [
40159         "Togo",
40160         "tg",
40161         "228"
40162       ],
40163       [
40164         "Tokelau",
40165         "tk",
40166         "690"
40167       ],
40168       [
40169         "Tonga",
40170         "to",
40171         "676"
40172       ],
40173       [
40174         "Trinidad and Tobago",
40175         "tt",
40176         "1868"
40177       ],
40178       [
40179         "Tunisia (‫تونس‬‎)",
40180         "tn",
40181         "216"
40182       ],
40183       [
40184         "Turkey (Türkiye)",
40185         "tr",
40186         "90"
40187       ],
40188       [
40189         "Turkmenistan",
40190         "tm",
40191         "993"
40192       ],
40193       [
40194         "Turks and Caicos Islands",
40195         "tc",
40196         "1649"
40197       ],
40198       [
40199         "Tuvalu",
40200         "tv",
40201         "688"
40202       ],
40203       [
40204         "U.S. Virgin Islands",
40205         "vi",
40206         "1340"
40207       ],
40208       [
40209         "Uganda",
40210         "ug",
40211         "256"
40212       ],
40213       [
40214         "Ukraine (Україна)",
40215         "ua",
40216         "380"
40217       ],
40218       [
40219         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40220         "ae",
40221         "971"
40222       ],
40223       [
40224         "United Kingdom",
40225         "gb",
40226         "44",
40227         0
40228       ],
40229       [
40230         "United States",
40231         "us",
40232         "1",
40233         0
40234       ],
40235       [
40236         "Uruguay",
40237         "uy",
40238         "598"
40239       ],
40240       [
40241         "Uzbekistan (Oʻzbekiston)",
40242         "uz",
40243         "998"
40244       ],
40245       [
40246         "Vanuatu",
40247         "vu",
40248         "678"
40249       ],
40250       [
40251         "Vatican City (Città del Vaticano)",
40252         "va",
40253         "39",
40254         1
40255       ],
40256       [
40257         "Venezuela",
40258         "ve",
40259         "58"
40260       ],
40261       [
40262         "Vietnam (Việt Nam)",
40263         "vn",
40264         "84"
40265       ],
40266       [
40267         "Wallis and Futuna (Wallis-et-Futuna)",
40268         "wf",
40269         "681"
40270       ],
40271       [
40272         "Western Sahara (‫الصحراء الغربية‬‎)",
40273         "eh",
40274         "212",
40275         1
40276       ],
40277       [
40278         "Yemen (‫اليمن‬‎)",
40279         "ye",
40280         "967"
40281       ],
40282       [
40283         "Zambia",
40284         "zm",
40285         "260"
40286       ],
40287       [
40288         "Zimbabwe",
40289         "zw",
40290         "263"
40291       ],
40292       [
40293         "Åland Islands",
40294         "ax",
40295         "358",
40296         1
40297       ]
40298   ];
40299   
40300   return d;
40301 }/**
40302 *    This script refer to:
40303 *    Title: International Telephone Input
40304 *    Author: Jack O'Connor
40305 *    Code version:  v12.1.12
40306 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40307 **/
40308
40309 /**
40310  * @class Roo.bootstrap.PhoneInput
40311  * @extends Roo.bootstrap.TriggerField
40312  * An input with International dial-code selection
40313  
40314  * @cfg {String} defaultDialCode default '+852'
40315  * @cfg {Array} preferedCountries default []
40316   
40317  * @constructor
40318  * Create a new PhoneInput.
40319  * @param {Object} config Configuration options
40320  */
40321
40322 Roo.bootstrap.PhoneInput = function(config) {
40323     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40324 };
40325
40326 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40327         
40328         listWidth: undefined,
40329         
40330         selectedClass: 'active',
40331         
40332         invalidClass : "has-warning",
40333         
40334         validClass: 'has-success',
40335         
40336         allowed: '0123456789',
40337         
40338         max_length: 15,
40339         
40340         /**
40341          * @cfg {String} defaultDialCode The default dial code when initializing the input
40342          */
40343         defaultDialCode: '+852',
40344         
40345         /**
40346          * @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
40347          */
40348         preferedCountries: false,
40349         
40350         getAutoCreate : function()
40351         {
40352             var data = Roo.bootstrap.PhoneInputData();
40353             var align = this.labelAlign || this.parentLabelAlign();
40354             var id = Roo.id();
40355             
40356             this.allCountries = [];
40357             this.dialCodeMapping = [];
40358             
40359             for (var i = 0; i < data.length; i++) {
40360               var c = data[i];
40361               this.allCountries[i] = {
40362                 name: c[0],
40363                 iso2: c[1],
40364                 dialCode: c[2],
40365                 priority: c[3] || 0,
40366                 areaCodes: c[4] || null
40367               };
40368               this.dialCodeMapping[c[2]] = {
40369                   name: c[0],
40370                   iso2: c[1],
40371                   priority: c[3] || 0,
40372                   areaCodes: c[4] || null
40373               };
40374             }
40375             
40376             var cfg = {
40377                 cls: 'form-group',
40378                 cn: []
40379             };
40380             
40381             var input =  {
40382                 tag: 'input',
40383                 id : id,
40384                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40385                 maxlength: this.max_length,
40386                 cls : 'form-control tel-input',
40387                 autocomplete: 'new-password'
40388             };
40389             
40390             var hiddenInput = {
40391                 tag: 'input',
40392                 type: 'hidden',
40393                 cls: 'hidden-tel-input'
40394             };
40395             
40396             if (this.name) {
40397                 hiddenInput.name = this.name;
40398             }
40399             
40400             if (this.disabled) {
40401                 input.disabled = true;
40402             }
40403             
40404             var flag_container = {
40405                 tag: 'div',
40406                 cls: 'flag-box',
40407                 cn: [
40408                     {
40409                         tag: 'div',
40410                         cls: 'flag'
40411                     },
40412                     {
40413                         tag: 'div',
40414                         cls: 'caret'
40415                     }
40416                 ]
40417             };
40418             
40419             var box = {
40420                 tag: 'div',
40421                 cls: this.hasFeedback ? 'has-feedback' : '',
40422                 cn: [
40423                     hiddenInput,
40424                     input,
40425                     {
40426                         tag: 'input',
40427                         cls: 'dial-code-holder',
40428                         disabled: true
40429                     }
40430                 ]
40431             };
40432             
40433             var container = {
40434                 cls: 'roo-select2-container input-group',
40435                 cn: [
40436                     flag_container,
40437                     box
40438                 ]
40439             };
40440             
40441             if (this.fieldLabel.length) {
40442                 var indicator = {
40443                     tag: 'i',
40444                     tooltip: 'This field is required'
40445                 };
40446                 
40447                 var label = {
40448                     tag: 'label',
40449                     'for':  id,
40450                     cls: 'control-label',
40451                     cn: []
40452                 };
40453                 
40454                 var label_text = {
40455                     tag: 'span',
40456                     html: this.fieldLabel
40457                 };
40458                 
40459                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40460                 label.cn = [
40461                     indicator,
40462                     label_text
40463                 ];
40464                 
40465                 if(this.indicatorpos == 'right') {
40466                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40467                     label.cn = [
40468                         label_text,
40469                         indicator
40470                     ];
40471                 }
40472                 
40473                 if(align == 'left') {
40474                     container = {
40475                         tag: 'div',
40476                         cn: [
40477                             container
40478                         ]
40479                     };
40480                     
40481                     if(this.labelWidth > 12){
40482                         label.style = "width: " + this.labelWidth + 'px';
40483                     }
40484                     if(this.labelWidth < 13 && this.labelmd == 0){
40485                         this.labelmd = this.labelWidth;
40486                     }
40487                     if(this.labellg > 0){
40488                         label.cls += ' col-lg-' + this.labellg;
40489                         input.cls += ' col-lg-' + (12 - this.labellg);
40490                     }
40491                     if(this.labelmd > 0){
40492                         label.cls += ' col-md-' + this.labelmd;
40493                         container.cls += ' col-md-' + (12 - this.labelmd);
40494                     }
40495                     if(this.labelsm > 0){
40496                         label.cls += ' col-sm-' + this.labelsm;
40497                         container.cls += ' col-sm-' + (12 - this.labelsm);
40498                     }
40499                     if(this.labelxs > 0){
40500                         label.cls += ' col-xs-' + this.labelxs;
40501                         container.cls += ' col-xs-' + (12 - this.labelxs);
40502                     }
40503                 }
40504             }
40505             
40506             cfg.cn = [
40507                 label,
40508                 container
40509             ];
40510             
40511             var settings = this;
40512             
40513             ['xs','sm','md','lg'].map(function(size){
40514                 if (settings[size]) {
40515                     cfg.cls += ' col-' + size + '-' + settings[size];
40516                 }
40517             });
40518             
40519             this.store = new Roo.data.Store({
40520                 proxy : new Roo.data.MemoryProxy({}),
40521                 reader : new Roo.data.JsonReader({
40522                     fields : [
40523                         {
40524                             'name' : 'name',
40525                             'type' : 'string'
40526                         },
40527                         {
40528                             'name' : 'iso2',
40529                             'type' : 'string'
40530                         },
40531                         {
40532                             'name' : 'dialCode',
40533                             'type' : 'string'
40534                         },
40535                         {
40536                             'name' : 'priority',
40537                             'type' : 'string'
40538                         },
40539                         {
40540                             'name' : 'areaCodes',
40541                             'type' : 'string'
40542                         }
40543                     ]
40544                 })
40545             });
40546             
40547             if(!this.preferedCountries) {
40548                 this.preferedCountries = [
40549                     'hk',
40550                     'gb',
40551                     'us'
40552                 ];
40553             }
40554             
40555             var p = this.preferedCountries.reverse();
40556             
40557             if(p) {
40558                 for (var i = 0; i < p.length; i++) {
40559                     for (var j = 0; j < this.allCountries.length; j++) {
40560                         if(this.allCountries[j].iso2 == p[i]) {
40561                             var t = this.allCountries[j];
40562                             this.allCountries.splice(j,1);
40563                             this.allCountries.unshift(t);
40564                         }
40565                     } 
40566                 }
40567             }
40568             
40569             this.store.proxy.data = {
40570                 success: true,
40571                 data: this.allCountries
40572             };
40573             
40574             return cfg;
40575         },
40576         
40577         initEvents : function()
40578         {
40579             this.createList();
40580             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40581             
40582             this.indicator = this.indicatorEl();
40583             this.flag = this.flagEl();
40584             this.dialCodeHolder = this.dialCodeHolderEl();
40585             
40586             this.trigger = this.el.select('div.flag-box',true).first();
40587             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40588             
40589             var _this = this;
40590             
40591             (function(){
40592                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40593                 _this.list.setWidth(lw);
40594             }).defer(100);
40595             
40596             this.list.on('mouseover', this.onViewOver, this);
40597             this.list.on('mousemove', this.onViewMove, this);
40598             this.inputEl().on("keyup", this.onKeyUp, this);
40599             this.inputEl().on("keypress", this.onKeyPress, this);
40600             
40601             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40602
40603             this.view = new Roo.View(this.list, this.tpl, {
40604                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40605             });
40606             
40607             this.view.on('click', this.onViewClick, this);
40608             this.setValue(this.defaultDialCode);
40609         },
40610         
40611         onTriggerClick : function(e)
40612         {
40613             Roo.log('trigger click');
40614             if(this.disabled){
40615                 return;
40616             }
40617             
40618             if(this.isExpanded()){
40619                 this.collapse();
40620                 this.hasFocus = false;
40621             }else {
40622                 this.store.load({});
40623                 this.hasFocus = true;
40624                 this.expand();
40625             }
40626         },
40627         
40628         isExpanded : function()
40629         {
40630             return this.list.isVisible();
40631         },
40632         
40633         collapse : function()
40634         {
40635             if(!this.isExpanded()){
40636                 return;
40637             }
40638             this.list.hide();
40639             Roo.get(document).un('mousedown', this.collapseIf, this);
40640             Roo.get(document).un('mousewheel', this.collapseIf, this);
40641             this.fireEvent('collapse', this);
40642             this.validate();
40643         },
40644         
40645         expand : function()
40646         {
40647             Roo.log('expand');
40648
40649             if(this.isExpanded() || !this.hasFocus){
40650                 return;
40651             }
40652             
40653             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40654             this.list.setWidth(lw);
40655             
40656             this.list.show();
40657             this.restrictHeight();
40658             
40659             Roo.get(document).on('mousedown', this.collapseIf, this);
40660             Roo.get(document).on('mousewheel', this.collapseIf, this);
40661             
40662             this.fireEvent('expand', this);
40663         },
40664         
40665         restrictHeight : function()
40666         {
40667             this.list.alignTo(this.inputEl(), this.listAlign);
40668             this.list.alignTo(this.inputEl(), this.listAlign);
40669         },
40670         
40671         onViewOver : function(e, t)
40672         {
40673             if(this.inKeyMode){
40674                 return;
40675             }
40676             var item = this.view.findItemFromChild(t);
40677             
40678             if(item){
40679                 var index = this.view.indexOf(item);
40680                 this.select(index, false);
40681             }
40682         },
40683
40684         // private
40685         onViewClick : function(view, doFocus, el, e)
40686         {
40687             var index = this.view.getSelectedIndexes()[0];
40688             
40689             var r = this.store.getAt(index);
40690             
40691             if(r){
40692                 this.onSelect(r, index);
40693             }
40694             if(doFocus !== false && !this.blockFocus){
40695                 this.inputEl().focus();
40696             }
40697         },
40698         
40699         onViewMove : function(e, t)
40700         {
40701             this.inKeyMode = false;
40702         },
40703         
40704         select : function(index, scrollIntoView)
40705         {
40706             this.selectedIndex = index;
40707             this.view.select(index);
40708             if(scrollIntoView !== false){
40709                 var el = this.view.getNode(index);
40710                 if(el){
40711                     this.list.scrollChildIntoView(el, false);
40712                 }
40713             }
40714         },
40715         
40716         createList : function()
40717         {
40718             this.list = Roo.get(document.body).createChild({
40719                 tag: 'ul',
40720                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40721                 style: 'display:none'
40722             });
40723             
40724             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40725         },
40726         
40727         collapseIf : function(e)
40728         {
40729             var in_combo  = e.within(this.el);
40730             var in_list =  e.within(this.list);
40731             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40732             
40733             if (in_combo || in_list || is_list) {
40734                 return;
40735             }
40736             this.collapse();
40737         },
40738         
40739         onSelect : function(record, index)
40740         {
40741             if(this.fireEvent('beforeselect', this, record, index) !== false){
40742                 
40743                 this.setFlagClass(record.data.iso2);
40744                 this.setDialCode(record.data.dialCode);
40745                 this.hasFocus = false;
40746                 this.collapse();
40747                 this.fireEvent('select', this, record, index);
40748             }
40749         },
40750         
40751         flagEl : function()
40752         {
40753             var flag = this.el.select('div.flag',true).first();
40754             if(!flag){
40755                 return false;
40756             }
40757             return flag;
40758         },
40759         
40760         dialCodeHolderEl : function()
40761         {
40762             var d = this.el.select('input.dial-code-holder',true).first();
40763             if(!d){
40764                 return false;
40765             }
40766             return d;
40767         },
40768         
40769         setDialCode : function(v)
40770         {
40771             this.dialCodeHolder.dom.value = '+'+v;
40772         },
40773         
40774         setFlagClass : function(n)
40775         {
40776             this.flag.dom.className = 'flag '+n;
40777         },
40778         
40779         getValue : function()
40780         {
40781             var v = this.inputEl().getValue();
40782             if(this.dialCodeHolder) {
40783                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40784             }
40785             return v;
40786         },
40787         
40788         setValue : function(v)
40789         {
40790             var d = this.getDialCode(v);
40791             
40792             //invalid dial code
40793             if(v.length == 0 || !d || d.length == 0) {
40794                 if(this.rendered){
40795                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40796                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40797                 }
40798                 return;
40799             }
40800             
40801             //valid dial code
40802             this.setFlagClass(this.dialCodeMapping[d].iso2);
40803             this.setDialCode(d);
40804             this.inputEl().dom.value = v.replace('+'+d,'');
40805             this.hiddenEl().dom.value = this.getValue();
40806             
40807             this.validate();
40808         },
40809         
40810         getDialCode : function(v)
40811         {
40812             v = v ||  '';
40813             
40814             if (v.length == 0) {
40815                 return this.dialCodeHolder.dom.value;
40816             }
40817             
40818             var dialCode = "";
40819             if (v.charAt(0) != "+") {
40820                 return false;
40821             }
40822             var numericChars = "";
40823             for (var i = 1; i < v.length; i++) {
40824               var c = v.charAt(i);
40825               if (!isNaN(c)) {
40826                 numericChars += c;
40827                 if (this.dialCodeMapping[numericChars]) {
40828                   dialCode = v.substr(1, i);
40829                 }
40830                 if (numericChars.length == 4) {
40831                   break;
40832                 }
40833               }
40834             }
40835             return dialCode;
40836         },
40837         
40838         reset : function()
40839         {
40840             this.setValue(this.defaultDialCode);
40841             this.validate();
40842         },
40843         
40844         hiddenEl : function()
40845         {
40846             return this.el.select('input.hidden-tel-input',true).first();
40847         },
40848         
40849         // after setting val
40850         onKeyUp : function(e){
40851             this.setValue(this.getValue());
40852         },
40853         
40854         onKeyPress : function(e){
40855             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40856                 e.stopEvent();
40857             }
40858         }
40859         
40860 });
40861 /**
40862  * @class Roo.bootstrap.MoneyField
40863  * @extends Roo.bootstrap.ComboBox
40864  * Bootstrap MoneyField class
40865  * 
40866  * @constructor
40867  * Create a new MoneyField.
40868  * @param {Object} config Configuration options
40869  */
40870
40871 Roo.bootstrap.MoneyField = function(config) {
40872     
40873     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40874     
40875 };
40876
40877 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40878     
40879     /**
40880      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40881      */
40882     allowDecimals : true,
40883     /**
40884      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40885      */
40886     decimalSeparator : ".",
40887     /**
40888      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40889      */
40890     decimalPrecision : 0,
40891     /**
40892      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40893      */
40894     allowNegative : true,
40895     /**
40896      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40897      */
40898     allowZero: true,
40899     /**
40900      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40901      */
40902     minValue : Number.NEGATIVE_INFINITY,
40903     /**
40904      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40905      */
40906     maxValue : Number.MAX_VALUE,
40907     /**
40908      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40909      */
40910     minText : "The minimum value for this field is {0}",
40911     /**
40912      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40913      */
40914     maxText : "The maximum value for this field is {0}",
40915     /**
40916      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40917      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40918      */
40919     nanText : "{0} is not a valid number",
40920     /**
40921      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40922      */
40923     castInt : true,
40924     /**
40925      * @cfg {String} defaults currency of the MoneyField
40926      * value should be in lkey
40927      */
40928     defaultCurrency : false,
40929     /**
40930      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40931      */
40932     thousandsDelimiter : false,
40933     /**
40934      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40935      */
40936     max_length: false,
40937     
40938     inputlg : 9,
40939     inputmd : 9,
40940     inputsm : 9,
40941     inputxs : 6,
40942     
40943     store : false,
40944     
40945     getAutoCreate : function()
40946     {
40947         var align = this.labelAlign || this.parentLabelAlign();
40948         
40949         var id = Roo.id();
40950
40951         var cfg = {
40952             cls: 'form-group',
40953             cn: []
40954         };
40955
40956         var input =  {
40957             tag: 'input',
40958             id : id,
40959             cls : 'form-control roo-money-amount-input',
40960             autocomplete: 'new-password'
40961         };
40962         
40963         var hiddenInput = {
40964             tag: 'input',
40965             type: 'hidden',
40966             id: Roo.id(),
40967             cls: 'hidden-number-input'
40968         };
40969         
40970         if(this.max_length) {
40971             input.maxlength = this.max_length; 
40972         }
40973         
40974         if (this.name) {
40975             hiddenInput.name = this.name;
40976         }
40977
40978         if (this.disabled) {
40979             input.disabled = true;
40980         }
40981
40982         var clg = 12 - this.inputlg;
40983         var cmd = 12 - this.inputmd;
40984         var csm = 12 - this.inputsm;
40985         var cxs = 12 - this.inputxs;
40986         
40987         var container = {
40988             tag : 'div',
40989             cls : 'row roo-money-field',
40990             cn : [
40991                 {
40992                     tag : 'div',
40993                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40994                     cn : [
40995                         {
40996                             tag : 'div',
40997                             cls: 'roo-select2-container input-group',
40998                             cn: [
40999                                 {
41000                                     tag : 'input',
41001                                     cls : 'form-control roo-money-currency-input',
41002                                     autocomplete: 'new-password',
41003                                     readOnly : 1,
41004                                     name : this.currencyName
41005                                 },
41006                                 {
41007                                     tag :'span',
41008                                     cls : 'input-group-addon',
41009                                     cn : [
41010                                         {
41011                                             tag: 'span',
41012                                             cls: 'caret'
41013                                         }
41014                                     ]
41015                                 }
41016                             ]
41017                         }
41018                     ]
41019                 },
41020                 {
41021                     tag : 'div',
41022                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41023                     cn : [
41024                         {
41025                             tag: 'div',
41026                             cls: this.hasFeedback ? 'has-feedback' : '',
41027                             cn: [
41028                                 input
41029                             ]
41030                         }
41031                     ]
41032                 }
41033             ]
41034             
41035         };
41036         
41037         if (this.fieldLabel.length) {
41038             var indicator = {
41039                 tag: 'i',
41040                 tooltip: 'This field is required'
41041             };
41042
41043             var label = {
41044                 tag: 'label',
41045                 'for':  id,
41046                 cls: 'control-label',
41047                 cn: []
41048             };
41049
41050             var label_text = {
41051                 tag: 'span',
41052                 html: this.fieldLabel
41053             };
41054
41055             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41056             label.cn = [
41057                 indicator,
41058                 label_text
41059             ];
41060
41061             if(this.indicatorpos == 'right') {
41062                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41063                 label.cn = [
41064                     label_text,
41065                     indicator
41066                 ];
41067             }
41068
41069             if(align == 'left') {
41070                 container = {
41071                     tag: 'div',
41072                     cn: [
41073                         container
41074                     ]
41075                 };
41076
41077                 if(this.labelWidth > 12){
41078                     label.style = "width: " + this.labelWidth + 'px';
41079                 }
41080                 if(this.labelWidth < 13 && this.labelmd == 0){
41081                     this.labelmd = this.labelWidth;
41082                 }
41083                 if(this.labellg > 0){
41084                     label.cls += ' col-lg-' + this.labellg;
41085                     input.cls += ' col-lg-' + (12 - this.labellg);
41086                 }
41087                 if(this.labelmd > 0){
41088                     label.cls += ' col-md-' + this.labelmd;
41089                     container.cls += ' col-md-' + (12 - this.labelmd);
41090                 }
41091                 if(this.labelsm > 0){
41092                     label.cls += ' col-sm-' + this.labelsm;
41093                     container.cls += ' col-sm-' + (12 - this.labelsm);
41094                 }
41095                 if(this.labelxs > 0){
41096                     label.cls += ' col-xs-' + this.labelxs;
41097                     container.cls += ' col-xs-' + (12 - this.labelxs);
41098                 }
41099             }
41100         }
41101
41102         cfg.cn = [
41103             label,
41104             container,
41105             hiddenInput
41106         ];
41107         
41108         var settings = this;
41109
41110         ['xs','sm','md','lg'].map(function(size){
41111             if (settings[size]) {
41112                 cfg.cls += ' col-' + size + '-' + settings[size];
41113             }
41114         });
41115         
41116         return cfg;
41117     },
41118     
41119     initEvents : function()
41120     {
41121         this.indicator = this.indicatorEl();
41122         
41123         this.initCurrencyEvent();
41124         
41125         this.initNumberEvent();
41126     },
41127     
41128     initCurrencyEvent : function()
41129     {
41130         if (!this.store) {
41131             throw "can not find store for combo";
41132         }
41133         
41134         this.store = Roo.factory(this.store, Roo.data);
41135         this.store.parent = this;
41136         
41137         this.createList();
41138         
41139         this.triggerEl = this.el.select('.input-group-addon', true).first();
41140         
41141         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41142         
41143         var _this = this;
41144         
41145         (function(){
41146             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41147             _this.list.setWidth(lw);
41148         }).defer(100);
41149         
41150         this.list.on('mouseover', this.onViewOver, this);
41151         this.list.on('mousemove', this.onViewMove, this);
41152         this.list.on('scroll', this.onViewScroll, this);
41153         
41154         if(!this.tpl){
41155             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41156         }
41157         
41158         this.view = new Roo.View(this.list, this.tpl, {
41159             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41160         });
41161         
41162         this.view.on('click', this.onViewClick, this);
41163         
41164         this.store.on('beforeload', this.onBeforeLoad, this);
41165         this.store.on('load', this.onLoad, this);
41166         this.store.on('loadexception', this.onLoadException, this);
41167         
41168         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41169             "up" : function(e){
41170                 this.inKeyMode = true;
41171                 this.selectPrev();
41172             },
41173
41174             "down" : function(e){
41175                 if(!this.isExpanded()){
41176                     this.onTriggerClick();
41177                 }else{
41178                     this.inKeyMode = true;
41179                     this.selectNext();
41180                 }
41181             },
41182
41183             "enter" : function(e){
41184                 this.collapse();
41185                 
41186                 if(this.fireEvent("specialkey", this, e)){
41187                     this.onViewClick(false);
41188                 }
41189                 
41190                 return true;
41191             },
41192
41193             "esc" : function(e){
41194                 this.collapse();
41195             },
41196
41197             "tab" : function(e){
41198                 this.collapse();
41199                 
41200                 if(this.fireEvent("specialkey", this, e)){
41201                     this.onViewClick(false);
41202                 }
41203                 
41204                 return true;
41205             },
41206
41207             scope : this,
41208
41209             doRelay : function(foo, bar, hname){
41210                 if(hname == 'down' || this.scope.isExpanded()){
41211                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41212                 }
41213                 return true;
41214             },
41215
41216             forceKeyDown: true
41217         });
41218         
41219         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41220         
41221     },
41222     
41223     initNumberEvent : function(e)
41224     {
41225         this.inputEl().on("keydown" , this.fireKey,  this);
41226         this.inputEl().on("focus", this.onFocus,  this);
41227         this.inputEl().on("blur", this.onBlur,  this);
41228         
41229         this.inputEl().relayEvent('keyup', this);
41230         
41231         if(this.indicator){
41232             this.indicator.addClass('invisible');
41233         }
41234  
41235         this.originalValue = this.getValue();
41236         
41237         if(this.validationEvent == 'keyup'){
41238             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41239             this.inputEl().on('keyup', this.filterValidation, this);
41240         }
41241         else if(this.validationEvent !== false){
41242             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41243         }
41244         
41245         if(this.selectOnFocus){
41246             this.on("focus", this.preFocus, this);
41247             
41248         }
41249         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41250             this.inputEl().on("keypress", this.filterKeys, this);
41251         } else {
41252             this.inputEl().relayEvent('keypress', this);
41253         }
41254         
41255         var allowed = "0123456789";
41256         
41257         if(this.allowDecimals){
41258             allowed += this.decimalSeparator;
41259         }
41260         
41261         if(this.allowNegative){
41262             allowed += "-";
41263         }
41264         
41265         if(this.thousandsDelimiter) {
41266             allowed += ",";
41267         }
41268         
41269         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41270         
41271         var keyPress = function(e){
41272             
41273             var k = e.getKey();
41274             
41275             var c = e.getCharCode();
41276             
41277             if(
41278                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41279                     allowed.indexOf(String.fromCharCode(c)) === -1
41280             ){
41281                 e.stopEvent();
41282                 return;
41283             }
41284             
41285             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41286                 return;
41287             }
41288             
41289             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41290                 e.stopEvent();
41291             }
41292         };
41293         
41294         this.inputEl().on("keypress", keyPress, this);
41295         
41296     },
41297     
41298     onTriggerClick : function(e)
41299     {   
41300         if(this.disabled){
41301             return;
41302         }
41303         
41304         this.page = 0;
41305         this.loadNext = false;
41306         
41307         if(this.isExpanded()){
41308             this.collapse();
41309             return;
41310         }
41311         
41312         this.hasFocus = true;
41313         
41314         if(this.triggerAction == 'all') {
41315             this.doQuery(this.allQuery, true);
41316             return;
41317         }
41318         
41319         this.doQuery(this.getRawValue());
41320     },
41321     
41322     getCurrency : function()
41323     {   
41324         var v = this.currencyEl().getValue();
41325         
41326         return v;
41327     },
41328     
41329     restrictHeight : function()
41330     {
41331         this.list.alignTo(this.currencyEl(), this.listAlign);
41332         this.list.alignTo(this.currencyEl(), this.listAlign);
41333     },
41334     
41335     onViewClick : function(view, doFocus, el, e)
41336     {
41337         var index = this.view.getSelectedIndexes()[0];
41338         
41339         var r = this.store.getAt(index);
41340         
41341         if(r){
41342             this.onSelect(r, index);
41343         }
41344     },
41345     
41346     onSelect : function(record, index){
41347         
41348         if(this.fireEvent('beforeselect', this, record, index) !== false){
41349         
41350             this.setFromCurrencyData(index > -1 ? record.data : false);
41351             
41352             this.collapse();
41353             
41354             this.fireEvent('select', this, record, index);
41355         }
41356     },
41357     
41358     setFromCurrencyData : function(o)
41359     {
41360         var currency = '';
41361         
41362         this.lastCurrency = o;
41363         
41364         if (this.currencyField) {
41365             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41366         } else {
41367             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41368         }
41369         
41370         this.lastSelectionText = currency;
41371         
41372         //setting default currency
41373         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41374             this.setCurrency(this.defaultCurrency);
41375             return;
41376         }
41377         
41378         this.setCurrency(currency);
41379     },
41380     
41381     setFromData : function(o)
41382     {
41383         var c = {};
41384         
41385         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41386         
41387         this.setFromCurrencyData(c);
41388         
41389         var value = '';
41390         
41391         if (this.name) {
41392             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41393         } else {
41394             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41395         }
41396         
41397         this.setValue(value);
41398         
41399     },
41400     
41401     setCurrency : function(v)
41402     {   
41403         this.currencyValue = v;
41404         
41405         if(this.rendered){
41406             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41407             this.validate();
41408         }
41409     },
41410     
41411     setValue : function(v)
41412     {
41413         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41414         
41415         this.value = v;
41416         
41417         if(this.rendered){
41418             
41419             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41420             
41421             this.inputEl().dom.value = (v == '') ? '' :
41422                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41423             
41424             if(!this.allowZero && v === '0') {
41425                 this.hiddenEl().dom.value = '';
41426                 this.inputEl().dom.value = '';
41427             }
41428             
41429             this.validate();
41430         }
41431     },
41432     
41433     getRawValue : function()
41434     {
41435         var v = this.inputEl().getValue();
41436         
41437         return v;
41438     },
41439     
41440     getValue : function()
41441     {
41442         return this.fixPrecision(this.parseValue(this.getRawValue()));
41443     },
41444     
41445     parseValue : function(value)
41446     {
41447         if(this.thousandsDelimiter) {
41448             value += "";
41449             r = new RegExp(",", "g");
41450             value = value.replace(r, "");
41451         }
41452         
41453         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41454         return isNaN(value) ? '' : value;
41455         
41456     },
41457     
41458     fixPrecision : function(value)
41459     {
41460         if(this.thousandsDelimiter) {
41461             value += "";
41462             r = new RegExp(",", "g");
41463             value = value.replace(r, "");
41464         }
41465         
41466         var nan = isNaN(value);
41467         
41468         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41469             return nan ? '' : value;
41470         }
41471         return parseFloat(value).toFixed(this.decimalPrecision);
41472     },
41473     
41474     decimalPrecisionFcn : function(v)
41475     {
41476         return Math.floor(v);
41477     },
41478     
41479     validateValue : function(value)
41480     {
41481         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41482             return false;
41483         }
41484         
41485         var num = this.parseValue(value);
41486         
41487         if(isNaN(num)){
41488             this.markInvalid(String.format(this.nanText, value));
41489             return false;
41490         }
41491         
41492         if(num < this.minValue){
41493             this.markInvalid(String.format(this.minText, this.minValue));
41494             return false;
41495         }
41496         
41497         if(num > this.maxValue){
41498             this.markInvalid(String.format(this.maxText, this.maxValue));
41499             return false;
41500         }
41501         
41502         return true;
41503     },
41504     
41505     validate : function()
41506     {
41507         if(this.disabled || this.allowBlank){
41508             this.markValid();
41509             return true;
41510         }
41511         
41512         var currency = this.getCurrency();
41513         
41514         if(this.validateValue(this.getRawValue()) && currency.length){
41515             this.markValid();
41516             return true;
41517         }
41518         
41519         this.markInvalid();
41520         return false;
41521     },
41522     
41523     getName: function()
41524     {
41525         return this.name;
41526     },
41527     
41528     beforeBlur : function()
41529     {
41530         if(!this.castInt){
41531             return;
41532         }
41533         
41534         var v = this.parseValue(this.getRawValue());
41535         
41536         if(v || v == 0){
41537             this.setValue(v);
41538         }
41539     },
41540     
41541     onBlur : function()
41542     {
41543         this.beforeBlur();
41544         
41545         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41546             //this.el.removeClass(this.focusClass);
41547         }
41548         
41549         this.hasFocus = false;
41550         
41551         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41552             this.validate();
41553         }
41554         
41555         var v = this.getValue();
41556         
41557         if(String(v) !== String(this.startValue)){
41558             this.fireEvent('change', this, v, this.startValue);
41559         }
41560         
41561         this.fireEvent("blur", this);
41562     },
41563     
41564     inputEl : function()
41565     {
41566         return this.el.select('.roo-money-amount-input', true).first();
41567     },
41568     
41569     currencyEl : function()
41570     {
41571         return this.el.select('.roo-money-currency-input', true).first();
41572     },
41573     
41574     hiddenEl : function()
41575     {
41576         return this.el.select('input.hidden-number-input',true).first();
41577     }
41578     
41579 });/**
41580  * @class Roo.bootstrap.BezierSignature
41581  * @extends Roo.bootstrap.Component
41582  * Bootstrap BezierSignature class
41583  * This script refer to:
41584  *    Title: Signature Pad
41585  *    Author: szimek
41586  *    Availability: https://github.com/szimek/signature_pad
41587  *
41588  * @constructor
41589  * Create a new BezierSignature
41590  * @param {Object} config The config object
41591  */
41592
41593 Roo.bootstrap.BezierSignature = function(config){
41594     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41595     this.addEvents({
41596         "resize" : true
41597     });
41598 };
41599
41600 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41601 {
41602      
41603     curve_data: [],
41604     
41605     is_empty: true,
41606     
41607     mouse_btn_down: true,
41608     
41609     /**
41610      * @cfg {int} canvas height
41611      */
41612     canvas_height: '200px',
41613     
41614     /**
41615      * @cfg {float|function} Radius of a single dot.
41616      */ 
41617     dot_size: false,
41618     
41619     /**
41620      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41621      */
41622     min_width: 0.5,
41623     
41624     /**
41625      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41626      */
41627     max_width: 2.5,
41628     
41629     /**
41630      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41631      */
41632     throttle: 16,
41633     
41634     /**
41635      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41636      */
41637     min_distance: 5,
41638     
41639     /**
41640      * @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.
41641      */
41642     bg_color: 'rgba(0, 0, 0, 0)',
41643     
41644     /**
41645      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41646      */
41647     dot_color: 'black',
41648     
41649     /**
41650      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41651      */ 
41652     velocity_filter_weight: 0.7,
41653     
41654     /**
41655      * @cfg {function} Callback when stroke begin. 
41656      */
41657     onBegin: false,
41658     
41659     /**
41660      * @cfg {function} Callback when stroke end.
41661      */
41662     onEnd: false,
41663     
41664     getAutoCreate : function()
41665     {
41666         var cls = 'roo-signature column';
41667         
41668         if(this.cls){
41669             cls += ' ' + this.cls;
41670         }
41671         
41672         var col_sizes = [
41673             'lg',
41674             'md',
41675             'sm',
41676             'xs'
41677         ];
41678         
41679         for(var i = 0; i < col_sizes.length; i++) {
41680             if(this[col_sizes[i]]) {
41681                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41682             }
41683         }
41684         
41685         var cfg = {
41686             tag: 'div',
41687             cls: cls,
41688             cn: [
41689                 {
41690                     tag: 'div',
41691                     cls: 'roo-signature-body',
41692                     cn: [
41693                         {
41694                             tag: 'canvas',
41695                             cls: 'roo-signature-body-canvas',
41696                             height: this.canvas_height,
41697                             width: this.canvas_width
41698                         }
41699                     ]
41700                 },
41701                 {
41702                     tag: 'input',
41703                     type: 'file',
41704                     style: 'display: none'
41705                 }
41706             ]
41707         };
41708         
41709         return cfg;
41710     },
41711     
41712     initEvents: function() 
41713     {
41714         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41715         
41716         var canvas = this.canvasEl();
41717         
41718         // mouse && touch event swapping...
41719         canvas.dom.style.touchAction = 'none';
41720         canvas.dom.style.msTouchAction = 'none';
41721         
41722         this.mouse_btn_down = false;
41723         canvas.on('mousedown', this._handleMouseDown, this);
41724         canvas.on('mousemove', this._handleMouseMove, this);
41725         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41726         
41727         if (window.PointerEvent) {
41728             canvas.on('pointerdown', this._handleMouseDown, this);
41729             canvas.on('pointermove', this._handleMouseMove, this);
41730             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41731         }
41732         
41733         if ('ontouchstart' in window) {
41734             canvas.on('touchstart', this._handleTouchStart, this);
41735             canvas.on('touchmove', this._handleTouchMove, this);
41736             canvas.on('touchend', this._handleTouchEnd, this);
41737         }
41738         
41739         Roo.EventManager.onWindowResize(this.resize, this, true);
41740         
41741         // file input event
41742         this.fileEl().on('change', this.uploadImage, this);
41743         
41744         this.clear();
41745         
41746         this.resize();
41747     },
41748     
41749     resize: function(){
41750         
41751         var canvas = this.canvasEl().dom;
41752         var ctx = this.canvasElCtx();
41753         var img_data = false;
41754         
41755         if(canvas.width > 0) {
41756             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41757         }
41758         // setting canvas width will clean img data
41759         canvas.width = 0;
41760         
41761         var style = window.getComputedStyle ? 
41762             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41763             
41764         var padding_left = parseInt(style.paddingLeft) || 0;
41765         var padding_right = parseInt(style.paddingRight) || 0;
41766         
41767         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41768         
41769         if(img_data) {
41770             ctx.putImageData(img_data, 0, 0);
41771         }
41772     },
41773     
41774     _handleMouseDown: function(e)
41775     {
41776         if (e.browserEvent.which === 1) {
41777             this.mouse_btn_down = true;
41778             this.strokeBegin(e);
41779         }
41780     },
41781     
41782     _handleMouseMove: function (e)
41783     {
41784         if (this.mouse_btn_down) {
41785             this.strokeMoveUpdate(e);
41786         }
41787     },
41788     
41789     _handleMouseUp: function (e)
41790     {
41791         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41792             this.mouse_btn_down = false;
41793             this.strokeEnd(e);
41794         }
41795     },
41796     
41797     _handleTouchStart: function (e) {
41798         
41799         e.preventDefault();
41800         if (e.browserEvent.targetTouches.length === 1) {
41801             // var touch = e.browserEvent.changedTouches[0];
41802             // this.strokeBegin(touch);
41803             
41804              this.strokeBegin(e); // assume e catching the correct xy...
41805         }
41806     },
41807     
41808     _handleTouchMove: function (e) {
41809         e.preventDefault();
41810         // var touch = event.targetTouches[0];
41811         // _this._strokeMoveUpdate(touch);
41812         this.strokeMoveUpdate(e);
41813     },
41814     
41815     _handleTouchEnd: function (e) {
41816         var wasCanvasTouched = e.target === this.canvasEl().dom;
41817         if (wasCanvasTouched) {
41818             e.preventDefault();
41819             // var touch = event.changedTouches[0];
41820             // _this._strokeEnd(touch);
41821             this.strokeEnd(e);
41822         }
41823     },
41824     
41825     reset: function () {
41826         this._lastPoints = [];
41827         this._lastVelocity = 0;
41828         this._lastWidth = (this.min_width + this.max_width) / 2;
41829         this.canvasElCtx().fillStyle = this.dot_color;
41830     },
41831     
41832     strokeMoveUpdate: function(e)
41833     {
41834         this.strokeUpdate(e);
41835         
41836         if (this.throttle) {
41837             this.throttleStroke(this.strokeUpdate, this.throttle);
41838         }
41839         else {
41840             this.strokeUpdate(e);
41841         }
41842     },
41843     
41844     strokeBegin: function(e)
41845     {
41846         var newPointGroup = {
41847             color: this.dot_color,
41848             points: []
41849         };
41850         
41851         if (typeof this.onBegin === 'function') {
41852             this.onBegin(e);
41853         }
41854         
41855         this.curve_data.push(newPointGroup);
41856         this.reset();
41857         this.strokeUpdate(e);
41858     },
41859     
41860     strokeUpdate: function(e)
41861     {
41862         var rect = this.canvasEl().dom.getBoundingClientRect();
41863         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41864         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41865         var lastPoints = lastPointGroup.points;
41866         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41867         var isLastPointTooClose = lastPoint
41868             ? point.distanceTo(lastPoint) <= this.min_distance
41869             : false;
41870         var color = lastPointGroup.color;
41871         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41872             var curve = this.addPoint(point);
41873             if (!lastPoint) {
41874                 this.drawDot({color: color, point: point});
41875             }
41876             else if (curve) {
41877                 this.drawCurve({color: color, curve: curve});
41878             }
41879             lastPoints.push({
41880                 time: point.time,
41881                 x: point.x,
41882                 y: point.y
41883             });
41884         }
41885     },
41886     
41887     strokeEnd: function(e)
41888     {
41889         this.strokeUpdate(e);
41890         if (typeof this.onEnd === 'function') {
41891             this.onEnd(e);
41892         }
41893     },
41894     
41895     addPoint:  function (point) {
41896         var _lastPoints = this._lastPoints;
41897         _lastPoints.push(point);
41898         if (_lastPoints.length > 2) {
41899             if (_lastPoints.length === 3) {
41900                 _lastPoints.unshift(_lastPoints[0]);
41901             }
41902             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41903             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41904             _lastPoints.shift();
41905             return curve;
41906         }
41907         return null;
41908     },
41909     
41910     calculateCurveWidths: function (startPoint, endPoint) {
41911         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41912             (1 - this.velocity_filter_weight) * this._lastVelocity;
41913
41914         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41915         var widths = {
41916             end: newWidth,
41917             start: this._lastWidth
41918         };
41919         
41920         this._lastVelocity = velocity;
41921         this._lastWidth = newWidth;
41922         return widths;
41923     },
41924     
41925     drawDot: function (_a) {
41926         var color = _a.color, point = _a.point;
41927         var ctx = this.canvasElCtx();
41928         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41929         ctx.beginPath();
41930         this.drawCurveSegment(point.x, point.y, width);
41931         ctx.closePath();
41932         ctx.fillStyle = color;
41933         ctx.fill();
41934     },
41935     
41936     drawCurve: function (_a) {
41937         var color = _a.color, curve = _a.curve;
41938         var ctx = this.canvasElCtx();
41939         var widthDelta = curve.endWidth - curve.startWidth;
41940         var drawSteps = Math.floor(curve.length()) * 2;
41941         ctx.beginPath();
41942         ctx.fillStyle = color;
41943         for (var i = 0; i < drawSteps; i += 1) {
41944         var t = i / drawSteps;
41945         var tt = t * t;
41946         var ttt = tt * t;
41947         var u = 1 - t;
41948         var uu = u * u;
41949         var uuu = uu * u;
41950         var x = uuu * curve.startPoint.x;
41951         x += 3 * uu * t * curve.control1.x;
41952         x += 3 * u * tt * curve.control2.x;
41953         x += ttt * curve.endPoint.x;
41954         var y = uuu * curve.startPoint.y;
41955         y += 3 * uu * t * curve.control1.y;
41956         y += 3 * u * tt * curve.control2.y;
41957         y += ttt * curve.endPoint.y;
41958         var width = curve.startWidth + ttt * widthDelta;
41959         this.drawCurveSegment(x, y, width);
41960         }
41961         ctx.closePath();
41962         ctx.fill();
41963     },
41964     
41965     drawCurveSegment: function (x, y, width) {
41966         var ctx = this.canvasElCtx();
41967         ctx.moveTo(x, y);
41968         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41969         this.is_empty = false;
41970     },
41971     
41972     clear: function()
41973     {
41974         var ctx = this.canvasElCtx();
41975         var canvas = this.canvasEl().dom;
41976         ctx.fillStyle = this.bg_color;
41977         ctx.clearRect(0, 0, canvas.width, canvas.height);
41978         ctx.fillRect(0, 0, canvas.width, canvas.height);
41979         this.curve_data = [];
41980         this.reset();
41981         this.is_empty = true;
41982     },
41983     
41984     fileEl: function()
41985     {
41986         return  this.el.select('input',true).first();
41987     },
41988     
41989     canvasEl: function()
41990     {
41991         return this.el.select('canvas',true).first();
41992     },
41993     
41994     canvasElCtx: function()
41995     {
41996         return this.el.select('canvas',true).first().dom.getContext('2d');
41997     },
41998     
41999     getImage: function(type)
42000     {
42001         if(this.is_empty) {
42002             return false;
42003         }
42004         
42005         // encryption ?
42006         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42007     },
42008     
42009     drawFromImage: function(img_src)
42010     {
42011         var img = new Image();
42012         
42013         img.onload = function(){
42014             this.canvasElCtx().drawImage(img, 0, 0);
42015         }.bind(this);
42016         
42017         img.src = img_src;
42018         
42019         this.is_empty = false;
42020     },
42021     
42022     selectImage: function()
42023     {
42024         this.fileEl().dom.click();
42025     },
42026     
42027     uploadImage: function(e)
42028     {
42029         var reader = new FileReader();
42030         
42031         reader.onload = function(e){
42032             var img = new Image();
42033             img.onload = function(){
42034                 this.reset();
42035                 this.canvasElCtx().drawImage(img, 0, 0);
42036             }.bind(this);
42037             img.src = e.target.result;
42038         }.bind(this);
42039         
42040         reader.readAsDataURL(e.target.files[0]);
42041     },
42042     
42043     // Bezier Point Constructor
42044     Point: (function () {
42045         function Point(x, y, time) {
42046             this.x = x;
42047             this.y = y;
42048             this.time = time || Date.now();
42049         }
42050         Point.prototype.distanceTo = function (start) {
42051             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42052         };
42053         Point.prototype.equals = function (other) {
42054             return this.x === other.x && this.y === other.y && this.time === other.time;
42055         };
42056         Point.prototype.velocityFrom = function (start) {
42057             return this.time !== start.time
42058             ? this.distanceTo(start) / (this.time - start.time)
42059             : 0;
42060         };
42061         return Point;
42062     }()),
42063     
42064     
42065     // Bezier Constructor
42066     Bezier: (function () {
42067         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42068             this.startPoint = startPoint;
42069             this.control2 = control2;
42070             this.control1 = control1;
42071             this.endPoint = endPoint;
42072             this.startWidth = startWidth;
42073             this.endWidth = endWidth;
42074         }
42075         Bezier.fromPoints = function (points, widths, scope) {
42076             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42077             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42078             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42079         };
42080         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42081             var dx1 = s1.x - s2.x;
42082             var dy1 = s1.y - s2.y;
42083             var dx2 = s2.x - s3.x;
42084             var dy2 = s2.y - s3.y;
42085             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42086             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42087             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42088             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42089             var dxm = m1.x - m2.x;
42090             var dym = m1.y - m2.y;
42091             var k = l2 / (l1 + l2);
42092             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42093             var tx = s2.x - cm.x;
42094             var ty = s2.y - cm.y;
42095             return {
42096                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42097                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42098             };
42099         };
42100         Bezier.prototype.length = function () {
42101             var steps = 10;
42102             var length = 0;
42103             var px;
42104             var py;
42105             for (var i = 0; i <= steps; i += 1) {
42106                 var t = i / steps;
42107                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42108                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42109                 if (i > 0) {
42110                     var xdiff = cx - px;
42111                     var ydiff = cy - py;
42112                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42113                 }
42114                 px = cx;
42115                 py = cy;
42116             }
42117             return length;
42118         };
42119         Bezier.prototype.point = function (t, start, c1, c2, end) {
42120             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42121             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42122             + (3.0 * c2 * (1.0 - t) * t * t)
42123             + (end * t * t * t);
42124         };
42125         return Bezier;
42126     }()),
42127     
42128     throttleStroke: function(fn, wait) {
42129       if (wait === void 0) { wait = 250; }
42130       var previous = 0;
42131       var timeout = null;
42132       var result;
42133       var storedContext;
42134       var storedArgs;
42135       var later = function () {
42136           previous = Date.now();
42137           timeout = null;
42138           result = fn.apply(storedContext, storedArgs);
42139           if (!timeout) {
42140               storedContext = null;
42141               storedArgs = [];
42142           }
42143       };
42144       return function wrapper() {
42145           var args = [];
42146           for (var _i = 0; _i < arguments.length; _i++) {
42147               args[_i] = arguments[_i];
42148           }
42149           var now = Date.now();
42150           var remaining = wait - (now - previous);
42151           storedContext = this;
42152           storedArgs = args;
42153           if (remaining <= 0 || remaining > wait) {
42154               if (timeout) {
42155                   clearTimeout(timeout);
42156                   timeout = null;
42157               }
42158               previous = now;
42159               result = fn.apply(storedContext, storedArgs);
42160               if (!timeout) {
42161                   storedContext = null;
42162                   storedArgs = [];
42163               }
42164           }
42165           else if (!timeout) {
42166               timeout = window.setTimeout(later, remaining);
42167           }
42168           return result;
42169       };
42170   }
42171   
42172 });
42173
42174  
42175
42176